diff --git a/src/engine/e_if_server.h b/src/engine/e_if_server.h index b165ae3cb..1acd184c5 100644 --- a/src/engine/e_if_server.h +++ b/src/engine/e_if_server.h @@ -135,6 +135,6 @@ int server_tick(); */ int server_tickspeed(); -int server_ban_add(NETADDR addr, int type, int seconds); +int server_ban_add(NETADDR addr, int seconds); int server_ban_remove(NETADDR addr); #endif diff --git a/src/engine/e_network.c b/src/engine/e_network.c index 0999d189b..2b4ed0137 100644 --- a/src/engine/e_network.c +++ b/src/engine/e_network.c @@ -264,6 +264,20 @@ unsigned char *unpack_chunk_header(unsigned char *data, NETCHUNKHEADER *header) } +void send_controlmsg(NETSOCKET socket, NETADDR *addr, int ack, int controlmsg, const void *extra, int extra_size) +{ + NETPACKETCONSTRUCT construct; + construct.flags = NET_PACKETFLAG_CONTROL; + construct.ack = ack; + construct.num_chunks = 0; + construct.data_size = 1+extra_size; + construct.chunk_data[0] = controlmsg; + mem_copy(&construct.chunk_data[1], extra, extra_size); + + /* send the control message */ + send_packet(socket, addr, &construct); +} + void netcommon_openlog(const char *sentlog, const char *recvlog) { if(sentlog) diff --git a/src/engine/e_network.h b/src/engine/e_network.h index d90bd42fe..04453cf98 100644 --- a/src/engine/e_network.h +++ b/src/engine/e_network.h @@ -18,7 +18,6 @@ typedef struct typedef struct { NETADDR addr; - int type; int expires; } NETBANINFO; @@ -72,7 +71,7 @@ int netserver_drop(NETSERVER *s, int client_id, const char *reason); int netserver_client_addr(NETSERVER *s, int client_id, NETADDR *addr); int netserver_max_clients(NETSERVER *s); -int netserver_ban_add(NETSERVER *s, NETADDR addr, int type, int seconds); +int netserver_ban_add(NETSERVER *s, NETADDR addr, int seconds); int netserver_ban_remove(NETSERVER *s, NETADDR addr); int netserver_ban_num(NETSERVER *s); /* caution, slow */ int netserver_ban_get(NETSERVER *s, int index, NETBANINFO *info); /* caution, slow */ diff --git a/src/engine/e_network_conn.c b/src/engine/e_network_conn.c index a8ef588fb..1d241d064 100644 --- a/src/engine/e_network_conn.c +++ b/src/engine/e_network_conn.c @@ -129,19 +129,11 @@ void conn_queue_chunk(NETCONNECTION *conn, int flags, int data_size, const void } } + static void conn_send_control(NETCONNECTION *conn, int controlmsg, const void *extra, int extra_size) { - NETPACKETCONSTRUCT construct; - construct.flags = NET_PACKETFLAG_CONTROL; - construct.ack = conn->ack; - construct.num_chunks = 0; - construct.data_size = 1+extra_size; - construct.chunk_data[0] = controlmsg; - mem_copy(&construct.chunk_data[1], extra, extra_size); - /* send the control message */ - send_packet(conn->socket, &conn->peeraddr, &construct); - conn->last_send_time = time_get(); + send_controlmsg(conn->socket, &conn->peeraddr, conn->ack, controlmsg, extra, extra_size); } static void conn_resend_chunk(NETCONNECTION *conn, NETCHUNKDATA *resend) diff --git a/src/engine/e_network_internal.h b/src/engine/e_network_internal.h index 704f4f4ad..6c58ec880 100644 --- a/src/engine/e_network_internal.h +++ b/src/engine/e_network_internal.h @@ -127,6 +127,7 @@ typedef struct NETRECVINFO unsigned char buffer[NET_MAX_PACKETSIZE]; } NETRECVINFO; +/* */ /* connection functions */ void conn_init(NETCONNECTION *conn, NETSOCKET socket); @@ -145,6 +146,7 @@ void recvinfo_start(NETRECVINFO *info, NETADDR *addr, NETCONNECTION *conn, int c int recvinfo_fetch_chunk(NETRECVINFO *info, NETCHUNK *chunk); /* misc helper functions */ +void send_controlmsg(NETSOCKET socket, NETADDR *addr, int ack, int controlmsg, const void *extra, int extra_size); void send_packet_connless(NETSOCKET socket, NETADDR *addr, const void *data, int data_size); void send_packet(NETSOCKET socket, NETADDR *addr, NETPACKETCONSTRUCT *packet); int unpack_packet(unsigned char *buffer, int size, NETPACKETCONSTRUCT *packet); diff --git a/src/engine/e_network_server.c b/src/engine/e_network_server.c index 7e60a9ce8..8ec65504b 100644 --- a/src/engine/e_network_server.c +++ b/src/engine/e_network_server.c @@ -11,13 +11,38 @@ typedef struct NETBAN { NETBANINFO info; + /* hash list */ struct NETBAN *hashnext; struct NETBAN *hashprev; + /* used or free list */ struct NETBAN *next; struct NETBAN *prev; } NETBAN; +#define MACRO_LIST_LINK_FIRST(object, first, prev, next) \ + { if(first) first->prev = object; \ + object->prev = (void*)0; \ + object->next = first; \ + first = object; } + +#define MACRO_LIST_LINK_AFTER(object, after, prev, next) \ + { object->prev = after; \ + object->next = after->next; \ + after->next = object; \ + if(object->next) \ + object->next->prev = object; \ + } + +#define MACRO_LIST_UNLINK(object, first, prev, next) \ + { if(object->next) object->next->prev = object->prev; \ + if(object->prev) object->prev->next = object->next; \ + else first = object->next; \ + object->next = 0; object->prev = 0; } + +#define MACRO_LIST_FIND(start, next, expression) \ + { while(start && !(expression)) start = start->next; } + struct NETSERVER { NETSOCKET socket; @@ -129,78 +154,98 @@ int netserver_ban_num(NETSERVER *s) return count; } -int netserver_ban_remove(NETSERVER *s, NETADDR addr) +static void netserver_ban_remove_by_object(NETSERVER *s, NETBAN *ban) { - return 0; + int iphash = (ban->info.addr.ip[0]+ban->info.addr.ip[1]+ban->info.addr.ip[2]+ban->info.addr.ip[3])&0xff; + dbg_msg("netserver", "removing ban on %d.%d.%d.%d", + ban->info.addr.ip[0], ban->info.addr.ip[1], ban->info.addr.ip[2], ban->info.addr.ip[3]); + MACRO_LIST_UNLINK(ban, s->banpool_firstused, prev, next); + MACRO_LIST_UNLINK(ban, s->bans[iphash], hashprev, hashnext); + MACRO_LIST_LINK_FIRST(ban, s->banpool_firstfree, prev, next); } -int netserver_ban_add(NETSERVER *s, NETADDR addr, int type, int seconds) +int netserver_ban_remove(NETSERVER *s, NETADDR addr) { int iphash = (addr.ip[0]+addr.ip[1]+addr.ip[2]+addr.ip[3])&0xff; - unsigned stamp = time_timestamp() + seconds; - NETBAN *ban; - NETBAN *insert_after; + NETBAN *ban = s->bans[iphash]; - /* search to see if it already exists */ - for(ban = s->bans[iphash]; ban; ban = ban->hashnext) + MACRO_LIST_FIND(ban, hashnext, net_addr_comp(&ban->info.addr, &addr) == 0); + + if(ban) { - if(net_addr_comp(&ban->info.addr, &addr) == 0) - { - if(ban->info.expires < stamp) - { - /* decide what to do here */ - } - return -1; - } + netserver_ban_remove_by_object(s, ban); + return 0; + } + + return -1; +} + +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 */ + /* fetch and clear the new ban */ ban = s->banpool_firstfree; - s->banpool_firstfree->prev = 0; - ban->next = 0; - ban->prev = 0; - ban->info.expires = 0; - if(seconds) - ban->info.expires = stamp; - ban->info.type = type; + 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 */ - if(s->bans[iphash]) - s->bans[iphash]->hashprev = ban; - ban->hashnext = s->bans[iphash]; - ban->hashprev = 0; - s->bans[iphash] = ban; + MACRO_LIST_LINK_FIRST(ban, s->bans[iphash], hashprev, hashnext); /* insert it into the used list */ - insert_after = s->banpool_firstused; - while(insert_after) { - if(!insert_after->next) - break; - if(insert_after->next->info.expires < stamp) - break; - insert_after = insert_after->next; - } - - if(!insert_after || insert_after->info.expires > stamp) - { - /* insert first */ - s->banpool_firstused = ban; - ban->next = insert_after; - ban->prev = 0; - } - else - { - /* insert after */ - ban->next = insert_after->next; - ban->prev = insert_after; - if(ban->next) - ban->next->prev = ban; - insert_after->next = ban; + 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 */ @@ -210,9 +255,9 @@ int netserver_ban_add(NETSERVER *s, NETADDR addr, int type, int seconds) NETADDR banaddr; if(seconds) - str_format(buf, sizeof(buf), "you have been banned for %d seconds", 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"); + str_format(buf, sizeof(buf), "you have been banned for life"); for(i = 0; i < s->max_clients; i++) { @@ -222,13 +267,14 @@ int netserver_ban_add(NETSERVER *s, NETADDR addr, int type, int seconds) if(net_addr_comp(&addr, &banaddr) == 0) netserver_drop(s, i, buf); } - } - + } return 0; } int netserver_update(NETSERVER *s) { + unsigned now = time_timestamp(); + int i; for(i = 0; i < s->max_clients; i++) { @@ -236,6 +282,16 @@ int netserver_update(NETSERVER *s) 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; } @@ -278,8 +334,23 @@ int netserver_recv(NETSERVER *s, NETCHUNK *chunk) } /* check if we just should drop the packet */ - if(ban && ban->info.type == NETBANTYPE_DROP && (!ban->info.expires || ban->info.expires > now)) + if(ban) + { + // banned, reply with a message + char banstr[128]; + if(ban->info.expires) + { + int mins = ((ban->info.expires - now)+59)/60; + if(mins == 1) + str_format(banstr, sizeof(banstr), "banned for %d minute", mins); + else + str_format(banstr, sizeof(banstr), "banned for %d minutes", mins); + } + else + str_format(banstr, sizeof(banstr), "banned for life"); + send_controlmsg(s->socket, &addr, 0, NET_CTRLMSG_CLOSE, banstr, str_length(banstr)+1); continue; + } if(s->recv.data.flags&NET_PACKETFLAG_CONNLESS) { @@ -296,43 +367,37 @@ int netserver_recv(NETSERVER *s, NETCHUNK *chunk) if(s->recv.data.flags&NET_PACKETFLAG_CONTROL && s->recv.data.chunk_data[0] == NET_CTRLMSG_CONNECT) { found = 0; - - if(ban && ban->info.expires > now) + + /* check if we already got this client */ + for(i = 0; i < s->max_clients; i++) { - /* TODO: soft ban, reply with a message */ + if(s->slots[i].conn.state != NET_CONNSTATE_OFFLINE && + net_addr_comp(&s->slots[i].conn.peeraddr, &addr) == 0) + { + found = 1; /* silent ignore.. we got this client already */ + break; + } } - else + + /* client that wants to connect */ + if(!found) { - /* check if we already got this client */ for(i = 0; i < s->max_clients; i++) { - if(s->slots[i].conn.state != NET_CONNSTATE_OFFLINE && - net_addr_comp(&s->slots[i].conn.peeraddr, &addr) == 0) + if(s->slots[i].conn.state == NET_CONNSTATE_OFFLINE) { - found = 1; /* silent ignore.. we got this client already */ + found = 1; + conn_feed(&s->slots[i].conn, &s->recv.data, &addr); + if(s->new_client) + s->new_client(i, s->user_ptr); break; } } - /* client that wants to connect */ if(!found) { - for(i = 0; i < s->max_clients; i++) - { - if(s->slots[i].conn.state == NET_CONNSTATE_OFFLINE) - { - found = 1; - conn_feed(&s->slots[i].conn, &s->recv.data, &addr); - if(s->new_client) - s->new_client(i, s->user_ptr); - break; - } - } - - if(!found) - { - /* TODO: send server full message */ - } + const char fullmsg[] = "server is full"; + send_controlmsg(s->socket, &addr, 0, NET_CTRLMSG_CLOSE, fullmsg, sizeof(fullmsg)); } } } diff --git a/src/engine/server/es_server.c b/src/engine/server/es_server.c index 05a989839..b8df0f50a 100644 --- a/src/engine/server/es_server.c +++ b/src/engine/server/es_server.c @@ -824,9 +824,9 @@ static void server_process_client_packet(NETCHUNK *packet) } -int server_ban_add(NETADDR addr, int type, int seconds) +int server_ban_add(NETADDR addr, int seconds) { - return netserver_ban_add(net, addr, type, seconds); + return netserver_ban_add(net, addr, seconds); } int server_ban_remove(NETADDR addr) @@ -1177,29 +1177,78 @@ static void con_kick(void *result, void *user_data) server_kick(console_arg_int(result, 0), "kicked by console"); } +static int str_allnum(const char *str) +{ + while(*str) + { + if(!(*str >= '0' && *str <= '9')) + return 0; + str++; + } + return 1; +} + static void con_ban(void *result, void *user_data) +{ + NETADDR addr; + char addrstr[128]; + const char *str = console_arg_string(result, 0); + int minutes = console_arg_int(result, 1); + + if(minutes == 0) + minutes = 30; + + if(net_addr_from_str(&addr, str) == 0) + server_ban_add(addr, minutes*60); + else if(str_allnum(str)) + { + NETADDR addr; + int cid = atoi(str); + + if(cid < 0 || cid > MAX_CLIENTS || clients[cid].state == SRVCLIENT_STATE_EMPTY) + { + dbg_msg("server", "invalid client id"); + return; + } + + netserver_client_addr(net, cid, &addr); + server_ban_add(addr, minutes*60); + } + + addr.port = 0; + net_addr_str(&addr, addrstr, sizeof(addrstr)); + + dbg_msg("server", "banned %s for %d minutes", addrstr, minutes); +} + +static void con_unban(void *result, void *user_data) { NETADDR addr; const char *str = console_arg_string(result, 0); if(net_addr_from_str(&addr, str) == 0) - server_ban_add(addr, 2, 60); + server_ban_remove(addr); + else + dbg_msg("server", "invalid network address"); } - static void con_bans(void *result, void *user_data) { int i; + unsigned now = time_timestamp(); NETBANINFO info; NETADDR addr; int num = netserver_ban_num(net); for(i = 0; i < num; i++) { + unsigned t; netserver_ban_get(net, i, &info); addr = info.addr; - dbg_msg("server", "#%d %d.%d.%d.%d", i, addr.ip[0], addr.ip[1], addr.ip[2], addr.ip[3]); + t = info.expires - now; + dbg_msg("server", "#%d %d.%d.%d.%d for %d minutes and %d seconds", i, addr.ip[0], addr.ip[1], addr.ip[2], addr.ip[3], t/60, t%60); } + dbg_msg("server", "%d ban(s)", num); } static void con_status(void *result, void *user_data) @@ -1238,7 +1287,8 @@ static void con_stoprecord(void *result, void *user_data) static void server_register_commands() { MACRO_REGISTER_COMMAND("kick", "i", con_kick, 0); - MACRO_REGISTER_COMMAND("ban", "r", con_ban, 0); + MACRO_REGISTER_COMMAND("ban", "s?i", con_ban, 0); + MACRO_REGISTER_COMMAND("unban", "s", con_unban, 0); MACRO_REGISTER_COMMAND("bans", "", con_bans, 0); MACRO_REGISTER_COMMAND("status", "", con_status, 0); MACRO_REGISTER_COMMAND("shutdown", "", con_shutdown, 0);