/****** 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 #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