/*
 *  SSLP_Protocol.h - implementations of SSLP services use this to describe the network protocol.
 *
 *  Copyright (c) 2001 Woody Zenfell, III.
 *  woodyzenfell@hotmail.com

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Library General Public
    License as published by the Free Software Foundation; either
    version 2 of the License, or (at your option) any later version.

    This library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Library General Public License for more details.

    You should have received a copy of the GNU Library General Public
    License along with this library; if not, write to the Free
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

 *  Simple Service Location Protocol (hope that name's ok with everyone, did not research it at all)
 *	(Near-)conflict with well-known Secure Socket Layer is unfortunate, but I doubt anyone will mistake them :) .
 *	Someone smart has surely done this better somewhere and has probably proven various characteristics,
 *	but I decided to hack out my own anyway.  Tough.
 *
 *	The current implementation (and a small amount of the interface) relies on the SDL_net cross-platform
 *	IP networking library.  The current implementation (and no part of the interface) relies on my own
 *	"SDL_netx" UDP broadcast companion to SDL_net, as well as some other features of SDL.
 *
 *	for the Aleph One project, to do NBP-ish player location on non-AppleTalk networks.  The current
 *	implementation is a (significant) simplification of what this could or perhaps ought to be, but
 *	it's sufficient for Aleph One's needs, which is all that matters right now.  Besides, like I said,
 *	I'm sure there's something better already out there that's designed for heavyweight application.
 *
 *  Created by Woody Zenfell, III on Tue Sep 11 2001.
 
 June 15, 2002 (Loren Petrich):
 	Added a packed size, so as to avoid compiler dependency
 */

#ifndef SSLP_PROTOCOL_H
#define	SSLP_PROTOCOL_H

#include	<SDL_net.h>

#ifdef OBSOLETE
#ifdef __GNUC__
#define PACKED_ATTRIBUTE __attribute__ ((packed))
#else
#define PACKED_ATTRIBUTE
#ifndef _MSC_VER
#error You must arrange for struct SSLP_Packet to be close-packed.
#endif
#endif
#endif


#ifndef SSLP_PORT
#define	SSLP_PORT		15367		// I made this up, change if it sucks
#endif

#define	SSLPP_MAGIC		0x73736c70	// 'sslp' (when in network byte order)
#define	SSLPP_VERSION		1		// change this number if you change the protocol or packet format
#define	SSLPP_MESSAGE_FIND	0x66696e64	// 'find'
#define	SSLPP_MESSAGE_HAVE	0x68617665	// 'have'
#define	SSLPP_MESSAGE_LOST	0x6c6f7374	// you guessed it, 'lost'

// note: Marathon game protocol has its own version independent of SSLP version - don't adjust SSLPP_VERSION
// if changing only the Marathon protocol.

// SSLP is implemented on datagram-type protocols (e.g. UDP).  Datagrams may get lost.  That's ok.
// It's not overly clever or anything - in fact, it could hardly be more straightforward.  Thus "Simple" :)
// ("stupid"?  "silly"?)

// It's assumed that the host and port a FIND message originates from is the same port HAVE and LOST messages should
// be sent to.  It's assumed that a host advertising a service with HAVE has the same host-address as (but maybe a
// different port from) the service itself.
// Life will be easier for everyone if all participants send and receive all SSLP packets on port number SSLP_PORT.

// SSLP does not "guarantee" anything about its findings - it's intended merely as an aid.  This means (in particular)
// a host that found a service via SSLP cannot assume that the service definitely exists at the host and port provided...
// but it's a probably a good place to look ;)


// Here's how it works:

// Host wanting to find instances of a service on the network (in this case, gathering computer finding
// network Aleph One players) broadcasts datagrams carrying FIND messages.  The service_type field
// indicates what kind of service the host is looking for.  Typically, the message would be rebroadcast
// once every 1-5 seconds or so for whatever duration the application wants (in Aleph One, probably duration
// of Gather Network Game "add players" dialog) - this is our only mechanism for coping with dropped packets.
// Host may also unicast a FIND message if it suspects a service resides on a particular host, e.g. if it was
// there a while ago.
// As noted above, FIND messages should be sent from the port that HAVE messages should be received on.

// Hosts allowing discovery of their services must respond (to the sender's host and port) to every incoming
// FIND message carrying the correct service_type (as defined by strncmp() == 0) with a HAVE message.  service_type
// of the HAVE message ought to match that of the FIND message.  service_name of the HAVE message gives the name of
// a service instance on the host.
// In Aleph One, the service_name would be the name of the player.
// A host is allowed to broadcast a HAVE message (once) when a service becomes available, to speed things up a little.
// A host may unicast an unsolicited HAVE message if it thinks a host is looking for its service (e.g. if the host
// was looking for the service a while ago, or a user "hints" the system that a named host probably wants to find
// our service).  Heck, if the user bothered to hint us an address, it probably means the broadcast FIND messages
// don't get to us (and our single broadcast HAVE probably won't get to it).  May as well unicast it periodically
// until we don't need to be discovered anymore.
// Typically, an Aleph One player service would be allowed to be discovered while the "Join Network Game" dialog is
// "committed" but before the player has been gathered.

// Hosts should, as a courtesy to FINDers, send a LOST message when discovery is no longer desired.  Ideally you'd
// send a LOST to everyone you sent a HAVE to, but just broadcasting one on SSLP_PORT is probably good enough.
// LOST is not required for "correct" operation (whatever that is for a system that guarantees nothing), since FINDers
// ought to age and discard found services for which no HAVE has been received for some time, but LOST in most cases
// should help the FINDer keep his lists better up-to-date.
// Typically, Aleph One would send this when a player cancels "Join Network Game" after "committing", or when
// the player has been gathered (and thus no longer needs to be "discovered"). 

// Ugly laziness here, using same constants for network protocol format as for API.  Oh well.
#ifndef SSLP_MAX_TYPE_LENGTH
#define	SSLP_MAX_TYPE_LENGTH	32
#endif
#ifndef SSLP_MAX_NAME_LENGTH
#define	SSLP_MAX_NAME_LENGTH	32
#endif

#ifdef OBSOLETE
#ifdef _MSC_VER
#pragma pack(push,1)
#endif
#endif

// ASSUMPTION: if we tell the compiler this should be "packed", it will have the same alignment/padding
// on all platforms.  Numeric fields must always be in network ("big-endian") order when placed on the wire.
struct SSLP_Packet {
    Uint32	sslpp_magic;		// should always be SSLPP_MAGIC
    Uint32	sslpp_version;		// set to SSLPP_VERSION, for catching version mismatch
    Uint32	sslpp_message;		// set to SSLPP_MESSAGE_*
    Uint16	sslpp_service_port;	// only valid in HAVE messages, states on which port the service can be contacted.
    Uint16	sslpp_reserved;		// should always be 0
    char	sslpp_service_type[SSLP_MAX_TYPE_LENGTH];	// type desired, for FIND; type provided or no longer provided on HAVE or LOST
    char	sslpp_service_name[SSLP_MAX_NAME_LENGTH];	// name of the service instance.  meaningless in FIND.
}; // PACKED_ATTRIBUTE ;

// The packed size and packed-packet type
const int SIZEOF_SSLP_Packet = 3*4 + 2*2 + SSLP_MAX_TYPE_LENGTH + SSLP_MAX_NAME_LENGTH;

// service_type and service_name are treated as C-strings in comparisons - i.e. it's a match if all the bytes up to the first '\0'
// are equal.  also, whenever a name or type is copied, only the bytes up to and including the first '\0' are copied.
// Note that it's not an error for all SSLP_MAX_*_LENGTH bytes of name or type storage to be filled with data - a terminating
// NULL is _not_ required if all SSLP_MAX_*_LENGTH bytes are used.

#ifdef OBSOLETE
#ifdef _MSC_VER
#pragma pack(pop)
#endif
#endif

// sizeof(struct SSLP_Packet) == 80

#endif//SSLP_PROTOCOL_H
