358 lines
6.8 KiB
C
358 lines
6.8 KiB
C
#include <arpa/inet.h>
|
|
#include <fcntl.h>
|
|
#include <netdb.h>
|
|
#include <netinet/in.h>
|
|
#include <signal.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/time.h>
|
|
#include <sys/timeb.h>
|
|
#include <sys/types.h>
|
|
#include <sys/uio.h>
|
|
#include <time.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;
|
|
}
|
|
}
|
|
}
|