#include #include #include #include #include #include #include #include #include #include #include #include #include #include #define MAXMSG 256 #define PORT 5521 #define TIMETOLIVE 600 // seconds typedef unsigned char uchar; char logging=0; unsigned char mesg[MAXMSG]; int lastsize; int network; char masterhostname[256]; #define PKT_REGISTER 16 #define PKT_ACK 24 #define PKT_INFO 40 #define PKT_QUERY 32 #define MAXMATCHES 16 struct registration { uchar id; uchar unique[4]; uchar password[4]; uchar version[4]; uchar name[16]; uchar status; }; struct query { uchar id; uchar password[4]; uchar version[4]; }; struct gamehost { struct gamehost *next,*prev; uchar machine[4]; uchar port[2]; struct registration reg; long timeout; }; struct gamehost *freehosts=0,*activehosts=0; int udpsocket,myport; struct sockaddr_in myname={0},mastername={0}; socklen_t senderlength; struct sockaddr_in sender={0}; long longtime(void) { struct timeb tb; ftime(&tb); return tb.time; } char *timestr() { static char timestring[80]; time_t t; int l; time(&t); strcpy(timestring,ctime(&t)); l=strlen(timestring); if(l && timestring[l-1]=='\n') timestring[l-1]=0; return timestring; } int putmsg(struct sockaddr_in *toname,unsigned char *msg,int len) { int status; status=sendto(udpsocket,msg,len,0, (struct sockaddr *)toname,sizeof(struct sockaddr_in)); return status; } void ack() { uchar copy[256]; *copy=PKT_ACK; memmove(copy+1,mesg,lastsize); putmsg(&sender,copy,lastsize+1); } int getmsg(int seconds) { int size; lastsize=-1; memset(&sender,0,sizeof(sender)); senderlength=sizeof(sender); if(seconds) { struct timeval timeout; fd_set readfds; int res; timeout.tv_sec=seconds; timeout.tv_usec=0; FD_ZERO(&readfds); FD_SET(udpsocket,&readfds); res=select(udpsocket+1,&readfds,0,0,&timeout); if(res<=0) return -1; } lastsize=size=recvfrom(udpsocket,mesg,MAXMSG,0, (struct sockaddr *)&sender,&senderlength); return size; } long longind(unsigned char *p) { return (p[0]<<24L) | (p[1]<<16L) | (p[2]<<8) | p[3]; } short shortind(unsigned char *p) { return (p[0]<<8L) | p[1]; } void openport(int portwant) { int status; myport=portwant; udpsocket=socket(PF_INET,SOCK_DGRAM,IPPROTO_UDP); if(udpsocket==-1) { perror("socket()"); exit(1); } memset(&myname,0,sizeof(myname)); myname.sin_family=AF_INET; myname.sin_addr.s_addr=htonl(INADDR_ANY); myname.sin_port=htons(myport); status=bind(udpsocket,(struct sockaddr *) &myname,sizeof(myname)); if(status==-1) { perror("bind()"); exit(1); } } #define PERBLOCK 512 struct gamehost *newhost() { struct gamehost *h,*block; int i; if(!freehosts) { block=malloc(sizeof(struct gamehost)*PERBLOCK); if(!block) return 0; freehosts=block; i=PERBLOCK-1; while(i--) { block->next=block+1; ++block; } block->next=0; } h=freehosts; freehosts=freehosts->next; memset(h,0,sizeof(struct gamehost)); return h; } void freehost(struct gamehost *h) { h->next=freehosts; freehosts=h; } struct gamehost *findmatch(struct registration *key) { struct gamehost *h; h=activehosts; while(h) { if(!memcmp(&h->reg,key,sizeof(struct registration)-1)) return h; h=h->next; } return 0; } void insert(struct gamehost *h) { if(activehosts) { h->next=activehosts; h->prev=activehosts->prev; activehosts->prev=h; activehosts=h; } else { h->next=h->prev=0; activehosts=h; } } void delete(struct gamehost *h) { if(h->prev) h->prev->next=h->next; else activehosts=h->next; if(h->next) h->next->prev=h->prev; freehost(h); } void doreg() { struct registration *new; struct gamehost *match; long now; new=(struct registration *)mesg; match=findmatch(new); if(logging) { unsigned addr=ntohl(sender.sin_addr.s_addr); unsigned short port=ntohs(sender.sin_port); printf("reg :%s:%d.%d.%d.%d:%d %c%lx '%s'\n",timestr(), (addr>>24)&255,(addr>>16)&255,(addr>>8)&255,addr&255,port, new->status ? '+' : '-',(long)match,new->name); fflush(stdout); } if(!match && !new->status) {ack();return;} if(match && new->status) {ack();return;} if(!match && new->status) { match=newhost(); if(!match) return; // No memory, what can we do? memmove(match->machine,&sender.sin_addr.s_addr,4); memmove(match->port,&sender.sin_port,2); match->reg=*new; now=longtime(); match->timeout=now+TIMETOLIVE; ack(); insert(match); return; } else // match && !new->status { delete(match); ack(); return; } } void doquery() { uchar *password; uchar *version; struct gamehost *h; uchar response[2048],*rput,*countersave; int counter; if(logging) { unsigned addr=ntohl(sender.sin_addr.s_addr); unsigned short port=ntohs(sender.sin_port); printf("query:%s:%d.%d.%d.%d:%d\n",timestr(), (addr>>24)&255,(addr>>16)&255,(addr>>8)&255,addr&255,port); fflush(stdout); } password=mesg+1; version=mesg+5; h=activehosts; rput=response; *rput++=PKT_INFO; memmove(rput,password,4); rput+=4; memmove(rput,version,4); rput+=4; countersave=rput; *rput++=0; *rput++=0; counter=0; while(h) { if(!memcmp(password,h->reg.password,4) && !memcmp(version,h->reg.version,4) && counterreg.unique,4); rput+=4; memmove(rput,h->machine,4); rput+=4; memmove(rput,h->port,2); rput+=2; memmove(rput,h->reg.name,sizeof(h->reg.name)); rput+=sizeof(h->reg.name); } h=h->next; } *countersave++=counter>>8; *countersave++=counter; putmsg(&sender,response,rput-response); } void purge(long cutoff) { struct gamehost *h,*h2; h=activehosts; while(h) { h2=h; h=h->next; if(cutoff-h2->timeout>0) { delete(h2); } } } int main(int argc,char **argv) { int i; int want; int size; long purgetime; long now; want=PORT; if(argc>1) { for(i=1;i2) want=atoi(argv[i]+2); else if(i+1=1) switch(*mesg) { case PKT_REGISTER: if(size0) // avoid year 203x bug... { purge(purgetime); purgetime+=TIMETOLIVE; } } }