int netserver_send(NETSERVER *s, NETCHUNK *chunk) { if(chunk->data_size >= NET_MAX_PAYLOAD) { dbg_msg("netserver", "packet payload too big. %d. dropping packet", chunk->data_size); return -1; } if(chunk->flags&NETSENDFLAG_CONNLESS) { /* send connectionless packet */ send_packet_connless(s->socket, &chunk->address, chunk->data, chunk->data_size); } else { int f = 0; dbg_assert(chunk->client_id >= 0, "errornous client id"); dbg_assert(chunk->client_id < s->max_clients, "errornous client id"); if(chunk->flags&NETSENDFLAG_VITAL) f = NET_CHUNKFLAG_VITAL; if(conn_queue_chunk(&s->slots[chunk->client_id].conn, f, chunk->data_size, chunk->data) == 0) { if(chunk->flags&NETSENDFLAG_FLUSH) conn_flush(&s->slots[chunk->client_id].conn); } else { netserver_drop(s, chunk->client_id, "error sending data"); } } return 0; }
void server_kick(int client_id, const char *reason) { if(client_id < 0 || client_id > MAX_CLIENTS) return; if(clients[client_id].state != SRVCLIENT_STATE_EMPTY) netserver_drop(net, client_id, reason); }
int netserver_update(NETSERVER *s) { unsigned now = time_timestamp(); int i; for(i = 0; i < s->max_clients; i++) { conn_update(&s->slots[i].conn); if(s->slots[i].conn.state == NET_CONNSTATE_ERROR) netserver_drop(s, i, conn_error(&s->slots[i].conn)); } /* remove expired bans */ while(s->banpool_firstused && s->banpool_firstused->info.expires < now) { NETBAN *ban = s->banpool_firstused; netserver_ban_remove_by_object(s, ban); } (void)now; return 0; }
int netserver_ban_add(NETSERVER *s, NETADDR addr, int seconds) { int iphash = (addr.ip[0]+addr.ip[1]+addr.ip[2]+addr.ip[3])&0xff; unsigned stamp = 0xffffffff; NETBAN *ban; /* remove the port */ addr.port = 0; if(seconds) stamp = time_timestamp() + seconds; /* search to see if it already exists */ ban = s->bans[iphash]; MACRO_LIST_FIND(ban, hashnext, net_addr_comp(&ban->info.addr, &addr) == 0); if(ban) { /* adjust the ban */ ban->info.expires = stamp; return 0; } if(!s->banpool_firstfree) return -1; /* fetch and clear the new ban */ ban = s->banpool_firstfree; MACRO_LIST_UNLINK(ban, s->banpool_firstfree, prev, next); /* setup the ban info */ ban->info.expires = stamp; ban->info.addr = addr; /* add it to the ban hash */ MACRO_LIST_LINK_FIRST(ban, s->bans[iphash], hashprev, hashnext); /* insert it into the used list */ { if(s->banpool_firstused) { NETBAN *insert_after = s->banpool_firstused; MACRO_LIST_FIND(insert_after, next, stamp < insert_after->info.expires); if(insert_after) insert_after = insert_after->prev; else { /* add to last */ insert_after = s->banpool_firstused; while(insert_after->next) insert_after = insert_after->next; } if(insert_after) { MACRO_LIST_LINK_AFTER(ban, insert_after, prev, next); } else { MACRO_LIST_LINK_FIRST(ban, s->banpool_firstused, prev, next); } } else { MACRO_LIST_LINK_FIRST(ban, s->banpool_firstused, prev, next); } } /* drop banned clients */ { char buf[128]; int i; NETADDR banaddr; if(seconds) str_format(buf, sizeof(buf), "you have been banned for %d minutes", seconds/60); else str_format(buf, sizeof(buf), "you have been banned for life"); for(i = 0; i < s->max_clients; i++) { banaddr = s->slots[i].conn.peeraddr; banaddr.port = 0; if(net_addr_comp(&addr, &banaddr) == 0) netserver_drop(s, i, buf); } } return 0; }
static void server_process_client_packet(NETCHUNK *packet) { int cid = packet->client_id; NETADDR addr; int sys; int msg = msg_unpack_start(packet->data, packet->data_size, &sys); if(clients[cid].state == SRVCLIENT_STATE_AUTH) { if(sys && msg == NETMSG_INFO) { char version[64]; const char *password; str_copy(version, msg_unpack_string(), 64); if(strcmp(version, mods_net_version()) != 0) { /* OH F**K! wrong version, drop him */ char reason[256]; str_format(reason, sizeof(reason), "wrong version. server is running '%s' and client '%s'.", mods_net_version(), version); netserver_drop(net, cid, reason); return; } str_copy(clients[cid].name, msg_unpack_string(), MAX_NAME_LENGTH); str_copy(clients[cid].clan, msg_unpack_string(), MAX_CLANNAME_LENGTH); password = msg_unpack_string(); if(config.password[0] != 0 && strcmp(config.password, password) != 0) { /* wrong password */ netserver_drop(net, cid, "wrong password"); return; } clients[cid].state = SRVCLIENT_STATE_CONNECTING; server_send_map(cid); } } else { if(sys) { /* system message */ if(msg == NETMSG_REQUEST_MAP_DATA) { int chunk = msg_unpack_int(); int chunk_size = 1024-128; int offset = chunk * chunk_size; int last = 0; /* drop faulty map data requests */ if(chunk < 0 || offset > current_map_size) return; if(offset+chunk_size >= current_map_size) { chunk_size = current_map_size-offset; if(chunk_size < 0) chunk_size = 0; last = 1; } msg_pack_start_system(NETMSG_MAP_DATA, MSGFLAG_VITAL|MSGFLAG_FLUSH); msg_pack_int(last); msg_pack_int(current_map_size); msg_pack_int(chunk_size); msg_pack_raw(¤t_map_data[offset], chunk_size); msg_pack_end(); server_send_msg(cid); if(config.debug) dbg_msg("server", "sending chunk %d with size %d", chunk, chunk_size); } else if(msg == NETMSG_READY) { if(clients[cid].state == SRVCLIENT_STATE_CONNECTING) { netserver_client_addr(net, cid, &addr); dbg_msg("server", "player is ready. cid=%x ip=%d.%d.%d.%d", cid, addr.ip[0], addr.ip[1], addr.ip[2], addr.ip[3] ); clients[cid].state = SRVCLIENT_STATE_READY; mods_connected(cid); } } else if(msg == NETMSG_ENTERGAME) { if(clients[cid].state == SRVCLIENT_STATE_READY) { netserver_client_addr(net, cid, &addr); dbg_msg("server", "player has entered the game. cid=%x ip=%d.%d.%d.%d", cid, addr.ip[0], addr.ip[1], addr.ip[2], addr.ip[3] ); clients[cid].state = SRVCLIENT_STATE_INGAME; mods_client_enter(cid); } } else if(msg == NETMSG_INPUT) { int tick, size, i; CLIENT_INPUT *input; int64 tagtime; clients[cid].last_acked_snapshot = msg_unpack_int(); tick = msg_unpack_int(); size = msg_unpack_int(); /* check for errors */ if(msg_unpack_error() || size/4 > MAX_INPUT_SIZE) return; if(clients[cid].last_acked_snapshot > 0) clients[cid].snap_rate = SRVCLIENT_SNAPRATE_FULL; if(snapstorage_get(&clients[cid].snapshots, clients[cid].last_acked_snapshot, &tagtime, 0, 0) >= 0) clients[cid].latency = (int)(((time_get()-tagtime)*1000)/time_freq()); /* add message to report the input timing */ /* skip packets that are old */ if(tick > clients[cid].last_input_tick) { int time_left = ((server_tick_start_time(tick)-time_get())*1000) / time_freq(); msg_pack_start_system(NETMSG_INPUTTIMING, 0); msg_pack_int(tick); msg_pack_int(time_left); msg_pack_end(); server_send_msg(cid); } clients[cid].last_input_tick = tick; input = &clients[cid].inputs[clients[cid].current_input]; if(tick <= server_tick()) tick = server_tick()+1; input->game_tick = tick; for(i = 0; i < size/4; i++) input->data[i] = msg_unpack_int(); mem_copy(clients[cid].latestinput.data, input->data, MAX_INPUT_SIZE*sizeof(int)); clients[cid].current_input++; clients[cid].current_input %= 200; /* call the mod with the fresh input data */ if(clients[cid].state == SRVCLIENT_STATE_INGAME) mods_client_direct_input(cid, clients[cid].latestinput.data); } else if(msg == NETMSG_RCON_CMD) { const char *cmd = msg_unpack_string(); if(msg_unpack_error() == 0 && clients[cid].authed) { dbg_msg("server", "cid=%d rcon='%s'", cid, cmd); console_execute_line(cmd); } } else if(msg == NETMSG_RCON_AUTH) { const char *pw; msg_unpack_string(); /* login name, not used */ pw = msg_unpack_string(); if(msg_unpack_error() == 0) { if(config.sv_rcon_password[0] == 0) { server_send_rcon_line(cid, "No rcon password set on server. Set sv_rcon_password to enable the remote console."); } else if(strcmp(pw, config.sv_rcon_password) == 0) { msg_pack_start_system(NETMSG_RCON_AUTH_STATUS, MSGFLAG_VITAL); msg_pack_int(1); msg_pack_end(); server_send_msg(cid); clients[cid].authed = 1; server_send_rcon_line(cid, "Authentication successful. Remote console access granted."); dbg_msg("server", "cid=%d authed", cid); } else { server_send_rcon_line(cid, "Wrong password."); } } } else if(msg == NETMSG_PING) { msg_pack_start_system(NETMSG_PING_REPLY, 0); msg_pack_end(); server_send_msg(cid); } else { char hex[] = "0123456789ABCDEF"; char buf[512]; int b; for(b = 0; b < packet->data_size && b < 32; b++) { buf[b*3] = hex[((const unsigned char *)packet->data)[b]>>4]; buf[b*3+1] = hex[((const unsigned char *)packet->data)[b]&0xf]; buf[b*3+2] = ' '; buf[b*3+3] = 0; } dbg_msg("server", "strange message cid=%d msg=%d data_size=%d", cid, msg, packet->data_size); dbg_msg("server", "%s", buf); } } else { /* game message */ if(clients[cid].state >= SRVCLIENT_STATE_READY) mods_message(msg, cid); } }