/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ #include #include extern "C" { #include } #include "mastersrv.h" enum { MTU = 1400, EXPIRE_TIME = 90 }; static struct CHECK_SERVER { NETADDR address; NETADDR alt_address; int try_count; int64 try_time; } check_servers[MAX_SERVERS]; static int num_checkservers = 0; static struct PACKET_DATA { unsigned char header[sizeof(SERVERBROWSE_LIST)]; NETADDR servers[MAX_SERVERS]; } data; static struct COUNT_PACKET_DATA { unsigned char header[sizeof(SERVERBROWSE_COUNT)]; unsigned char high; unsigned char low; } count_data; static int64 server_expire[MAX_SERVERS]; static int num_servers = 0; static net_client net_checker; // NAT/FW checker static net_client net_op; // main void send_ok(NETADDR *addr) { NETCHUNK p; p.client_id = -1; p.address = *addr; p.flags = NETSENDFLAG_CONNLESS; p.data_size = sizeof(SERVERBROWSE_FWOK); p.data = SERVERBROWSE_FWOK; // send on both to be sure net_checker.send(&p); net_op.send(&p); } void send_error(NETADDR *addr) { NETCHUNK p; p.client_id = -1; p.address = *addr; p.flags = NETSENDFLAG_CONNLESS; p.data_size = sizeof(SERVERBROWSE_FWERROR); p.data = SERVERBROWSE_FWERROR; net_op.send(&p); } void send_check(NETADDR *addr) { NETCHUNK p; p.client_id = -1; p.address = *addr; p.flags = NETSENDFLAG_CONNLESS; p.data_size = sizeof(SERVERBROWSE_FWCHECK); p.data = SERVERBROWSE_FWCHECK; net_checker.send(&p); } void add_checkserver(NETADDR *info, NETADDR *alt) { // add server if(num_checkservers == MAX_SERVERS) { dbg_msg("mastersrv", "error: mastersrv is full"); return; } dbg_msg("mastersrv", "checking: %d.%d.%d.%d:%d (%d.%d.%d.%d:%d)", info->ip[0], info->ip[1], info->ip[2], info->ip[3], info->port, alt->ip[0], alt->ip[1], alt->ip[2], alt->ip[3], alt->port); check_servers[num_checkservers].address = *info; check_servers[num_checkservers].alt_address = *alt; check_servers[num_checkservers].try_count = 0; check_servers[num_checkservers].try_time = 0; num_checkservers++; } void add_server(NETADDR *info) { // see if server already exists in list int i; for(i = 0; i < num_servers; i++) { if(net_addr_comp(&data.servers[i], info) == 0) { dbg_msg("mastersrv", "updated: %d.%d.%d.%d:%d", info->ip[0], info->ip[1], info->ip[2], info->ip[3], info->port); server_expire[i] = time_get()+time_freq()*EXPIRE_TIME; return; } } // add server if(num_servers == MAX_SERVERS) { dbg_msg("mastersrv", "error: mastersrv is full"); return; } dbg_msg("mastersrv", "added: %d.%d.%d.%d:%d", info->ip[0], info->ip[1], info->ip[2], info->ip[3], info->port); data.servers[num_servers] = *info; server_expire[num_servers] = time_get()+time_freq()*EXPIRE_TIME; num_servers++; } void update_servers() { int64 now = time_get(); int64 freq = time_freq(); for(int i = 0; i < num_checkservers; i++) { if(now > check_servers[i].try_time+freq) { if(check_servers[i].try_count == 10) { dbg_msg("mastersrv", "check failed: %d.%d.%d.%d:%d", check_servers[i].address.ip[0], check_servers[i].address.ip[1], check_servers[i].address.ip[2], check_servers[i].address.ip[3],check_servers[i].address.port, check_servers[i].alt_address.ip[0], check_servers[i].alt_address.ip[1], check_servers[i].alt_address.ip[2], check_servers[i].alt_address.ip[3],check_servers[i].alt_address.port); // FAIL!! send_error(&check_servers[i].address); check_servers[i] = check_servers[num_checkservers-1]; num_checkservers--; i--; } else { check_servers[i].try_count++; check_servers[i].try_time = now; if(check_servers[i].try_count&1) send_check(&check_servers[i].address); else send_check(&check_servers[i].alt_address); } } } } void purge_servers() { int64 now = time_get(); int i = 0; while(i < num_servers) { if(server_expire[i] < now) { // remove server dbg_msg("mastersrv", "expired: %d.%d.%d.%d:%d", data.servers[i].ip[0], data.servers[i].ip[1], data.servers[i].ip[2], data.servers[i].ip[3], data.servers[i].port); data.servers[i] = data.servers[num_servers-1]; server_expire[i] = server_expire[num_servers-1]; num_servers--; } else i++; } } int main(int argc, char **argv) { NETADDR bindaddr; dbg_logger_stdout(); net_init(); mem_zero(&bindaddr, sizeof(bindaddr)); bindaddr.port = MASTERSERVER_PORT; net_op.open(bindaddr, 0); bindaddr.port = MASTERSERVER_PORT+1; net_checker.open(bindaddr, 0); // TODO: check socket for errors mem_copy(data.header, SERVERBROWSE_LIST, sizeof(SERVERBROWSE_LIST)); mem_copy(count_data.header, SERVERBROWSE_COUNT, sizeof(SERVERBROWSE_COUNT)); dbg_msg("mastersrv", "started"); while(1) { net_op.update(); net_checker.update(); // process packets NETCHUNK packet; while(net_op.recv(&packet)) { if(packet.data_size == sizeof(SERVERBROWSE_HEARTBEAT)+2 && memcmp(packet.data, SERVERBROWSE_HEARTBEAT, sizeof(SERVERBROWSE_HEARTBEAT)) == 0) { NETADDR alt; unsigned char *d = (unsigned char *)packet.data; alt = packet.address; alt.port = (d[sizeof(SERVERBROWSE_HEARTBEAT)]<<8) | d[sizeof(SERVERBROWSE_HEARTBEAT)+1]; // add it add_checkserver(&packet.address, &alt); } else if(packet.data_size == sizeof(SERVERBROWSE_GETCOUNT) && memcmp(packet.data, SERVERBROWSE_GETCOUNT, sizeof(SERVERBROWSE_GETCOUNT)) == 0) { dbg_msg("mastersrv", "count requested, responding with %d", num_servers); NETCHUNK p; p.client_id = -1; p.address = packet.address; p.flags = NETSENDFLAG_CONNLESS; p.data_size = sizeof(count_data); p.data = &count_data; count_data.high = (num_servers>>8)&0xff; count_data.low = num_servers&0xff; net_op.send(&p); } else if(packet.data_size == sizeof(SERVERBROWSE_GETLIST) && memcmp(packet.data, SERVERBROWSE_GETLIST, sizeof(SERVERBROWSE_GETLIST)) == 0) { // someone requested the list dbg_msg("mastersrv", "requested, responding with %d servers", num_servers); NETCHUNK p; p.client_id = -1; p.address = packet.address; p.flags = NETSENDFLAG_CONNLESS; p.data_size = num_servers*sizeof(NETADDR)+sizeof(SERVERBROWSE_LIST); p.data = &data; net_op.send(&p); } } // process packets while(net_checker.recv(&packet)) { if(packet.data_size == sizeof(SERVERBROWSE_FWRESPONSE) && memcmp(packet.data, SERVERBROWSE_FWRESPONSE, sizeof(SERVERBROWSE_FWRESPONSE)) == 0) { // remove it from checking for(int i = 0; i < num_checkservers; i++) { if(net_addr_comp(&check_servers[i].address, &packet.address) == 0 || net_addr_comp(&check_servers[i].alt_address, &packet.address) == 0) { num_checkservers--; check_servers[i] = check_servers[num_checkservers]; break; } } add_server(&packet.address); send_ok(&packet.address); } } // TODO: shouldn't be done every fuckin frame purge_servers(); update_servers(); // be nice to the CPU thread_sleep(1); } return 0; }