393 lines
6.5 KiB
C
393 lines
6.5 KiB
C
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <sys/uio.h>
|
|
#include <sys/types.h>
|
|
#include <sys/socket.h>
|
|
#include <netinet/in.h>
|
|
#include <arpa/inet.h>
|
|
#include <netdb.h>
|
|
#include <sys/timeb.h>
|
|
#include <sys/time.h>
|
|
#include <time.h>
|
|
#include <signal.h>
|
|
#include <fcntl.h>
|
|
|
|
|
|
#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) && counter<MAXMATCHES)
|
|
{
|
|
++counter;
|
|
memmove(rput,h->reg.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;i<argc;++i)
|
|
if(!strncmp(argv[i],"-p",2))
|
|
{
|
|
if(strlen(argv[i])>2) want=atoi(argv[i]+2);
|
|
else if(i+1<argc) want=atoi(argv[i+1]);
|
|
}
|
|
}
|
|
freehosts=0;
|
|
openport(want);
|
|
purgetime=longtime()+TIMETOLIVE;
|
|
for(;;)
|
|
{
|
|
size=getmsg(10);
|
|
if(size>=1)
|
|
switch(*mesg)
|
|
{
|
|
case PKT_REGISTER:
|
|
if(size<sizeof(struct registration)) continue;
|
|
doreg();
|
|
break;
|
|
case PKT_QUERY:
|
|
if(size<sizeof(struct query)) continue;
|
|
doquery();
|
|
break;
|
|
}
|
|
now=longtime();
|
|
if(now-purgetime>0) // avoid year 203x bug...
|
|
{
|
|
purge(purgetime);
|
|
purgetime+=TIMETOLIVE;
|
|
}
|
|
}
|
|
}
|