Kawe Mazidjatari b3a68ed095 Add EABase, EAThread and DirtySDK to R5sdk
DirtySDK (EA's Dirty Sockets library) will be used for the LiveAPI implementation, and depends on: EABase, EAThread.
2024-04-05 18:29:03 +02:00

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);
}