mirror of
https://github.com/Mauler125/r5sdk.git
synced 2025-02-09 19:15:03 +01:00
DirtySDK (EA's Dirty Sockets library) will be used for the LiveAPI implementation, and depends on: EABase, EAThread.
919 lines
28 KiB
C
919 lines
28 KiB
C
/*H*************************************************************************************/
|
|
/*!
|
|
\File protoadvt.c
|
|
|
|
\Description
|
|
This advertising module provides a relatively simple multi-protocol
|
|
distributed name server architecture utilizing the broadcast capabilities
|
|
of UDP and IPX. Once the module is instantiated, it can be used as both
|
|
an advertiser (server) and watcher (client) simultaneously.
|
|
|
|
\Copyright
|
|
Copyright (c) Tiburon Entertainment / Electronic Arts 2002. ALL RIGHTS RESERVED.
|
|
|
|
\Version 1.0 02/17/99 (GWS) Original version
|
|
\Version 1.1 02/25/99 (GWS) Alpha release
|
|
\Version 1.2 07/27/99 (GWS) Initial release
|
|
\Version 1.3 10/28/99 (GWS) Message queue elimination
|
|
\Version 1.4 07/10/00 (GWS) Windock2 dependency removal
|
|
\Version 1.5 12/11/00 (GWS) Ported to Dirtysock
|
|
\Version 2.0 03/28/03 (GWS) Made more robust with double-broadcast sends
|
|
\Version 2.1 06/16/03 (JLB) Made string comparisons case-insensitive
|
|
*/
|
|
/*************************************************************************************H*/
|
|
|
|
/*** Include files *********************************************************************/
|
|
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
|
|
#include "DirtySDK/dirtysock.h"
|
|
#include "DirtySDK/dirtysock/dirtymem.h"
|
|
#include "DirtySDK/proto/protoadvt.h"
|
|
|
|
#if defined(DIRTYCODE_PS4)
|
|
#include <sys/types.h>
|
|
#endif
|
|
/*** Defines ***************************************************************************/
|
|
|
|
/*** Macros ****************************************************************************/
|
|
|
|
/*** Type Definitions ******************************************************************/
|
|
|
|
//! raw broadcast packet format
|
|
typedef struct ProtoAdvtPacketT
|
|
{
|
|
//! static packet identifier
|
|
uint8_t ident[3];
|
|
//! single char freq (avoid byte ordering issues)
|
|
uint8_t freq;
|
|
//! advertisement sequence number
|
|
uint8_t seqn[4];
|
|
//! the kind of service
|
|
char kind[32];
|
|
//! the name of this particular service
|
|
char name[32];
|
|
//! misc notes about service
|
|
char note[192];
|
|
//! list of service addresses
|
|
char addr[120];
|
|
} ProtoAdvtPacketT;
|
|
|
|
//! a list of services
|
|
typedef struct ServListT
|
|
{
|
|
//! actual packet data
|
|
ProtoAdvtPacketT packet;
|
|
//! timeout until item expires or is rebroadcast
|
|
uint32_t timeout;
|
|
//! last internet address associated with service
|
|
char inetdot[16];
|
|
uint32_t inetval;
|
|
uint32_t hostval;
|
|
//! flag to indicate if packet has local origin
|
|
int32_t local;
|
|
//! link to next item in list
|
|
struct ServListT *next;
|
|
} ServListT;
|
|
|
|
//! local module reference
|
|
struct ProtoAdvtRef
|
|
{
|
|
//! control access to resources
|
|
NetCritT crit;
|
|
//! list of services to announce
|
|
ServListT *announce;
|
|
//! known service list
|
|
ServListT *snoopbuf;
|
|
ServListT *snoopend;
|
|
//! dirtysock memory group
|
|
int32_t memgroup;
|
|
void *memgrpusrdata;
|
|
//! seeding is needed
|
|
int32_t seed;
|
|
//! number of users
|
|
int32_t usage;
|
|
//! active socket
|
|
SocketT *sock;
|
|
//! broadcast address
|
|
struct sockaddr addr;
|
|
//! indicate that snoop buffer changed
|
|
int32_t snoop;
|
|
};
|
|
|
|
|
|
/*** Function Prototypes ***************************************************************/
|
|
|
|
/*** Variables *************************************************************************/
|
|
|
|
// Private variables
|
|
|
|
static int32_t g_count = 0;
|
|
static ProtoAdvtRef *g_ref = NULL;
|
|
|
|
// Public variables
|
|
|
|
|
|
/*** Private Functions *****************************************************************/
|
|
|
|
|
|
/*F*************************************************************************************/
|
|
/*!
|
|
\Function _ProtoAdvtSendPacket
|
|
|
|
\Description
|
|
Sends a ProtoAdvt packet.
|
|
|
|
\Input *pRef - module ref
|
|
\Input *pPacket - packet to send
|
|
|
|
\Output
|
|
int32_t - return result from SocketSendto()
|
|
|
|
\Version 11/30/2006 (jbrookes)
|
|
*/
|
|
/*************************************************************************************F*/
|
|
static int32_t _ProtoAdvtSendPacket(ProtoAdvtRef *pRef, const ProtoAdvtPacketT *pPacket)
|
|
{
|
|
int32_t iPacketSize = sizeof(*pPacket);
|
|
return(SocketSendto(pRef->sock, (const char *)pPacket, iPacketSize, 0, &pRef->addr, sizeof(pRef->addr)));
|
|
}
|
|
|
|
/*F*************************************************************************************/
|
|
/*!
|
|
\Function _ProtoAdvtRecvPacket
|
|
|
|
\Description
|
|
Receives a ProtoAdvt packet. Variable-length string fields are unpacked from
|
|
the compacted sent packet to produce the fixed-length result packet.
|
|
|
|
\Input *pRef - module ref
|
|
\Input *pPacket - packet buffer to receive to
|
|
\Input *pFrom - sender's address
|
|
|
|
\Output
|
|
int32_t - return result from SocketRecvfrom()
|
|
|
|
\Version 11/30/2006 (jbrookes)
|
|
*/
|
|
/*************************************************************************************F*/
|
|
static int32_t _ProtoAdvtRecvPacket(ProtoAdvtRef *pRef, ProtoAdvtPacketT *pPacket, struct sockaddr *pFrom)
|
|
{
|
|
int32_t iFromLen = sizeof(*pFrom);
|
|
return(SocketRecvfrom(pRef->sock, (char *)pPacket, sizeof(*pPacket), 0, pFrom, &iFromLen));
|
|
}
|
|
|
|
/*F*************************************************************************************/
|
|
/*!
|
|
\Function _ProtoAdvtRequestSeed
|
|
|
|
\Description
|
|
Send a seed request
|
|
|
|
\Input *pRef - module ref
|
|
|
|
\Version 03/28/03 (GWS)
|
|
*/
|
|
/*************************************************************************************F*/
|
|
static void _ProtoAdvtRequestSeed(ProtoAdvtRef *pRef)
|
|
{
|
|
ProtoAdvtPacketT Packet;
|
|
|
|
// setup a query packet
|
|
ds_memclr(&Packet, sizeof(Packet));
|
|
Packet.ident[0] = ADVERT_PACKET_IDENTIFIER[0];
|
|
Packet.ident[1] = ADVERT_PACKET_IDENTIFIER[1];
|
|
Packet.ident[2] = ADVERT_PACKET_IDENTIFIER[2];
|
|
Packet.kind[0] = '?';
|
|
|
|
// do the send
|
|
_ProtoAdvtSendPacket(pRef, &Packet);
|
|
}
|
|
|
|
|
|
/*F*************************************************************************************/
|
|
/*!
|
|
\Function _ProtoAdvtCallback
|
|
|
|
\Description
|
|
Main worker callback
|
|
|
|
\Input *sock - connection socket pointer
|
|
\Input flags - unused
|
|
\Input *_ref - ProtoAdvtRef pointer
|
|
|
|
\Output int32_t - zero
|
|
|
|
\Version 11/01/02 (GWS)
|
|
*/
|
|
/*************************************************************************************F*/
|
|
static int32_t _ProtoAdvtCallback(SocketT *sock, int32_t flags, void *_ref)
|
|
{
|
|
int32_t len;
|
|
int32_t local;
|
|
struct sockaddr base;
|
|
struct sockaddr from;
|
|
ServListT *list, *item, **link;
|
|
ProtoAdvtPacketT packet;
|
|
ProtoAdvtRef *ref = (ProtoAdvtRef *) _ref;
|
|
uint32_t tick;
|
|
|
|
// make sure we own resources
|
|
if (!NetCritTry(&ref->crit))
|
|
{
|
|
NetPrintf(("protoadvt: could not acquire critical section\n"));
|
|
return(0);
|
|
}
|
|
|
|
// process all pending packets
|
|
while (ref->sock != NULL)
|
|
{
|
|
// request seeding if needed
|
|
if (ref->seed > 0)
|
|
{
|
|
ref->seed = 0;
|
|
_ProtoAdvtRequestSeed(ref);
|
|
}
|
|
|
|
// try and receive a packet
|
|
packet.kind[0] = '\0';
|
|
SockaddrInit(&from, AF_INET);
|
|
len = _ProtoAdvtRecvPacket(ref, &packet, &from);
|
|
if ((len <= 0) || (packet.kind[0] == '\0'))
|
|
{
|
|
break;
|
|
}
|
|
|
|
// get local address we would have used
|
|
SockaddrInit(&base, AF_INET);
|
|
SocketHost(&base, sizeof(base), &from, sizeof(from));
|
|
|
|
// see if packet was sent locally
|
|
local = 0;
|
|
if ((base.sa_family == from.sa_family) && (from.sa_family == AF_INET))
|
|
local = (memcmp(&from.sa_data, &base.sa_data, 6) == 0);
|
|
|
|
// ignore packets without proper identifier
|
|
if ((packet.ident[0] != ADVERT_PACKET_IDENTIFIER[0]) ||
|
|
(packet.ident[1] != ADVERT_PACKET_IDENTIFIER[1]) ||
|
|
(packet.ident[2] != ADVERT_PACKET_IDENTIFIER[2]))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// handle seed request first
|
|
if ((packet.kind[0] == '?') && (packet.kind[1] == 0))
|
|
{
|
|
tick = NetTick() + 1000;
|
|
// reduce timeouts for outgoing packets
|
|
for (list = ref->announce; list != NULL; list = list->next)
|
|
{
|
|
// reduce timeout down to 1 second
|
|
// (helps prevent multiple seed requests from causing storm)
|
|
if (list->timeout > tick)
|
|
list->timeout = tick;
|
|
}
|
|
// get next packet
|
|
continue;
|
|
}
|
|
|
|
// make sure name & kind are set
|
|
if (packet.name[0] == 0)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// process the packet
|
|
item = NULL;
|
|
for (list = ref->snoopbuf; list != ref->snoopend; ++list)
|
|
{
|
|
// check for unused block
|
|
if ((item == NULL) && (list->packet.name[0] == 0))
|
|
{
|
|
item = list;
|
|
}
|
|
// see if we already have this packet
|
|
if ((ds_stricmp(packet.kind, list->packet.kind) == 0) &&
|
|
(ds_stricmp(packet.name, list->packet.name) == 0) &&
|
|
(memcmp(packet.seqn, list->packet.seqn, sizeof(packet.seqn)) == 0))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
// see if this is a new packet
|
|
if ((list == ref->snoopend) && (item != NULL))
|
|
{
|
|
list = item;
|
|
ds_memclr(list, sizeof(*list));
|
|
// populate with kind/name/seqn (never change)
|
|
ds_memcpy_s(list->packet.seqn, sizeof(list->packet.seqn), packet.seqn, sizeof(packet.seqn));
|
|
ds_strnzcpy(list->packet.kind, packet.kind, sizeof(list->packet.kind));
|
|
ds_strnzcpy(list->packet.name, packet.name, sizeof(list->packet.name));
|
|
ds_strnzcpy(list->packet.note, packet.note, sizeof(list->packet.note));
|
|
// indicate snoop buffer changed
|
|
ref->snoop += 1;
|
|
NetPrintf(("protoadvt: added new advert name=%s freq=%d addr=%a\n", list->packet.name, packet.freq, SockaddrInGetAddr(&from)));
|
|
}
|
|
|
|
// update the record (assuming we found/allocated)
|
|
if (list != ref->snoopend)
|
|
{
|
|
ds_strnzcpy(list->packet.addr, packet.addr, sizeof(list->packet.addr));
|
|
ds_strnzcpy(list->packet.note, packet.note, sizeof(list->packet.note));
|
|
list->timeout = NetTick()+2*(packet.freq*1000)+1000;
|
|
list->local = local;
|
|
list->inetval = SockaddrInGetAddr(&from);
|
|
list->hostval = SockaddrInGetAddr(&base);
|
|
SockaddrInGetAddrText(&from, list->inetdot, sizeof(list->inetdot));
|
|
// indicate snoop buffer changed
|
|
ref->snoop += 1;
|
|
}
|
|
// loop checks for another packet
|
|
}
|
|
|
|
// check for expired services
|
|
tick = NetTick();
|
|
for (list = ref->snoopbuf; list != ref->snoopend; ++list)
|
|
{
|
|
if (list->packet.name[0] != 0)
|
|
{
|
|
// see if anything has expired
|
|
if ((list->packet.addr[0] == 0) || (list->timeout == 0) || (tick > list->timeout))
|
|
{
|
|
NetPrintf(("protoadvt: expiring advert name=%s\n", list->packet.name));
|
|
list->packet.name[0] = 0;
|
|
list->packet.kind[0] = 0;
|
|
// indicate snoop buffer changed
|
|
ref->snoop += 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
// check for expired announcements
|
|
for (link = &ref->announce; (*link) != NULL;)
|
|
{
|
|
// see if it has expired
|
|
if ((*link)->timeout == 0)
|
|
{
|
|
item = *link;
|
|
*link = (*link)->next;
|
|
// send out a cancelation notice
|
|
NetPrintf(("protoadvt: canceling announcement name=%s\n", item->packet.name));
|
|
item->packet.addr[0] = '\0';
|
|
// send out notices
|
|
_ProtoAdvtSendPacket(ref, &item->packet);
|
|
// done with the record
|
|
DirtyMemFree(item, PROTOADVT_MEMID, ref->memgroup, ref->memgrpusrdata);
|
|
}
|
|
else
|
|
{
|
|
// move to next item
|
|
link = &(*link)->next;
|
|
}
|
|
}
|
|
|
|
// see if anything needs to be sent
|
|
tick = NetTick();
|
|
for (list = ref->announce; list != NULL; list = list->next)
|
|
{
|
|
// see if its time to send
|
|
if (tick > list->timeout)
|
|
{
|
|
// broadcast packet
|
|
_ProtoAdvtSendPacket(ref, &list->packet);
|
|
// update send time
|
|
list->timeout = tick + list->packet.freq*1000;
|
|
}
|
|
}
|
|
|
|
// done with update
|
|
NetCritLeave(&ref->crit);
|
|
return(0);
|
|
}
|
|
|
|
|
|
/*** Public Functions ******************************************************************/
|
|
|
|
|
|
/*F*************************************************************************************/
|
|
/*!
|
|
\Function ProtoAdvtAnnounce
|
|
|
|
\Description
|
|
Advertise a service as available
|
|
|
|
\Input *ref - reference pointer
|
|
\Input *kind - service class (max 32 characters including NUL)
|
|
\Input *name - service name (usually game name, max 32 characters including NUL)
|
|
\Input *note - service note (max 32 characters including NUL)
|
|
\Input *addr - service address list (max 120 characters, including NUL)
|
|
\Input freq - announcement frequency, in seconds (can be in the range [2...250])
|
|
|
|
\Output
|
|
int32_t - <0 = error, 0=ok
|
|
|
|
\Version 11/01/02 (GWS)
|
|
*/
|
|
/*************************************************************************************F*/
|
|
int32_t ProtoAdvtAnnounce(ProtoAdvtRef *ref, const char *kind, const char *name,
|
|
const char *note, const char *addr, int32_t freq)
|
|
{
|
|
ServListT *list;
|
|
ServListT *item;
|
|
|
|
// clamp the freqeuncy to valid range
|
|
if (freq == 0)
|
|
freq = 30;
|
|
if (freq < 2)
|
|
freq = 2;
|
|
if (freq > 250)
|
|
freq = 250;
|
|
|
|
// validate input strings
|
|
if ((kind == NULL) || (kind[0] == '\0'))
|
|
{
|
|
NetPrintf(("protoadvt: error, invalid kind\n"));
|
|
return(-1);
|
|
}
|
|
if ((name == NULL) || (name[0] == '\0'))
|
|
{
|
|
NetPrintf(("protoadvt: error, invalid name\n"));
|
|
return(-2);
|
|
}
|
|
if (note == NULL)
|
|
{
|
|
NetPrintf(("protoadvt: error, invalid note\n"));
|
|
return(-3);
|
|
}
|
|
if (addr == NULL)
|
|
{
|
|
NetPrintf(("protoadvt: error, invalid addr\n"));
|
|
return(-4);
|
|
}
|
|
|
|
// see if service is already listed
|
|
for (list = ref->announce; list != NULL; list = list->next)
|
|
{
|
|
// check for dupl
|
|
if ((ds_stricmp(kind, list->packet.kind) == 0) &&
|
|
(ds_stricmp(name, list->packet.name) == 0))
|
|
{
|
|
// update the addr field if necessary
|
|
if (ds_stricmp(addr, list->packet.addr) != 0)
|
|
{
|
|
// update address & force immediate broadcast of new info
|
|
ds_strnzcpy(list->packet.addr, addr, sizeof(list->packet.addr));
|
|
list->timeout = NetTick()-1;
|
|
}
|
|
|
|
// update the note field if necessary
|
|
if (ds_stricmp(note, list->packet.note) != 0)
|
|
{
|
|
// update note & force immediate broadcast of new info
|
|
ds_strnzcpy(list->packet.note, note, sizeof(list->packet.note));
|
|
list->timeout = NetTick()-1;
|
|
}
|
|
// all done
|
|
return(0);
|
|
}
|
|
}
|
|
|
|
// build a packet
|
|
item = DirtyMemAlloc(sizeof(*item), PROTOADVT_MEMID, ref->memgroup, ref->memgrpusrdata);
|
|
ds_memclr(item, sizeof(*item));
|
|
item->timeout = NetTick();
|
|
item->packet.ident[0] = ADVERT_PACKET_IDENTIFIER[0];
|
|
item->packet.ident[1] = ADVERT_PACKET_IDENTIFIER[1];
|
|
item->packet.ident[2] = ADVERT_PACKET_IDENTIFIER[2];
|
|
item->packet.freq = (unsigned char) freq;
|
|
item->packet.seqn[0] = (unsigned char)(item->timeout >> 24);
|
|
item->packet.seqn[1] = (unsigned char)(item->timeout >> 16);
|
|
item->packet.seqn[2] = (unsigned char)(item->timeout >> 8);
|
|
item->packet.seqn[3] = (unsigned char)(item->timeout >> 0);
|
|
ds_strnzcpy(item->packet.kind, kind, sizeof(item->packet.kind));
|
|
ds_strnzcpy(item->packet.name, name, sizeof(item->packet.name));
|
|
ds_strnzcpy(item->packet.addr, addr, sizeof(item->packet.addr));
|
|
ds_strnzcpy(item->packet.note, note, sizeof(item->packet.note));
|
|
NetPrintf(("protoadvt: broadcasting new announcement name=%s freq=%d\n", item->packet.name, item->packet.freq));
|
|
|
|
// send an immediate copy
|
|
_ProtoAdvtSendPacket(ref, &item->packet);
|
|
// schedule next send in 250ms
|
|
item->timeout = NetTick()+250;
|
|
|
|
// make sure we own resources
|
|
NetCritEnter(&ref->crit);
|
|
// add new item to list
|
|
item->next = ref->announce;
|
|
ref->announce = item;
|
|
// release resources
|
|
NetCritLeave(&ref->crit);
|
|
return(0);
|
|
}
|
|
|
|
/*F*************************************************************************************/
|
|
/*!
|
|
\Function ProtoAdvtCancel
|
|
|
|
\Description
|
|
Cancel server advertisement
|
|
|
|
\Input *ref - reference pointer
|
|
\Input *kind - service kind
|
|
\Input *name - service name (usually game name, NULL=wildcard)
|
|
|
|
\Output
|
|
int32_t - <0=error, 0=ok
|
|
|
|
\Version 11/01/02 (GWS)
|
|
*/
|
|
/*************************************************************************************F*/
|
|
int32_t ProtoAdvtCancel(ProtoAdvtRef *ref, const char *kind, const char *name)
|
|
{
|
|
ServListT *list;
|
|
|
|
// make sure we own resources
|
|
NetCritEnter(&ref->crit);
|
|
|
|
// locate the item in the announcement list
|
|
for (list = ref->announce; list != NULL; list = list->next)
|
|
{
|
|
// see if we got a match
|
|
if (((kind == NULL) || (ds_stricmp(kind, list->packet.kind) == 0)) &&
|
|
((name == NULL) || (ds_stricmp(name, list->packet.name) == 0)))
|
|
{
|
|
// mark as deleted
|
|
list->timeout = 0;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// release resources
|
|
NetCritLeave(&ref->crit);
|
|
|
|
// was item found?
|
|
return(list == NULL ? -1 : 0);
|
|
}
|
|
|
|
|
|
/*F*************************************************************************************/
|
|
/*!
|
|
\Function ProtoAdvtQuery
|
|
|
|
\Description
|
|
Query for available services
|
|
|
|
\Input *ref - reference pointer
|
|
\Input *kind - service kind
|
|
\Input *proto - protocol
|
|
\Input *buffer - target buffer
|
|
\Input buflen - target length
|
|
\Input local - if zero, exclude local lists
|
|
|
|
\Output
|
|
int32_t - number of matches
|
|
|
|
\Version 11/01/02 (GWS)
|
|
*/
|
|
/*************************************************************************************F*/
|
|
int32_t ProtoAdvtQuery(ProtoAdvtRef *ref, const char *kind, const char *proto,
|
|
char *buffer, int32_t buflen, int32_t local)
|
|
{
|
|
char *s, *t, *u;
|
|
char addr[256] = "";
|
|
char record[512];
|
|
int32_t count = 0;
|
|
ServListT *list;
|
|
|
|
// establish min buffer size
|
|
if (buflen < 5)
|
|
return(-1);
|
|
|
|
// default to empty buffer
|
|
buffer[0] = 0;
|
|
|
|
// see what matching services we know about
|
|
for (list = ref->snoopbuf; list != ref->snoopend; ++list)
|
|
{
|
|
// skip empty entries
|
|
if (list->packet.name[0] == 0)
|
|
continue;
|
|
// see if the kind matches
|
|
if (ds_stricmp(kind, list->packet.kind) != 0)
|
|
continue;
|
|
// exclude locals if not wanted
|
|
if ((!local) && (list->local))
|
|
continue;
|
|
// parse the address choices
|
|
for (s = list->packet.addr; *s != 0;)
|
|
{
|
|
// parse out the address
|
|
for (t = addr; (*s != 0) && (*s != '\t');)
|
|
*t++ = *s++;
|
|
*t++ = 0;
|
|
if (*s == '\t')
|
|
++s;
|
|
// make sure address looks valid
|
|
if ((strlen(addr) < 5) || (addr[3] != ':'))
|
|
continue;
|
|
// see if this protocol type is wanted by caller
|
|
addr[3] = 0;
|
|
if ((proto[0] != 0) && (strstr(proto, addr) == NULL))
|
|
continue;
|
|
addr[3] = ':';
|
|
// compile data into a record
|
|
ds_strnzcpy(record, list->packet.name, sizeof(record));
|
|
for (t = record; *t != 0; ++t)
|
|
;
|
|
*t++ = '\t';
|
|
// copy over notes field
|
|
strcpy(t, list->packet.note);
|
|
while (*t != 0)
|
|
++t;
|
|
*t++ = '\t';
|
|
// append the address
|
|
for (u = addr; *u != 0; ++u)
|
|
{
|
|
// check for inet substitution
|
|
if ((u[0] == '~') && (u[1] == '1'))
|
|
{
|
|
// make sure inet address is known
|
|
if (list->inetdot[0] == 0)
|
|
break;
|
|
// copy over the address
|
|
strcpy(t, list->inetdot);
|
|
while (*t != 0)
|
|
++t;
|
|
++u;
|
|
continue;
|
|
}
|
|
// check for ipx substitution
|
|
if ((u[0] == '~') && (u[1] == '2'))
|
|
{
|
|
// no ipx support
|
|
break;
|
|
}
|
|
// raw copy
|
|
*t++ = *u;
|
|
}
|
|
// see if copy was canceled (substitution error)
|
|
if (*u != 0)
|
|
continue;
|
|
// terminate the record
|
|
*t++ = '\n';
|
|
*t = 0;
|
|
// see if the record will fit into output buffer
|
|
if (strlen(record)+5 > (unsigned) buflen)
|
|
{
|
|
// indicate an overflow
|
|
*buffer++ = '.';
|
|
*buffer++ = '.';
|
|
*buffer++ = '.';
|
|
*buffer++ = '\n';
|
|
*buffer = 0;
|
|
return(count);
|
|
}
|
|
// append the record to the buffer
|
|
strcpy(buffer, record);
|
|
buffer += (int32_t)strlen(record);
|
|
buflen -= (int32_t)strlen(record);
|
|
++count;
|
|
}
|
|
}
|
|
|
|
// return count of items
|
|
return(count);
|
|
}
|
|
|
|
|
|
/*F*************************************************************************************/
|
|
/*!
|
|
\Function ProtoAdvtLocate
|
|
|
|
\Description
|
|
Locate a specific advertisement and return advertisers address (UDP only)
|
|
|
|
\Input *ref - reference pointer
|
|
\Input *kind - service kind (NULL=any)
|
|
\Input *name - service name (usually game name, NULL=wildcard)
|
|
\Input *host - pointer to buffer for host (our) address, or NULL
|
|
\Input defval - value returned if module is not set
|
|
|
|
\Output
|
|
uint32_t - defval if ref is NULL, else first matching internet address
|
|
|
|
\Version 11/01/02 (GWS)
|
|
*/
|
|
/*************************************************************************************F*/
|
|
uint32_t ProtoAdvtLocate(ProtoAdvtRef *ref, const char *kind, const char *name,
|
|
uint32_t *host, uint32_t defval)
|
|
{
|
|
ServListT *list;
|
|
|
|
// just return default if module is not set
|
|
if (ref == NULL)
|
|
return(defval);
|
|
|
|
// see what matching services we know about
|
|
for (list = ref->snoopbuf; list != ref->snoopend; ++list)
|
|
{
|
|
// make sure service is valid
|
|
if (list->packet.name[0] == 0)
|
|
continue;
|
|
// exclude locals
|
|
if (list->local)
|
|
continue;
|
|
// see if the kind matches
|
|
if ((kind != NULL) && (kind[0] != 0) && (ds_stricmp(kind, list->packet.kind) != 0))
|
|
continue;
|
|
// make sure the name matches
|
|
if ((name != NULL) && (name[0] != 0) && (ds_stricmp(list->packet.name, name) != 0))
|
|
continue;
|
|
// make sure there is an internet address
|
|
if (list->inetval == 0)
|
|
continue;
|
|
// return host (our) address if they want it
|
|
if (host != NULL)
|
|
*host = list->hostval;
|
|
// return the internet address
|
|
defval = list->inetval;
|
|
break;
|
|
}
|
|
|
|
// return default value
|
|
return(defval);
|
|
}
|
|
|
|
|
|
/*F*************************************************************************************/
|
|
/*!
|
|
\Function ProtoAdvtConstruct
|
|
|
|
\Description
|
|
Construct an advertising agent
|
|
|
|
\Input limit - size of snoop buffer (min 4)
|
|
|
|
\Output
|
|
ProtoAdvtRef * - construct ref (passed to other routines)
|
|
|
|
\Version 11/01/02 (GWS)
|
|
*/
|
|
/*************************************************************************************F*/
|
|
ProtoAdvtRef *ProtoAdvtConstruct(int32_t limit)
|
|
{
|
|
SocketT *sock;
|
|
struct sockaddr bindaddr;
|
|
struct sockaddr peeraddr;
|
|
ProtoAdvtRef *ref;
|
|
int32_t iMemGroup;
|
|
void *pMemGroupUserData;
|
|
|
|
// Query current mem group data
|
|
DirtyMemGroupQuery(&iMemGroup, &pMemGroupUserData);
|
|
|
|
// see if already allocated
|
|
if (g_ref != NULL)
|
|
{
|
|
++g_count;
|
|
return(g_ref);
|
|
}
|
|
|
|
// allocate class storage
|
|
ref = DirtyMemAlloc(sizeof(*ref), PROTOADVT_MEMID, iMemGroup, pMemGroupUserData);
|
|
if (ref == NULL)
|
|
return(NULL);
|
|
ds_memclr(ref, sizeof(*ref));
|
|
ref->memgroup = iMemGroup;
|
|
ref->memgrpusrdata = pMemGroupUserData;
|
|
|
|
// allocate snooped advertisement buffer
|
|
if (limit < 4)
|
|
limit = 4;
|
|
ref->snoopbuf = DirtyMemAlloc(limit*sizeof(ref->snoopbuf[0]), PROTOADVT_MEMID, ref->memgroup, ref->memgrpusrdata);
|
|
if (ref->snoopbuf == NULL)
|
|
{
|
|
DirtyMemFree(ref, PROTOADVT_MEMID, ref->memgroup, ref->memgrpusrdata);
|
|
return(NULL);
|
|
}
|
|
ref->snoopend = ref->snoopbuf+limit;
|
|
ds_memclr(ref->snoopbuf, (int32_t)((char *)ref->snoopend-(char *)ref->snoopbuf));
|
|
|
|
// set initial revision value
|
|
ref->snoop = 1;
|
|
|
|
// create the actual socket
|
|
sock = SocketOpen(AF_INET, SOCK_DGRAM, 0);
|
|
if (sock == NULL)
|
|
{
|
|
DirtyMemFree(ref, PROTOADVT_MEMID, ref->memgroup, ref->memgrpusrdata);
|
|
return(NULL);
|
|
}
|
|
|
|
// mark as available for sharing
|
|
g_ref = ref;
|
|
g_count = 1;
|
|
|
|
// need to sync access to data
|
|
NetCritInit(&ref->crit, "protoadvt");
|
|
// limit access during setup
|
|
NetCritEnter(&ref->crit);
|
|
|
|
// bind with local address
|
|
SockaddrInit(&bindaddr, AF_INET);
|
|
SockaddrInSetPort(&bindaddr, ADVERT_BROADCAST_PORT_UDP);
|
|
SocketBind(sock, &bindaddr, sizeof(bindaddr));
|
|
|
|
// connect to remote address
|
|
SockaddrInit(&peeraddr, AF_INET);
|
|
SockaddrInSetPort(&peeraddr, ADVERT_BROADCAST_PORT_UDP);
|
|
SockaddrInSetAddr(&peeraddr, INADDR_BROADCAST);
|
|
|
|
// note: though it would be easier to use connect to bind the peer address
|
|
// to the socket, it will not work because winsock will not deliver an
|
|
// incoming packet to a socket connected to the sending port. therefore,
|
|
// the address must be maintained separately and used with sendto.
|
|
// (logic unchanged for dirtysock since it is already coded this way)
|
|
ds_memcpy_s(&ref->addr, sizeof(ref->addr), &peeraddr, sizeof(peeraddr));
|
|
|
|
// broadcast request for available server info (seed database)
|
|
ref->seed = 1;
|
|
// make the socket available
|
|
ref->sock = sock;
|
|
// bind the callback function
|
|
SocketCallback(ref->sock, CALLB_RECV, 100, ref, &_ProtoAdvtCallback);
|
|
|
|
// done with setup
|
|
NetCritLeave(&ref->crit);
|
|
|
|
// immediately send a seed request
|
|
// (the idle process will send a second within 100ms)
|
|
_ProtoAdvtRequestSeed(ref);
|
|
return(ref);
|
|
}
|
|
|
|
|
|
/*F*************************************************************************************/
|
|
/*!
|
|
\Function ProtoAdvtDestroy
|
|
|
|
\Description
|
|
Destruct an advertising agent
|
|
|
|
\Input *ref - construct ref
|
|
|
|
\Version 11/01/02 (GWS)
|
|
*/
|
|
/*************************************************************************************F*/
|
|
void ProtoAdvtDestroy(ProtoAdvtRef *ref)
|
|
{
|
|
SocketT *sock;
|
|
|
|
// make sure what is valid
|
|
if (ref == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// see if we are last
|
|
if (g_count > 1)
|
|
{
|
|
--g_count;
|
|
return;
|
|
}
|
|
|
|
// destroy the class
|
|
g_ref = NULL;
|
|
g_count = 0;
|
|
|
|
// cancel all announcements
|
|
while (ref->announce != NULL)
|
|
{
|
|
ProtoAdvtPacketT *packet = &ref->announce->packet;
|
|
ProtoAdvtCancel(ref, packet->kind, packet->name);
|
|
_ProtoAdvtCallback(ref->sock, 0, ref);
|
|
}
|
|
|
|
// make sure we own resources
|
|
NetCritEnter(&ref->crit);
|
|
|
|
// do the shutdown
|
|
sock = ref->sock;
|
|
ref->sock = NULL;
|
|
|
|
// release resources
|
|
NetCritLeave(&ref->crit);
|
|
|
|
// dispose of socket
|
|
SocketClose(sock);
|
|
|
|
// done with semaphore
|
|
NetCritKill(&ref->crit);
|
|
|
|
// done with ref
|
|
DirtyMemFree(ref->snoopbuf, PROTOADVT_MEMID, ref->memgroup, ref->memgrpusrdata);
|
|
DirtyMemFree(ref, PROTOADVT_MEMID, ref->memgroup, ref->memgrpusrdata);
|
|
}
|
|
|