openmohaa/code/gamespy/gcdkey/gcdkeys.c

962 lines
25 KiB
C
Raw Permalink Normal View History

2023-02-04 21:00:01 +01:00
/******
gcdkeys.c
GameSpy CDKey SDK Server Code
Copyright 1999-2007 GameSpy Industries, Inc
devsupport@gamespy.com
******
Please see the GameSpy CDKey SDK documentation for more
information
******/
/********
INCLUDES
********/
#include "gcdkeys.h"
#include "../common/gsCommon.h"
#include "../common/gsAvailable.h"
#include "../common/gsDebug.h"
#include <time.h>
#ifdef GUSE_ASSERTS
#define gassert(a) assert(a)
#else
#define gassert(a)
#endif
#define BUFSIZE 512
#ifdef __cplusplus
extern "C" {
#endif
/********
DEFINES
********/
#define VAL_PORT 29910
/* #define VAL_ADDR "key.gamespy.com" */
/*#define VAL_ADDR "204.182.161.103"*/
#define VAL_TIMEOUT 2000
#define VAL_RETRIES 2
#define INBUF_LEN 1024
#define MAX_PRODUCTS 4
#define MAX_KEEP_ALIVE_INTERVAL 20000
#define MAXPENDING_REAUTH 5 // prevent memory growth from spammed reauths.
#define REAUTH_LIFESPAN 5000 // prevent memory growth from unanswered reauths.
#define PROOF_TXT 'p','r','o','o','f'
#define IGNORED_TXT 's','e','e','d'
/********
TYPEDEFS
********/
typedef enum {cs_sentreq, cs_gotok, cs_gotnok, cs_done} gsclientstate_t;
typedef struct gsnode_s
{
void *object;
struct gsnode_s *next, *prev;
} gsnode_t;
typedef struct gsclient_s
{
int localid;
char hkey[33];
int sesskey;
int ip;
unsigned long sttime;
int ntries;
gsclientstate_t state;
void *instance;
AuthCallBackFn authfn;
RefreshAuthCallBackFn refreshauthfn;
char *errmsg;
char *reqstr;
int reqlen;
gsnode_t reauthq;
} gsclient_t;
typedef struct gsreauth_s
{
int sesskey;
char challenge[33];
struct sockaddr_in fromaddr;
gsi_time starttime;
} gsreauth_t;
typedef struct gsproduct_s
{
int pid;
gsnode_t clientq;
} gsproduct_t;
/********
GLOBALS
********/
char gcd_hostname[64] = "";
/********
PROTOTYPES
********/
static void send_auth_req(gsproduct_t *prod, gsclient_t *client, const char *challenge, const char *response);
static void resend_auth_req(gsclient_t *client);
static void send_keep_alive();
static void send_disconnect_req(gsproduct_t *prod, gsclient_t *client);
static void cdkey_process_buf(char *buf, int len, struct sockaddr *fromaddr);
static void process_oks(char *buf, int isok);
static void process_ison(char *buf, struct sockaddr_in *fromaddr);
static void process_ucount(char *buf, struct sockaddr_in *fromaddr);
static void send_uon(int skey, const char* ignored, const char* proof, struct sockaddr_in *fromaddr);
static void free_client_node(gsnode_t *node);
static int get_sockaddrin(char *host, int port, struct sockaddr_in *saddr, struct hostent **savehent);
static void xcode_buf(char *buf, int len);
static char *value_for_key(const char *s, const char *key);
static void add_to_queue(gsnode_t *t, gsnode_t *que);
static gsnode_t *remove_from_queue(gsnode_t *t, gsnode_t *que);
static int gcd_init_common(int gameid);
static int init_incoming_socket();
static gsproduct_t *find_product(int gameid);
/********
VARS
********/
static SOCKET sock = INVALID_SOCKET;
static unsigned short localport = 0;
static char enc[9]; /* used for xor encoding */
static struct sockaddr_in valaddr;
static int numproducts = 0;
gsproduct_t products[MAX_PRODUCTS];
/****************************************************************************/
/* PUBLIC FUNCTIONS */
/****************************************************************************/
int gcd_init(int gameid)
{
int ret;
const char defaulthost[] = {'k','e','y','.','g','a','m','e','s','p','y','.','c','o','m','\0'}; //key.gamespy.com
// check if the backend is available
if(__GSIACResult != GSIACAvailable)
return -1;
if (sock == INVALID_SOCKET) //hasn't been initialized yet
{
/* set up the UDP socket */
SocketStartUp();
ret = init_incoming_socket();
if (ret < 0)
return ret;
if (gcd_hostname[0] == 0)
strcpy(gcd_hostname, defaulthost);
get_sockaddrin(gcd_hostname,VAL_PORT,&valaddr,NULL);
}
return gcd_init_common(gameid);
}
#ifdef QR2CDKEY_INTEGRATION
extern struct qr2_implementation_s static_qr2_rec;
int gcd_init_qr2(qr2_t qrec, int gameid)
{
// check if the backend is available
if(__GSIACResult != GSIACAvailable)
return -1;
if (qrec == NULL)
qrec = &static_qr2_rec;
localport = (unsigned short)-1; /* we don't process any incoming data ourselves - it gets passed from the QR SDK */
sock = qrec->hbsock;
qrec->cdkeyprocess = cdkey_process_buf;
/* grab the outgoing address from the QR SDK */
memset(&valaddr,0,sizeof(struct sockaddr_in));
valaddr.sin_family = AF_INET;
valaddr.sin_port = htons((unsigned short)VAL_PORT);
valaddr.sin_addr.s_addr = qrec->hbaddr.sin_addr.s_addr;
return gcd_init_common(gameid);
}
#endif
void gcd_shutdown(void)
{
int i;
/* Make sure everyone is disconnected */
for (i = 0 ; i < numproducts ; i++)
gcd_disconnect_all(products[i].pid);
if(localport != (unsigned short)-1)
{
closesocket(sock);
SocketShutDown();
}
sock = INVALID_SOCKET;
numproducts = 0;
}
void gcd_authenticate_user(int gameid, int localid, unsigned int userip, const char *challenge,
const char *response, AuthCallBackFn authfn, RefreshAuthCallBackFn refreshfn, void *instance)
{
gsnode_t *node;
gsclient_t *client;
char hkey[33];
char *errmsg = NULL;
char badcdkey_t[] = {'B','a','d',' ','C','D',' ','K','e','y','\0'}; //Bad CD Key
char keyinuse_t[] = {'C','D',' ','K','e','y',' ','i','n',' ','u','s','e','\0'}; //CD Key in use
gsproduct_t *prod = find_product(gameid);
gassert(prod);
if (prod == NULL)
return;
/* get the hashed key */
strncpy(hkey, response, 32);
hkey[32] = 0;
/* if response is bogus, lets kill them */
if (strlen(response) < 72)
errmsg = goastrdup(badcdkey_t);
/* First, scan the current list for the same, or similar client */
node = &prod->clientq;
while ((node = node->next) != NULL)
{
/* make sure the localid isn't being reused
Change this code if you want to allow multiple users with the same CD Key on the
same server */
gsclient_t* client = (gsclient_t*)node->object;
gassert(client->localid != localid);
if (strcmp(hkey, client->hkey) == 0)
{ /* they appear to be on already!! */
errmsg = goastrdup(keyinuse_t);
break;
}
}
/* Create a new client */
client = (gsclient_t *)gsimalloc(sizeof(gsclient_t));
gassert(client);
client->localid = localid;
client->ip = (int)userip;
client->instance = instance;
client->errmsg = NULL;
client->reqstr = NULL;
client->authfn = authfn;
client->refreshauthfn = refreshfn;
client->reauthq.next = NULL;
client->reauthq.object = NULL;
client->reauthq.prev = NULL;
strcpy(client->hkey, hkey);
node = (gsnode_t *)gsimalloc(sizeof(gsnode_t));
gassert(node);
node->object = (void*)client;
add_to_queue(node, &prod->clientq);
if (errmsg != NULL)
{ /* there was already and error, mark them to die */
client->state = cs_gotnok;
client->errmsg = errmsg;
} else /* They aren't on this server, lets check the validation server */
send_auth_req(prod, client,challenge, response);
}
void gcd_process_reauth(int gameid, int localid, int skey, const char *response)
{
// find the pending reauth attempt
gsnode_t *clientnode;
gsnode_t *reauthnode;
gsproduct_t *prod = find_product(gameid);
gassert(prod);
if (prod == NULL)
return;
// find the client for this gameid
clientnode = &prod->clientq;
while ((clientnode = clientnode->next) != NULL)
{
gsclient_t *client = (gsclient_t*)clientnode->object;
if (client->localid == localid)
{
// find the reauth info for this client/skey
reauthnode = &client->reauthq;
while((reauthnode = reauthnode->next) != NULL)
{
gsreauth_t *reauth = (gsreauth_t*)reauthnode->object;
if (reauth->sesskey == skey)
{
// send the proof to the keymaster
send_uon(skey, "", response, &reauth->fromaddr);
remove_from_queue(reauthnode, &client->reauthq);
gsifree(reauthnode->object);
gsifree(reauthnode);
return;
}
}
}
}
}
// utility to free memory associated with a client node
static void free_client_node(gsnode_t *node)
{
if (node)
{
gsclient_t* client = (gsclient_t*)node->object;
if (client)
{
if (client->reqstr != NULL)
gsifree(client->reqstr);
if (client->errmsg != NULL)
gsifree(client->errmsg);
// free auth nodes
while (client->reauthq.next != NULL)
{
gsnode_t* authNode = remove_from_queue(client->reauthq.next, &client->reauthq);
gsifree(authNode->object);
gsifree(authNode);
}
gsifree(client);
}
gsifree(node);
}
return;
}
void gcd_disconnect_user(int gameid, int localid)
{
gsnode_t *node;
gsproduct_t *prod = find_product(gameid);
gassert(prod);
if (prod == NULL)
return;
/* First, scan the list for the client*/
node = &prod->clientq;
while ((node = node->next) != NULL)
{
gsclient_t* client = (gsclient_t*)node->object;
if (client->localid == localid)
{
send_disconnect_req(prod, client);
remove_from_queue(node, &prod->clientq);
free_client_node(node);
return;
}
}
/* No client found -- we should never get here!
But we may if you call disconnect_user during an negative authentication
(they are already removed) */
}
void gcd_disconnect_all(int gameid)
{
gsnode_t *node;
gsproduct_t *prod = find_product(gameid);
gassert(prod);
if (prod == NULL)
return;
/* Clear the entire list */
node = &prod->clientq;
while ((node = node->next) != NULL)
{
gsclient_t* client = (gsclient_t*)node->object;
send_disconnect_req(prod, client);
remove_from_queue(node, &prod->clientq);
free_client_node(node);
node = &prod->clientq;
}
}
char *gcd_getkeyhash(int gameid, int localid)
{
gsproduct_t *prod = find_product(gameid);
gsnode_t *node;
gassert(prod);
if (prod == NULL)
return "";
node = &prod->clientq;
/* Scan the list for the client*/
while ((node = node->next) != NULL)
{
gsclient_t* client = (gsclient_t*)node->object;
if (client->localid == localid)
return client->hkey;
}
return "";
}
void gcd_think(void)
{
static char indata[INBUF_LEN];
struct sockaddr_in saddr;
int saddrlen = sizeof(saddr);
fd_set set;
struct timeval timeout = {0,0};
int error;
int i;
gsnode_t *node, *oldnode;
char validated_t[] = {'V','a','l','i','d','a','t','e','d','\0'}; //Validated
char timeout_t[] = {'V','a','l','i','d','a','t','i','o','n',' ','T','i','m','e','o','u','t','\0'}; //Validation Timeout
gassert (sock != INVALID_SOCKET);
/* First, check for data on the socket and process commands */
if (localport != (unsigned short)-1) /* don't check if we are getting data from the QR SDK instead */
{
FD_ZERO ( &set );
FD_SET ( sock, &set );
while (1)
{
error = select(FD_SETSIZE, &set, NULL, NULL, &timeout);
if (gsiSocketIsError(error) || 0 == error)
break;
/* else we have data */
error = recvfrom(sock, indata, INBUF_LEN - 1, 0, (struct sockaddr *)&saddr, &saddrlen);
if (gsiSocketIsNotError(error))
{
indata[error] = '\0';
cdkey_process_buf(indata, error, (struct sockaddr *)&saddr);
}
}
}
send_keep_alive();
for (i = 0 ; i < numproducts ; i++)
{
/* Next, update the status of any clients and make callbacks */
node = &products[i].clientq;
while ((node = node->next) != NULL)
{
gsclient_t* client = (gsclient_t*)node->object;
switch (client->state)
{
case cs_sentreq:
if (current_time() < client->sttime + VAL_TIMEOUT)
break; /* keep waiting */
if (client->ntries <= VAL_RETRIES)
{ /* resend */
resend_auth_req(client);
break;
} /* else, go ahead an auth them, the val server timed out */
case cs_gotok:
/* if authorized or they timed out with no response, just auth them */
client->authfn(products[i].pid, client->localid, 1,
client->state == cs_gotok ? validated_t : timeout_t,
client->instance);
client->state = cs_done;
gsifree(client->reqstr);
client->reqstr = NULL;
break;
case cs_gotnok:
/* remove them first, in case the user calls disconnect */
oldnode = node;
node = node->prev;
remove_from_queue(oldnode, &products[i].clientq);
client->authfn(products[i].pid, client->localid, 0,
client->errmsg == NULL ? "" : client->errmsg,
client->instance);
free_client_node(oldnode);
break;
case cs_done:
// check pending reauth timeouts
if (client->reauthq.next != NULL)
{
// always look at "next" because we may remove nodes
gsnode_t* authnode = &client->reauthq;
while(authnode->next != NULL)
{
gsreauth_t* authdata = (gsreauth_t*)authnode->next->object;
gsi_time now = current_time();
if ((now - authdata->starttime) > REAUTH_LIFESPAN)
{
gsDebugFormat(GSIDebugCat_CDKey, GSIDebugType_Misc, GSIDebugLevel_Notice,
"Removing timed out reauth request [localid: %d, from: %s\r\n",
client->localid, inet_ntoa(authdata->fromaddr.sin_addr));
// timed out, delete it
remove_from_queue(authnode->next, &client->reauthq);
gsifree(authdata);
gsifree(authnode->next);
authnode->next = NULL;
}
else
authnode = authnode->next;
}
}
break;
default:
break;
}
}
}
}
/****************************************************************************/
/* UTIL FUNCTIONS */
/****************************************************************************/
static void cdkey_process_buf(char *buf, int len, struct sockaddr *fromaddr)
{
char tok[32];
char *pos;
char uok_t[] = {'u','o','k','\0'}; //uok
char unok_t[] = {'u','n','o','k','\0'}; //unok
char ison_t[] = {'i','s','o','n','\0'}; //ison
char ucount_t[] = {'u','c','o','u','n','t','\0'}; //ucount
xcode_buf(buf, len);
tok[0] = 0;
if (buf[0] == '\\')
{
pos = strchr(buf+1,'\\');
if (pos && (pos - buf <= 32)) /* right size token */
{
strncpy(tok, buf+1,pos-buf-1);
tok[pos-buf-1] = 0;
}
}
if (!tok[0])
return; /* bad command */
if (!strcmp(tok, uok_t))
{
process_oks(buf, 1);
}
else if (!strcmp(tok, unok_t))
{
process_oks(buf, 0);
}
else if (!strcmp(tok, ison_t))
{
process_ison(buf, (struct sockaddr_in *)fromaddr);
}
else if (!strcmp(tok, ucount_t))
{
process_ucount(buf, (struct sockaddr_in *)fromaddr);
}
else
{
send_keep_alive();
return; /* bad command */
}
send_keep_alive();
}
static int init_incoming_socket()
{
int ret;
struct sockaddr_in saddr;
int saddrlen;
sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (sock == INVALID_SOCKET)
return -1;
get_sockaddrin(NULL,0,&saddr,NULL);
ret = bind(sock, (struct sockaddr *)&saddr, sizeof(saddr));
if (gsiSocketIsError(ret))
return -1;
saddrlen = sizeof(saddr);
ret = getsockname(sock,(struct sockaddr *)&saddr, &saddrlen);
if (gsiSocketIsError(ret))
return -1;
localport = saddr.sin_port;
return 0;
}
static int gcd_init_common(int gameid)
{
gsproduct_t *prod;
gassert(numproducts < MAX_PRODUCTS);
if (numproducts >= MAX_PRODUCTS)
return -1; //too many products
prod = &products[numproducts++];
prod->pid = gameid;
prod->clientq.next = NULL;
prod->clientq.prev = NULL;
prod->clientq.object = NULL;
srand((unsigned int)current_time());
enc[0]='g';enc[1]='a';enc[2]='m';enc[3]='e';
enc[4]='s';enc[5]='p';enc[6]='y';enc[7]=0;
return 0;
}
static gsclient_t *find_client(char *keyhash, int sesskey, int* productid)
{
gsnode_t *node;
int i;
for (i = 0 ; i < numproducts ; i++)
{
node = &products[i].clientq;
while ((node = node->next) != NULL)
{
gsclient_t* client = (gsclient_t*)node->object;
if (strcmp(keyhash, client->hkey) == 0 && (sesskey == -1 || client->sesskey == sesskey))
{
if (productid != NULL)
*productid = products[i].pid;
return client;
}
}
}
return NULL;
}
static void process_oks(char *buf, int isok)
{
int sesskey;
char keyhash[33];
gsclient_t *client;
const char skey_t[] = {'s','k','e','y','\0'}; //skey
const char cd_t[] = {'c','d','\0'}; //cd
const char errmsg_t[] = {'e','r','r','m','s','g','\0'}; //errmsg
/* Samples
\uok\\cd\fe6667736f0c8ed7ff5cd9c0e74f\skey\2342
\unok\\cd\fe6667736f0c8ed7ff5cd9c0e74f\skey\23423\errmsg\Already playing on xyz server */
sesskey = atoi(value_for_key(buf,skey_t));
strncpy(keyhash,value_for_key(buf,cd_t),32);
keyhash[32] = 0;
client = find_client(keyhash, sesskey, NULL);
if (!client)
return;
if (client->sesskey != sesskey) /* bad session key */
return;
if (client->state == cs_done) /* too late to do anything! */
return;
if (isok)
client->state = cs_gotok;
else
{
client->state = cs_gotnok;
client->errmsg = goastrdup(value_for_key(buf,errmsg_t));
}
}
static void process_ucount(char *buf, struct sockaddr_in *fromaddr)
{
char outbuf[64];
char ucountformat[] = {'\\','u','c','o','u','n','t','\\','%','d','\0'}; //\\ucount\\%d
char pid_t[] = {'p','i','d','\0'}; //pid
gsnode_t *node;
int count = 0;
int len;
gsproduct_t *prod;
char *pos = value_for_key(buf, pid_t);
if (pos[0] == 0 && numproducts > 0) //not present.. use the first product
prod = &products[0];
else
prod = find_product(atoi(pos));
if (prod != NULL)
{
node = &prod->clientq;
while ((node = node->next) != NULL)
{
count++;
}
}
len = sprintf(outbuf, ucountformat, count);
xcode_buf(outbuf, len);
sendto(sock, outbuf, len, 0, (struct sockaddr *)fromaddr, sizeof(struct sockaddr_in));
}
static void send_uon(int skey, const char* ignored, const char* proof, struct sockaddr_in *fromaddr)
{
char outbuf[256];
int len;
const char uonformat[] = {'\\','u','o','n','\\','\\','s','k','e','y','\\','%','d','\\',IGNORED_TXT,'\\','%','s','\\',PROOF_TXT,'\\','%','s','\0'}; //\\uon\\\\skey\\%d\\seed\\%s\\proof\\%s
/* \ison\\cd\fe6667736f0c8ed7ff5cd9c0e74f\skey\32423 */
/* \uon\\skey\32423\seed\\proof\ OR \un\skey\32423\proof\fe6667736f0c8ed7ff5cd9c0e74f OR \uoff\\skey\32423 */
// seed is ignored by server
len = snprintf(outbuf, 255, uonformat,skey, ignored, proof);
outbuf[255] = '\0'; // snprintf doesn't null terminate in some cases
xcode_buf(outbuf, len);
sendto(sock, outbuf, len, 0, (struct sockaddr *)fromaddr, sizeof(struct sockaddr_in));
gsDebugFormat(GSIDebugCat_CDKey, GSIDebugType_Network, GSIDebugLevel_Notice,
"Sent uon response (ison) to %s. (proof: %s)\r\n",
inet_ntoa(fromaddr->sin_addr), proof);
}
static void send_uoff(int skey, struct sockaddr_in *fromaddr)
{
char outbuf[64];
int len;
const char uoffformat[] = {'\\','u','o','f','f','\\','\\','s','k','e','y','\\','%','d','\0'}; //\\uoff\\\\skey\\%d
len = sprintf(outbuf, uoffformat,skey);
xcode_buf(outbuf, len);
sendto(sock, outbuf, len, 0, (struct sockaddr *)fromaddr, sizeof(struct sockaddr_in));
}
static int get_queue_size(gsnode_t* node)
{
int count = 0;
if (!node)
return 0;
if (node->object) // starting from a valid node
count++;
while (node->next != NULL)
{
count++;
node = node->next;
}
return count;
}
static void process_ison(char *buf, struct sockaddr_in *fromaddr)
{
int sesskey;
int productid;
char* proofchallenge;
gsclient_t *client;
const char proofchallenge_t[] = {'p','c','h','\0'}; // proof challenge
const char skey_t[] = {'s','k','e','y','\0'}; //skey
const char cd_t[] = {'c','d','\0'}; //cd
sesskey = atoi(value_for_key(buf,skey_t));
proofchallenge = value_for_key(buf,proofchallenge_t);
if ( (client = find_client(value_for_key(buf,cd_t), -1, &productid)) != NULL
&& (client->state == cs_done)) /* If they are connected, return on */
{
// check the queue size to prevent memory growth (from malicious reauth requests)
int count = get_queue_size(&client->reauthq);
if (count < MAXPENDING_REAUTH)
{
// store the sesskey and fromaddr so we can respond with proof later
gsnode_t* node = (gsnode_t*)gsimalloc(sizeof(gsnode_t));
gsreauth_t* reauthdata = (gsreauth_t*)gsimalloc(sizeof(gsreauth_t));
gassert(node);
gassert(reauthdata);
memcpy(reauthdata->challenge, proofchallenge, 32);
memcpy(&reauthdata->fromaddr, fromaddr, sizeof(struct sockaddr_in));
reauthdata->sesskey = sesskey;
reauthdata->starttime = current_time();
node->object = (void*)reauthdata;
add_to_queue(node, &client->reauthq);
// send normal ison right away, later we'll followup with proof
// owatagusiam is ignored by server
send_uon(sesskey, "owatagusiam", "0", fromaddr);
// notify developer that we need proof of "ison"
client->refreshauthfn(productid, client->localid, sesskey, proofchallenge, client->instance);
}
}
else
{
send_uoff(sesskey, fromaddr);
}
}
static void send_disconnect_req(gsproduct_t *prod, gsclient_t *client)
{
char buf[BUFSIZE];
int len;
const char discformat[] = {'\\','d','i','s','c','\\','\\','p','i','d','\\','%','d','\\','c','d','\\','%','s','\\','i','p','\\','%','d','\0'}; //\\disc\\\\pid\\%d\\cd\\%s\\ip\\%d
/* \disc\\pid\12\cd\fe6667736f0c8ed7ff5cd9c0e74f\ip\2342342 */
len = sprintf(buf,discformat,
prod->pid, client->hkey,client->ip);
xcode_buf(buf, len);
sendto(sock, buf, len, 0, (struct sockaddr *)&valaddr, sizeof(valaddr));
}
static void send_auth_req(gsproduct_t *prod, gsclient_t *client, const char *challenge, const char *response)
{
char buf[BUFSIZE];
int len;
const char authformat[] = {'\\','a','u','t','h','\\','\\','p','i','d','\\','%','d','\\','c','h','\\','%','s','\\','r','e','s','p','\\','%','s','\\','i','p','\\','%','d','\\','s','k','e','y','\\','%','d','\\','r','e','q','p','r','o','o','f','\\','1','\\','\0'}; //\\auth\\\\pid\\%d\\ch\\%s\\resp\\%s\\ip\\%d\\skey\\%d\\reqproof\\1
client->state = cs_sentreq;
client->sesskey = (unsigned int)(rand() ^ current_time()) % 16384;
client->sttime = current_time();
client->ntries = 1;
/* \auth\\pid\12\ch\efx3232\resp\fe6667736f0c8ed7ff5cd9c0e74f98fd69e4da39560b82f40a628522ed10f0165c1d44a0\ip\2342342\skey\132432 */
len = snprintf(buf, BUFSIZE, authformat,
prod->pid, challenge, response, client->ip, client->sesskey);
buf[BUFSIZE-1] = '\0'; // sometimes snprintf doesn't null terminate
xcode_buf(buf, len);
sendto(sock, buf, len, 0, (struct sockaddr *)&valaddr, sizeof(valaddr));
/* save a copy for resends */
client->reqstr = (char *)gsimalloc(len);
memmove(client->reqstr, buf, len);
client->reqlen = len;
}
static void resend_auth_req(gsclient_t *client)
{
client->sttime = current_time();
client->ntries++;
sendto(sock, client->reqstr, client->reqlen, 0, (struct sockaddr *)&valaddr, sizeof(valaddr));
}
static void send_keep_alive()
{
static gsi_time lastKeepAliveSent = 0;
static const char *keepAlive = "\\ka\\\0";
char buf[BUFSIZE];
if (lastKeepAliveSent == 0)
lastKeepAliveSent = current_time();
if (current_time() > lastKeepAliveSent + MAX_KEEP_ALIVE_INTERVAL)
{
strcpy(buf, keepAlive);
xcode_buf(buf, strlen(keepAlive));
sendto(sock, buf, strlen(keepAlive), 0, (struct sockaddr *)&valaddr, sizeof(struct sockaddr_in));
lastKeepAliveSent = current_time();
}
}
/* value_for_key: this returns a value for a certain key in s, where s is a string
containing key\value pairs. If the key does not exist, it returns ""
Note: the value is stored in a common buffer. If you want to keep it, make a copy! */
static char *value_for_key(const char *s, const char *key)
{
static int valueindex;
char *pos,*pos2;
char slash_t[] = {'\\','\0'};
char keyspec[256];
static char value[2][256];
valueindex ^= 1;
strcpy(keyspec, slash_t);
strcat(keyspec,key);
strcat(keyspec,slash_t);
pos = strstr(s,keyspec);
if (!pos)
return "";
pos += strlen(keyspec);
pos2 = value[valueindex];
while (*pos && *pos != '\\' && (pos2 - value[valueindex] < 200))
*pos2++ = *pos++;
*pos2 = '\0';
return value[valueindex];
}
/* simple xor encoding */
static void xcode_buf(char *buf, int len)
{
int i;
char *pos = enc;
for (i = 0 ; i < len ; i++)
{
buf[i] ^= *pos++;
if (*pos == 0)
pos = enc;
}
}
/* Return a sockaddrin for the given host (numeric or DNS) and port)
Returns the hostent in savehent if it is not NULL */
static int get_sockaddrin(char *host, int port, struct sockaddr_in *saddr, struct hostent **savehent)
{
struct hostent *hent = NULL;
char broadcast_t[] = {'2','5','5','.','2','5','5','.','2','5','5','.','2','5','5','\0'}; //255.255.255.255
memset(saddr,0,sizeof(struct sockaddr_in));
saddr->sin_family = AF_INET;
saddr->sin_port = htons((unsigned short)port);
if (host == NULL)
saddr->sin_addr.s_addr = INADDR_ANY;
else
saddr->sin_addr.s_addr = inet_addr(host);
if (saddr->sin_addr.s_addr == INADDR_NONE && strcmp(host,broadcast_t) != 0)
{
hent = gethostbyname(host);
if (!hent)
return 0;
saddr->sin_addr.s_addr = *(u_long *)hent->h_addr_list[0];
}
if (savehent != NULL)
*savehent = hent;
return 1;
}
static gsproduct_t *find_product(int gameid)
{
int i;
for (i = 0 ; i < numproducts ; i++)
if (products[i].pid == gameid)
return &products[i];
return NULL;
}
/***********
Linked List Code
***********/
/*******
add_to_queue
*******/
static void add_to_queue(gsnode_t *t, gsnode_t *que)
{
while(que->next)
que=que->next;
que->next = t;
t->prev = que;
t->next = NULL;
}
/*******
remove_from_queue
if NULL is given as first parameter, top list item is popped off
item that is removed is returned, or NULL if not found
*******/
static gsnode_t *remove_from_queue(gsnode_t *t, gsnode_t *que)
{
if(!t) t = que->next;
if(!t) return(NULL);
t->prev->next = t->next;
if(t->next)
t->next->prev = t->prev;
return(t);
}
#ifdef __cplusplus
}
#endif