diff options
Diffstat (limited to 'src/common/socket.c')
-rw-r--r-- | src/common/socket.c | 851 |
1 files changed, 619 insertions, 232 deletions
diff --git a/src/common/socket.c b/src/common/socket.c index 5d4ea06a0..74d54dde8 100644 --- a/src/common/socket.c +++ b/src/common/socket.c @@ -2,8 +2,8 @@ * This file is part of Hercules. * http://herc.ws - http://github.com/HerculesWS/Hercules * - * Copyright (C) 2012-2015 Hercules Dev Team - * Copyright (C) Athena Dev Teams + * Copyright (C) 2012-2020 Hercules Dev Team + * Copyright (C) Athena Dev Teams * * Hercules is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -30,6 +30,7 @@ #include "common/memmgr.h" #include "common/mmo.h" #include "common/nullpo.h" +#include "common/packets.h" #include "common/showmsg.h" #include "common/strlib.h" #include "common/timer.h" @@ -38,51 +39,53 @@ #include <stdlib.h> #include <sys/types.h> +#ifdef SOCKET_EPOLL +#include <sys/epoll.h> +#endif // SOCKET_EPOLL + #ifdef WIN32 # include "common/winapi.h" -#else +#else // WIN32 # include <arpa/inet.h> # include <errno.h> # include <net/if.h> # include <netdb.h> #if defined __linux__ || defined __linux # include <linux/tcp.h> -#else +#else // defined __linux__ || defined __linux # include <netinet/in.h> # include <netinet/tcp.h> -#endif +#endif // defined __linux__ || defined __linux # include <sys/ioctl.h> # include <sys/socket.h> # include <sys/time.h> # include <unistd.h> -# ifndef SIOCGIFCONF -# include <sys/sockio.h> // SIOCGIFCONF on Solaris, maybe others? [Shinomori] -# endif -# ifndef FIONBIO -# include <sys/filio.h> // FIONBIO on Solaris [FlavioJS] -# endif +#ifndef SIOCGIFCONF +# include <sys/sockio.h> // SIOCGIFCONF on Solaris, maybe others? [Shinomori] +#endif // SIOCGIFCONF +#ifndef FIONBIO +# include <sys/filio.h> // FIONBIO on Solaris [FlavioJS] +#endif // FIONBIO -# ifdef HAVE_SETRLIMIT -# include <sys/resource.h> -# endif -#endif +#ifdef HAVE_SETRLIMIT +# include <sys/resource.h> +#endif // HAVE_SETRLIMIT +#endif // WIN32 /** * Socket Interface Source **/ -struct socket_interface sockt_s; +static struct socket_interface sockt_s; struct socket_interface *sockt; -struct socket_data **session; - #ifdef SEND_SHORTLIST // Add a fd to the shortlist so that it'll be recognized as a fd that needs // sending done on it. -void send_shortlist_add_fd(int fd); +static void send_shortlist_add_fd(int fd); // Do pending network sends (and eof handling) from the shortlist. -void send_shortlist_do_sends(void); -#endif +static void send_shortlist_do_sends(void); +#endif // SEND_SHORTLIST ///////////////////////////////////////////////////////////////////// #if defined(WIN32) @@ -117,7 +120,7 @@ static int sock_arr_len = 0; /// /// @param s Socket /// @return Fd or -1 -int sock2fd(SOCKET s) +static int sock2fd(SOCKET s) { int fd; @@ -138,7 +141,7 @@ int sock2fd(SOCKET s) /// /// @param s Socket /// @return New fd or -1 -int sock2newfd(SOCKET s) +static int sock2newfd(SOCKET s) { int fd; @@ -158,7 +161,7 @@ int sock2newfd(SOCKET s) return fd; } -int sAccept(int fd, struct sockaddr* addr, int* addrlen) +static int sAccept(int fd, struct sockaddr *addr, int *addrlen) { SOCKET s; @@ -169,14 +172,14 @@ int sAccept(int fd, struct sockaddr* addr, int* addrlen) return sock2newfd(s); } -int sClose(int fd) +static int sClose(int fd) { int ret = closesocket(fd2sock(fd)); fd2sock(fd) = INVALID_SOCKET; return ret; } -int sSocket(int af, int type, int protocol) +static int sSocket(int af, int type, int protocol) { SOCKET s; @@ -187,7 +190,7 @@ int sSocket(int af, int type, int protocol) return sock2newfd(s); } -char* sErr(int code) +static char *sErr(int code) { static char sbuf[512]; // strerror does not handle socket codes @@ -212,7 +215,7 @@ char* sErr(int code) #define sFD_ZERO FD_ZERO ///////////////////////////////////////////////////////////////////// -#else +#else // defined(WIN32) ///////////////////////////////////////////////////////////////////// // nix portability layer @@ -244,29 +247,40 @@ char* sErr(int code) #define sFD_ZERO FD_ZERO ///////////////////////////////////////////////////////////////////// -#endif +#endif // defined(WIN32) ///////////////////////////////////////////////////////////////////// #ifndef MSG_NOSIGNAL #define MSG_NOSIGNAL 0 -#endif +#endif // MSG_NOSIGNAL + +#ifndef SOCKET_EPOLL +// Select based Event Dispatcher: +static fd_set readfds; + +#else // SOCKET_EPOLL +// Epoll based Event Dispatcher: +static int epoll_maxevents = (FD_SETSIZE / 2); +static int epfd = SOCKET_ERROR; +static struct epoll_event epevent; +static struct epoll_event *epevents = NULL; -fd_set readfds; +#endif // SOCKET_EPOLL // Maximum packet size in bytes, which the client is able to handle. // Larger packets cause a buffer overflow and stack corruption. #if PACKETVER >= 20131223 static size_t socket_max_client_packet = 0xFFFF; -#else +#else // PACKETVER >= 20131223 static size_t socket_max_client_packet = 0x6000; -#endif +#endif // PACKETVER >= 20131223 #ifdef SHOW_SERVER_STATS // Data I/O statistics static size_t socket_data_i = 0, socket_data_ci = 0, socket_data_qi = 0; static size_t socket_data_o = 0, socket_data_co = 0, socket_data_qo = 0; static time_t socket_data_last_tick = 0; -#endif +#endif // SHOW_SERVER_STATS // initial recv buffer size (this will also be the max. size) // biggest known packet: S 0153 <len>.w <emblem data>.?B -> 24x24 256 color .bmp (0153 + len.w + 1618/1654/1756 bytes) @@ -279,19 +293,17 @@ static time_t socket_data_last_tick = 0; #define WFIFO_MAX (1*1024*1024) #ifdef SEND_SHORTLIST -int send_shortlist_array[FD_SETSIZE];// we only support FD_SETSIZE sockets, limit the array to that -int send_shortlist_count = 0;// how many fd's are in the shortlist -uint32 send_shortlist_set[(FD_SETSIZE+31)/32];// to know if specific fd's are already in the shortlist -#endif +static int send_shortlist_array[FD_SETSIZE];// we only support FD_SETSIZE sockets, limit the array to that +static int send_shortlist_count = 0;// how many fd's are in the shortlist +static uint32 send_shortlist_set[(FD_SETSIZE+31)/32];// to know if specific fd's are already in the shortlist +#endif // SEND_SHORTLIST static int create_session(int fd, RecvFunc func_recv, SendFunc func_send, ParseFunc func_parse); -#ifndef MINICORE - int ip_rules = 1; - static int connect_check(uint32 ip); -#endif +static int ip_rules = 1; +static int connect_check(uint32 ip); -const char* error_msg(void) +static const char *error_msg(void) { static char buf[512]; int code = sErrno; @@ -302,13 +314,24 @@ const char* error_msg(void) /*====================================== * CORE : Default processing functions *--------------------------------------*/ -int null_recv(int fd) { return 0; } -int null_send(int fd) { return 0; } -int null_parse(int fd) { return 0; } +static int null_recv(int fd) +{ + return 0; +} -ParseFunc default_func_parse = null_parse; +static int null_send(int fd) +{ + return 0; +} -void set_defaultparse(ParseFunc defaultparse) +static int null_parse(int fd) +{ + return 0; +} + +static ParseFunc default_func_parse = null_parse; + +static void set_defaultparse(ParseFunc defaultparse) { default_func_parse = defaultparse; } @@ -316,7 +339,7 @@ void set_defaultparse(ParseFunc defaultparse) /*====================================== * CORE : Socket options *--------------------------------------*/ -void set_nonblocking(int fd, unsigned long yes) +static void set_nonblocking(int fd, unsigned long yes) { // FIONBIO Use with a nonzero argp parameter to enable the nonblocking mode of socket s. // The argp parameter is zero if nonblocking is to be disabled. @@ -330,7 +353,7 @@ void set_nonblocking(int fd, unsigned long yes) * @param fd The socket descriptor * @param opt Optional, additional options to set (Can be NULL). */ -void setsocketopts(int fd, struct hSockOpt *opt) +static void setsocketopts(int fd, struct hSockOpt *opt) { #if defined(WIN32) BOOL yes = TRUE; @@ -380,30 +403,30 @@ void setsocketopts(int fd, struct hSockOpt *opt) ShowWarning("setsocketopts: Unable to set SO_LINGER mode for connection #%d!\n", fd); #ifdef TCP_THIN_LINEAR_TIMEOUTS - if (sSetsockopt(fd, IPPROTO_TCP, TCP_THIN_LINEAR_TIMEOUTS, (char *)&yes, sizeof(yes))) - ShowWarning("setsocketopts: Unable to set TCP_THIN_LINEAR_TIMEOUTS mode for connection #%d!\n", fd); -#endif + if (sSetsockopt(fd, IPPROTO_TCP, TCP_THIN_LINEAR_TIMEOUTS, (char *)&yes, sizeof(yes))) + ShowWarning("setsocketopts: Unable to set TCP_THIN_LINEAR_TIMEOUTS mode for connection #%d!\n", fd); +#endif // TCP_THIN_LINEAR_TIMEOUTS #ifdef TCP_THIN_DUPACK - if (sSetsockopt(fd, IPPROTO_TCP, TCP_THIN_DUPACK, (char *)&yes, sizeof(yes))) - ShowWarning("setsocketopts: Unable to set TCP_THIN_DUPACK mode for connection #%d!\n", fd); -#endif + if (sSetsockopt(fd, IPPROTO_TCP, TCP_THIN_DUPACK, (char *)&yes, sizeof(yes))) + ShowWarning("setsocketopts: Unable to set TCP_THIN_DUPACK mode for connection #%d!\n", fd); +#endif // TCP_THIN_DUPACK } /*====================================== * CORE : Socket Sub Function *--------------------------------------*/ -void set_eof(int fd) +static void set_eof(int fd) { if (sockt->session_is_active(fd)) { #ifdef SEND_SHORTLIST // Add this socket to the shortlist for eof handling. send_shortlist_add_fd(fd); -#endif +#endif // SEND_SHORTLIST sockt->session[fd]->flag.eof = 1; } } -int recv_to_fifo(int fd) +static int recv_to_fifo(int fd) { ssize_t len; @@ -436,11 +459,11 @@ int recv_to_fifo(int fd) { socket_data_ci += len; } -#endif - return 0; +#endif // SHOW_SERVER_STATS + return (int)len; } -int send_from_fifo(int fd) +static int send_from_fifo(int fd) { ssize_t len; @@ -453,23 +476,24 @@ int send_from_fifo(int fd) len = sSend(fd, (const char *) sockt->session[fd]->wdata, (int)sockt->session[fd]->wdata_size, MSG_NOSIGNAL); if( len == SOCKET_ERROR ) - {//An exception has occurred + { //An exception has occurred if( sErrno != S_EWOULDBLOCK ) { //ShowDebug("send_from_fifo: %s, ending connection #%d\n", error_msg(), fd); #ifdef SHOW_SERVER_STATS socket_data_qo -= sockt->session[fd]->wdata_size; -#endif +#endif // SHOW_SERVER_STATS sockt->session[fd]->wdata_size = 0; //Clear the send queue as we can't send anymore. [Skotlex] sockt->eof(fd); } return 0; } - if( len > 0 ) + if (len > 0) { + sockt->session[fd]->wdata_tick = sockt->last_tick; // some data could not be transferred? // shift unsent data to the beginning of the queue - if( (size_t)len < sockt->session[fd]->wdata_size ) + if ((size_t)len < sockt->session[fd]->wdata_size) memmove(sockt->session[fd]->wdata, sockt->session[fd]->wdata + len, sockt->session[fd]->wdata_size - len); sockt->session[fd]->wdata_size -= len; @@ -480,20 +504,20 @@ int send_from_fifo(int fd) { socket_data_co += len; } -#endif +#endif // SHOW_SERVER_STATS } return 0; } /// Best effort - there's no warranty that the data will be sent. -void flush_fifo(int fd) +static void flush_fifo(int fd) { if(sockt->session[fd] != NULL) sockt->session[fd]->func_send(fd); } -void flush_fifos(void) +static void flush_fifos(void) { int i; for(i = 1; i < sockt->fd_max; i++) @@ -503,7 +527,8 @@ void flush_fifos(void) /*====================================== * CORE : Connection functions *--------------------------------------*/ -int connect_client(int listen_fd) { +static int connect_client(int listen_fd) +{ int fd; struct sockaddr_in client_address; socklen_t len; @@ -529,23 +554,38 @@ int connect_client(int listen_fd) { setsocketopts(fd,NULL); sockt->set_nonblocking(fd, 1); -#ifndef MINICORE if( ip_rules && !connect_check(ntohl(client_address.sin_addr.s_addr)) ) { sockt->close(fd); return -1; } -#endif - if( sockt->fd_max <= fd ) sockt->fd_max = fd + 1; +#ifndef SOCKET_EPOLL + // Select Based Event Dispatcher sFD_SET(fd,&readfds); +#else // SOCKET_EPOLL + // Epoll based Event Dispatcher + epevent.data.fd = fd; + epevent.events = EPOLLIN; + + if(epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &epevent) == SOCKET_ERROR){ + ShowError("connect_client: New Socket #%d failed to add to epoll event dispatcher: %s\n", fd, error_msg()); + sClose(fd); + return -1; + } + +#endif // SOCKET_EPOLL + + if( sockt->fd_max <= fd ) sockt->fd_max = fd + 1; + create_session(fd, recv_to_fifo, send_from_fifo, default_func_parse); sockt->session[fd]->client_addr = ntohl(client_address.sin_addr.s_addr); + sockt->session[fd]->flag.validate = sockt->validate; return fd; } -int make_listen_bind(uint32 ip, uint16 port) +static int make_listen_bind(uint32 ip, uint16 port) { struct sockaddr_in server_address = { 0 }; int fd; @@ -586,17 +626,36 @@ int make_listen_bind(uint32 ip, uint16 port) exit(EXIT_FAILURE); } + +#ifndef SOCKET_EPOLL + // Select Based Event Dispatcher + sFD_SET(fd,&readfds); + +#else // SOCKET_EPOLL + // Epoll based Event Dispatcher + epevent.data.fd = fd; + epevent.events = EPOLLIN; + + if(epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &epevent) == SOCKET_ERROR){ + ShowError("make_listen_bind: failed to add listener socket #%d to epoll event dispatcher: %s\n", fd, error_msg()); + sClose(fd); + exit(EXIT_FAILURE); + } + +#endif // SOCKET_EPOLL + if(sockt->fd_max <= fd) sockt->fd_max = fd + 1; - sFD_SET(fd, &readfds); - create_session(fd, connect_client, null_send, null_parse); + + create_session(fd, sockt->connect_client, null_send, null_parse); sockt->session[fd]->client_addr = 0; // just listens sockt->session[fd]->rdata_tick = 0; // disable timeouts on this socket - + sockt->session[fd]->wdata_tick = 0; return fd; } -int make_connection(uint32 ip, uint16 port, struct hSockOpt *opt) { +static int make_connection(uint32 ip, uint16 port, struct hSockOpt *opt) +{ struct sockaddr_in remote_address = { 0 }; int fd; int result; @@ -637,9 +696,26 @@ int make_connection(uint32 ip, uint16 port, struct hSockOpt *opt) { //Now the socket can be made non-blocking. [Skotlex] sockt->set_nonblocking(fd, 1); - if (sockt->fd_max <= fd) sockt->fd_max = fd + 1; + +#ifndef SOCKET_EPOLL + // Select Based Event Dispatcher sFD_SET(fd,&readfds); +#else // SOCKET_EPOLL + // Epoll based Event Dispatcher + epevent.data.fd = fd; + epevent.events = EPOLLIN; + + if(epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &epevent) == SOCKET_ERROR){ + ShowError("make_connection: failed to add socket #%d to epoll event dispatcher: %s\n", fd, error_msg()); + sClose(fd); + return -1; + } + +#endif // SOCKET_EPOLL + + if(sockt->fd_max <= fd) sockt->fd_max = fd + 1; + create_session(fd, recv_to_fifo, send_from_fifo, default_func_parse); sockt->session[fd]->client_addr = ntohl(remote_address.sin_addr.s_addr); @@ -657,6 +733,7 @@ static int create_session(int fd, RecvFunc func_recv, SendFunc func_send, ParseF sockt->session[fd]->func_send = func_send; sockt->session[fd]->func_parse = func_parse; sockt->session[fd]->rdata_tick = sockt->last_tick; + sockt->session[fd]->wdata_tick = sockt->last_tick; sockt->session[fd]->session_data = NULL; sockt->session[fd]->hdata = NULL; return 0; @@ -668,7 +745,7 @@ static void delete_session(int fd) #ifdef SHOW_SERVER_STATS socket_data_qi -= sockt->session[fd]->rdata_size - sockt->session[fd]->rdata_pos; socket_data_qo -= sockt->session[fd]->wdata_size; -#endif +#endif // SHOW_SERVER_STATS aFree(sockt->session[fd]->rdata); aFree(sockt->session[fd]->wdata); if( sockt->session[fd]->session_data ) @@ -679,7 +756,7 @@ static void delete_session(int fd) } } -int realloc_fifo(int fd, unsigned int rfifo_size, unsigned int wfifo_size) +static int realloc_fifo(int fd, unsigned int rfifo_size, unsigned int wfifo_size) { if (!sockt->session_is_valid(fd)) return 0; @@ -696,7 +773,7 @@ int realloc_fifo(int fd, unsigned int rfifo_size, unsigned int wfifo_size) return 0; } -int realloc_writefifo(int fd, size_t addition) +static int realloc_writefifo(int fd, size_t addition) { size_t newsize; @@ -724,7 +801,7 @@ int realloc_writefifo(int fd, size_t addition) } /// advance the RFIFO cursor (marking 'len' bytes as processed) -int rfifoskip(int fd, size_t len) +static int rfifoskip(int fd, size_t len) { struct socket_data *s; @@ -735,23 +812,53 @@ int rfifoskip(int fd, size_t len) if (s->rdata_size < s->rdata_pos + len) { ShowError("RFIFOSKIP: skipped past end of read buffer! Adjusting from %"PRIuS" to %"PRIuS" (session #%d)\n", len, RFIFOREST(fd), fd); + Assert_report(0); len = RFIFOREST(fd); + } else { + const size_t lenRest = RFIFOREST(fd); + if (s->flag.validate == 1 && len == lenRest) { + if (lenRest >= 2) { + const uint32 cmd = (uint32)RFIFOW(fd, 0); + if (cmd < MIN_PACKET_DB || cmd > MAX_PACKET_DB) { + ShowError("Skip wrong packet id: 0x%04X\n", cmd); + Assert_report(0); + } else { + int packet_len = packets->db[cmd]; + if (packet_len == -1) { + if (lenRest < 4) { + ShowError("Skip packet with size smaller than 4\n"); + Assert_report(0); + } else { + packet_len = RFIFOW(fd, 2); + if (packet_len != lenRest) { + ShowError("Skip packet 0x%04X with dynamic size %"PRIuS", but must be size %d\n", cmd, lenRest, packet_len); + Assert_report(0); + } + } + } else if (packet_len != lenRest) { + ShowError("Skip packet 0x%04X with size %"PRIuS", but must be size %d\n", cmd, lenRest, packet_len); + Assert_report(0); + } + } + } + } } s->rdata_pos = s->rdata_pos + len; #ifdef SHOW_SERVER_STATS socket_data_qi -= len; -#endif +#endif // SHOW_SERVER_STATS return 0; } /// advance the WFIFO cursor (marking 'len' bytes for sending) -int wfifoset(int fd, size_t len) +static int wfifoset(int fd, size_t len, bool validate) { - size_t newreserve; - struct socket_data* s = sockt->session[fd]; + if (!sockt->session_is_valid(fd)) + return 0; - if (!sockt->session_is_valid(fd) || s->wdata == NULL) + struct socket_data* s = sockt->session[fd]; + if (s == NULL || s->wdata == NULL) return 0; // we have written len bytes to the buffer already before calling WFIFOSET @@ -789,48 +896,65 @@ int wfifoset(int fd, size_t len) } } + + if (validate && s->flag.validate == 1) + sockt->validateWfifo(fd, len); + s->wdata_size += len; #ifdef SHOW_SERVER_STATS socket_data_qo += len; -#endif +#endif // SHOW_SERVER_STATS //If the interserver has 200% of its normal size full, flush the data. if( s->flag.server && s->wdata_size >= 2*FIFOSIZE_SERVERLINK ) sockt->flush(fd); // always keep a WFIFO_SIZE reserve in the buffer // For inter-server connections, let the reserve be 1/4th of the link size. - newreserve = s->flag.server ? FIFOSIZE_SERVERLINK / 4 : WFIFO_SIZE; + size_t newreserve = s->flag.server ? FIFOSIZE_SERVERLINK / 4 : WFIFO_SIZE; // readjust the buffer to include the chosen reserve sockt->realloc_writefifo(fd, newreserve); #ifdef SEND_SHORTLIST send_shortlist_add_fd(fd); -#endif +#endif // SEND_SHORTLIST return 0; } -int do_sockets(int next) +static void wfifohead(int fd, size_t len) +{ + Assert_retv(fd >= 0); + + sockt->session[fd]->last_head_size = (uint32)len; + if (sockt->session[fd]->wdata_size + len > sockt->session[fd]->max_wdata) + sockt->realloc_writefifo(fd, len); +} + +static int do_sockets(int next) { +#ifndef SOCKET_EPOLL fd_set rfd; struct timeval timeout; +#endif // SOCKET_EPOLL int ret,i; // PRESEND Timers are executed before do_sendrecv and can send packets and/or set sessions to eof. // Send remaining data and process client-side disconnects here. #ifdef SEND_SHORTLIST send_shortlist_do_sends(); -#else - for (i = 1; i < sockt->fd_max; i++) - { - if(!sockt->session[fd] +#else // SEND_SHORTLIST + for (i = 1; i < sockt->fd_max; i++) { + if (sockt->session[i] == NULL) continue; - if(sockt->session[fd]>wdata_size) - sockt->session[fd]>func_send(i); + if (sockt->session[i]->wdata_size > 0) + sockt->session[i]->func_send(i); } -#endif +#endif // SEND_SHORTLIST + +#ifndef SOCKET_EPOLL + // Select based Event Dispatcher: // can timeout until the next tick timeout.tv_sec = next/1000; @@ -848,6 +972,20 @@ int do_sockets(int next) } return 0; // interrupted by a signal, just loop and try again } +#else // SOCKET_EPOLL + // Epoll based Event Dispatcher + + ret = epoll_wait(epfd, epevents, epoll_maxevents, next); + if(ret == SOCKET_ERROR) + { + if( sErrno != S_EINTR ) + { + ShowFatalError("do_sockets: epoll_wait() failed, %s!\n", error_msg()); + exit(EXIT_FAILURE); + } + return 0; // interrupted by a signal, just loop and try again + } +#endif // SOCKET_EPOLL sockt->last_tick = time(NULL); @@ -859,7 +997,33 @@ int do_sockets(int next) if( sockt->session[fd] ) sockt->session[fd]->func_recv(fd); } -#else +#elif defined(SOCKET_EPOLL) + // epoll based selection + + for( i = 0; i < ret; i++ ) + { + struct epoll_event *it = &epevents[i]; + struct socket_data *sock = sockt->session[ it->data.fd ]; + + if(!sock) + continue; + + if ((it->events & EPOLLERR) || + (it->events & EPOLLHUP) || + (!(it->events & EPOLLIN))) + { + // Got Error on this connection + sockt->eof( it->data.fd ); + + } else if (it->events & EPOLLIN) { + // data wainting + sock->func_recv( it->data.fd ); + + } + + } + +#else // defined(SOCKET_EPOLL) // otherwise assume that the fd_set is a bit-array and enumerate it in a standard way for( i = 1; ret && i < sockt->fd_max; ++i ) { @@ -869,12 +1033,12 @@ int do_sockets(int next) --ret; } } -#endif +#endif // defined(SOCKET_EPOLL) // POSTSEND Send remaining data and handle eof sessions. #ifdef SEND_SHORTLIST send_shortlist_do_sends(); -#else +#else // SEND_SHORTLIST for (i = 1; i < sockt->fd_max; i++) { if(!sockt->session[i]) @@ -888,7 +1052,7 @@ int do_sockets(int next) sockt->session[i]->func_parse(i); //This should close the session immediately. } } -#endif +#endif // SEND_SHORTLIST // parse input data on each socket for(i = 1; i < sockt->fd_max; i++) @@ -906,10 +1070,6 @@ int do_sockets(int next) } } -#ifdef __clang_analyzer__ - // Let Clang's static analyzer know this never happens (it thinks it might because of a NULL check in session_is_valid) - if (!sockt->session[i]) continue; -#endif // __clang_analyzer__ sockt->session[i]->func_parse(i); if(!sockt->session[i]) @@ -931,34 +1091,33 @@ int do_sockets(int next) sprintf(buf, "In: %.03f kB/s (%.03f kB/s, Q: %.03f kB) | Out: %.03f kB/s (%.03f kB/s, Q: %.03f kB) | RAM: %.03f MB", socket_data_i/1024., socket_data_ci/1024., socket_data_qi/1024., socket_data_o/1024., socket_data_co/1024., socket_data_qo/1024., iMalloc->usage()/1024.); #ifdef _WIN32 SetConsoleTitle(buf); -#else +#else // _WIN32 ShowMessage("\033[s\033[1;1H\033[2K%s\033[u", buf); -#endif +#endif // _WIN32 socket_data_last_tick = sockt->last_tick; socket_data_i = socket_data_ci = 0; socket_data_o = socket_data_co = 0; } -#endif +#endif // SHOW_SERVER_STATS return 0; } -////////////////////////////// -#ifndef MINICORE -////////////////////////////// // IP rules and DDoS protection -typedef struct connect_history { +struct connect_history { uint32 ip; int64 tick; int count; unsigned ddos : 1; -} ConnectHistory; +}; -typedef struct access_control { +struct access_control { uint32 ip; uint32 mask; -} AccessControl; +}; + +VECTOR_STRUCT_DECL(access_control_list, struct access_control); enum aco { ACO_DENY_ALLOW, @@ -966,16 +1125,14 @@ enum aco { ACO_MUTUAL_FAILURE }; -static AccessControl* access_allow = NULL; -static AccessControl* access_deny = NULL; +static struct access_control_list access_allow; +static struct access_control_list access_deny; static int access_order = ACO_DENY_ALLOW; -static int access_allownum = 0; -static int access_denynum = 0; static int access_debug = 0; static int ddos_count = 10; static int ddos_interval = 3*1000; static int ddos_autoreset = 10*60*1000; -struct DBMap *connect_history = NULL; +static struct DBMap *connect_history = NULL; static int connect_check_(uint32 ip); @@ -995,33 +1152,35 @@ static int connect_check(uint32 ip) /// 1 or 2 : Connection Accepted static int connect_check_(uint32 ip) { - ConnectHistory* hist = NULL; + struct connect_history *hist = NULL; int i; int is_allowip = 0; int is_denyip = 0; int connect_ok = 0; // Search the allow list - for( i=0; i < access_allownum; ++i ){ - if (SUBNET_MATCH(ip, access_allow[i].ip, access_allow[i].mask)) { - if( access_debug ){ + for (i = 0; i < VECTOR_LENGTH(access_allow); ++i) { + struct access_control *entry = &VECTOR_INDEX(access_allow, i); + if (SUBNET_MATCH(ip, entry->ip, entry->mask)) { + if (access_debug) { ShowInfo("connect_check: Found match from allow list:%u.%u.%u.%u IP:%u.%u.%u.%u Mask:%u.%u.%u.%u\n", CONVIP(ip), - CONVIP(access_allow[i].ip), - CONVIP(access_allow[i].mask)); + CONVIP(entry->ip), + CONVIP(entry->mask)); } is_allowip = 1; break; } } // Search the deny list - for( i=0; i < access_denynum; ++i ){ - if (SUBNET_MATCH(ip, access_deny[i].ip, access_deny[i].mask)) { - if( access_debug ){ + for (i = 0; i < VECTOR_LENGTH(access_deny); ++i) { + struct access_control *entry = &VECTOR_INDEX(access_deny, i); + if (SUBNET_MATCH(ip, entry->ip, entry->mask)) { + if (access_debug) { ShowInfo("connect_check: Found match from deny list:%u.%u.%u.%u IP:%u.%u.%u.%u Mask:%u.%u.%u.%u\n", CONVIP(ip), - CONVIP(access_deny[i].ip), - CONVIP(access_deny[i].mask)); + CONVIP(entry->ip), + CONVIP(entry->mask)); } is_denyip = 1; break; @@ -1076,7 +1235,7 @@ static int connect_check_(uint32 ip) } } // IP not found, add to history - CREATE(hist, ConnectHistory, 1); + CREATE(hist, struct connect_history, 1); hist->ip = ip; hist->tick = timer->gettick(); uidb_put(connect_history, ip, hist); @@ -1085,10 +1244,11 @@ static int connect_check_(uint32 ip) /// Timer function. /// Deletes old connection history records. -static int connect_check_clear(int tid, int64 tick, int id, intptr_t data) { +static int connect_check_clear(int tid, int64 tick, int id, intptr_t data) +{ int clear = 0; int list = 0; - ConnectHistory *hist = NULL; + struct connect_history *hist = NULL; struct DBIterator *iter; if( !db_size(connect_history) ) @@ -1116,11 +1276,14 @@ static int connect_check_clear(int tid, int64 tick, int id, intptr_t data) { /// Parses the ip address and mask and puts it into acc. /// Returns 1 is successful, 0 otherwise. -int access_ipmask(const char* str, AccessControl* acc) +static int access_ipmask(const char *str, struct access_control *acc) { uint32 ip; uint32 mask; + nullpo_ret(str); + nullpo_ret(acc); + if( strcmp(str,"all") == 0 ) { ip = 0; mask = 0; @@ -1159,87 +1322,209 @@ int access_ipmask(const char* str, AccessControl* acc) acc->mask = mask; return 1; } -////////////////////////////// -#endif -////////////////////////////// -int socket_config_read(const char* cfgName) +/** + * Adds an entry to the access list. + * + * @param setting The setting to read from. + * @param list_name The list name (used in error messages). + * @param access_list The access list to edit. + * + * @retval false in case of failure + */ +static bool access_list_add(struct config_setting_t *setting, const char *list_name, struct access_control_list *access_list) { - char line[1024],w1[1024],w2[1024]; - FILE *fp; + const char *temp = NULL; + int i, setting_length; - fp = fopen(cfgName, "r"); - if(fp == NULL) { - ShowError("File not found: %s\n", cfgName); - return 1; - } + nullpo_retr(false, setting); + nullpo_retr(false, list_name); + nullpo_retr(false, access_list); + + if ((setting_length = libconfig->setting_length(setting)) <= 0) + return false; - while (fgets(line, sizeof(line), fp)) { - if(line[0] == '/' && line[1] == '/') + VECTOR_ENSURE(*access_list, setting_length, 1); + for (i = 0; i < setting_length; i++) { + struct access_control acc; + if ((temp = libconfig->setting_get_string_elem(setting, i)) == NULL) { continue; - if (sscanf(line, "%1023[^:]: %1023[^\r\n]", w1, w2) != 2) + } + + if (!access_ipmask(temp, &acc)) { + ShowError("access_list_add: Invalid ip or ip range %s '%d'!\n", list_name, i); continue; + } + VECTOR_PUSH(*access_list, acc); + } + + return true; +} + +/** + * Reads 'socket_configuration/ip_rules' and initializes required variables. + * + * @param filename Path to configuration file (used in error and warning messages). + * @param config The current config being parsed. + * @param imported Whether the current config is imported from another file. + * + * @retval false in case of error. + */ +static bool socket_config_read_iprules(const char *filename, struct config_t *config, bool imported) +{ + struct config_setting_t *setting = NULL; + const char *temp = NULL; - if (!strcmpi(w1, "stall_time")) { - sockt->stall_time = atoi(w2); - if( sockt->stall_time < 3 ) - sockt->stall_time = 3;/* a minimum is required to refrain it from killing itself */ + nullpo_retr(false, filename); + nullpo_retr(false, config); + + if ((setting = libconfig->lookup(config, "socket_configuration/ip_rules")) == NULL) { + if (imported) + return true; + ShowError("socket_config_read: socket_configuration/ip_rules was not found in %s!\n", filename); + return false; + } + libconfig->setting_lookup_bool(setting, "enable", &ip_rules); + + if (!ip_rules) + return true; + + if (libconfig->setting_lookup_string(setting, "order", &temp) == CONFIG_TRUE) { + if (strcmpi(temp, "deny,allow" ) == 0) { + access_order = ACO_DENY_ALLOW; + } else if (strcmpi(temp, "allow, deny") == 0) { + access_order = ACO_ALLOW_DENY; + } else if (strcmpi(temp, "mutual-failure") == 0) { + access_order = ACO_MUTUAL_FAILURE; + } else { + ShowWarning("socket_config_read: invalid value '%s' for socket_configuration/ip_rules/order.\n", temp); } -#ifndef MINICORE - else if (!strcmpi(w1, "enable_ip_rules")) { - ip_rules = config_switch(w2); - } else if (!strcmpi(w1, "order")) { - if (!strcmpi(w2, "deny,allow")) - access_order = ACO_DENY_ALLOW; - else if (!strcmpi(w2, "allow,deny")) - access_order = ACO_ALLOW_DENY; - else if (!strcmpi(w2, "mutual-failure")) - access_order = ACO_MUTUAL_FAILURE; - } else if (!strcmpi(w1, "allow")) { - RECREATE(access_allow, AccessControl, access_allownum+1); - if (access_ipmask(w2, &access_allow[access_allownum])) - ++access_allownum; - else - ShowError("socket_config_read: Invalid ip or ip range '%s'!\n", line); - } else if (!strcmpi(w1, "deny")) { - RECREATE(access_deny, AccessControl, access_denynum+1); - if (access_ipmask(w2, &access_deny[access_denynum])) - ++access_denynum; - else - ShowError("socket_config_read: Invalid ip or ip range '%s'!\n", line); + } + + if ((setting = libconfig->lookup(config, "socket_configuration/ip_rules/allow_list")) == NULL) { + if (!imported) + ShowError("socket_config_read: socket_configuration/ip_rules/allow_list was not found in %s!\n", filename); + } else { + access_list_add(setting, "allow_list", &access_allow); + } + + if ((setting = libconfig->lookup(config, "socket_configuration/ip_rules/deny_list")) == NULL) { + if (!imported) + ShowError("socket_config_read: socket_configuration/ip_rules/deny_list was not found in %s!\n", filename); + } else { + access_list_add(setting, "deny_list", &access_deny); + } + + return true; +} + +/** + * Reads 'socket_configuration/ddos' and initializes required variables. + * + * @param filename Path to configuration file (used in error and warning messages). + * @param config The current config being parsed. + * @param imported Whether the current config is imported from another file. + * + * @retval false in case of error. + */ +static bool socket_config_read_ddos(const char *filename, struct config_t *config, bool imported) +{ + struct config_setting_t *setting = NULL; + + nullpo_retr(false, filename); + nullpo_retr(false, config); + + if ((setting = libconfig->lookup(config, "socket_configuration/ddos")) == NULL) { + if (imported) + return true; + ShowError("socket_config_read: socket_configuration/ddos was not found in %s!\n", filename); + return false; + } + + libconfig->setting_lookup_int(setting, "interval", &ddos_interval); + libconfig->setting_lookup_int(setting, "count", &ddos_count); + libconfig->setting_lookup_int(setting, "autoreset", &ddos_autoreset); + + return true; +} + +/** + * Reads 'socket_configuration' and initializes required variables. + * + * @param filename Path to configuration file. + * @param imported Whether the current config is imported from another file. + * + * @retval false in case of error. + */ +static bool socket_config_read(const char *filename, bool imported) +{ + struct config_t config; + struct config_setting_t *setting = NULL; + const char *import; + int i32 = 0; + bool retval = true; + + nullpo_retr(false, filename); + + if (!libconfig->load_file(&config, filename)) + return false; + + if ((setting = libconfig->lookup(&config, "socket_configuration")) == NULL) { + libconfig->destroy(&config); + if (imported) + return true; + ShowError("socket_config_read: socket_configuration was not found in %s!\n", filename); + return false; + } + + if (libconfig->setting_lookup_int(setting, "stall_time", &i32) == CONFIG_TRUE) { + if (i32 < 3) + i32 = 3; /* a minimum is required in order to refrain from killing itself */ + sockt->stall_time = i32; + } + +#ifdef SOCKET_EPOLL + if (libconfig->setting_lookup_int(setting, "epoll_maxevents", &i32) == CONFIG_TRUE) { + if (i32 < 16) + i32 = 16; // minimum that seems to be useful + epoll_maxevents = i32; + } +#endif // SOCKET_EPOLL + + { + uint32 ui32 = 0; + libconfig->setting_lookup_bool(setting, "debug", &access_debug); + if (libconfig->setting_lookup_uint32(setting, "socket_max_client_packet", &ui32) == CONFIG_TRUE) { + socket_max_client_packet = ui32; } - else if (!strcmpi(w1,"ddos_interval")) - ddos_interval = atoi(w2); - else if (!strcmpi(w1,"ddos_count")) - ddos_count = atoi(w2); - else if (!strcmpi(w1,"ddos_autoreset")) - ddos_autoreset = atoi(w2); - else if (!strcmpi(w1,"debug")) - access_debug = config_switch(w2); - else if (!strcmpi(w1,"socket_max_client_packet")) - socket_max_client_packet = strtoul(w2, NULL, 0); -#endif - else if (!strcmpi(w1, "import")) - socket_config_read(w2); - else - ShowWarning("Unknown setting '%s' in file %s\n", w1, cfgName); - } - - fclose(fp); - return 0; + } + + if (!socket_config_read_iprules(filename, &config, imported)) + retval = false; + if (!socket_config_read_ddos(filename, &config, imported)) + retval = false; + + // import should overwrite any previous configuration, so it should be called last + if (libconfig->lookup_string(&config, "import", &import) == CONFIG_TRUE) { + if (strcmp(import, filename) == 0 || strcmp(import, sockt->SOCKET_CONF_FILENAME) == 0) { + ShowWarning("socket_config_read: Loop detected! Skipping 'import'...\n"); + } else { + if (!socket_config_read(import, true)) + retval = false; + } + } + + libconfig->destroy(&config); + return retval; } -void socket_final(void) +static void socket_final(void) { int i; -#ifndef MINICORE if( connect_history ) db_destroy(connect_history); - if( access_allow ) - aFree(access_allow); - if( access_deny ) - aFree(access_deny); -#endif + VECTOR_CLEAR(access_allow); + VECTOR_CLEAR(access_deny); for( i = 1; i < sockt->fd_max; i++ ) if(sockt->session[i]) @@ -1255,16 +1540,38 @@ void socket_final(void) VECTOR_CLEAR(sockt->lan_subnets); VECTOR_CLEAR(sockt->allowed_ips); VECTOR_CLEAR(sockt->trusted_ips); + +#ifdef SOCKET_EPOLL + if(epfd != SOCKET_ERROR){ + close(epfd); + epfd = SOCKET_ERROR; + } + if(epevents != NULL){ + aFree(epevents); + epevents = NULL; + } +#endif // SOCKET_EPOLL + } /// Closes a socket. -void socket_close(int fd) +static void socket_close(int fd) { if( fd <= 0 ||fd >= FD_SETSIZE ) return;// invalid sockt->flush(fd); // Try to send what's left (although it might not succeed since it's a nonblocking socket) + +#ifndef SOCKET_EPOLL + // Select based Event Dispatcher sFD_CLR(fd, &readfds);// this needs to be done before closing the socket +#else // SOCKET_EPOLL + // Epoll based Event Dispatcher + epevent.data.fd = fd; + epevent.events = EPOLLIN; + epoll_ctl(epfd, EPOLL_CTL_DEL, fd, &epevent); // removing the socket from epoll when it's being closed is not required but recommended +#endif // SOCKET_EPOLL + sShutdown(fd, SHUT_RDWR); // Disallow further reads/writes sClose(fd); // We don't really care if these closing functions return an error, we are just shutting down and not reusing this socket. if (sockt->session[fd]) delete_session(fd); @@ -1272,7 +1579,7 @@ void socket_close(int fd) /// Retrieve local ips in host byte order. /// Uses loopback is no address is found. -int socket_getips(uint32* ips, int max) +static int socket_getips(uint32 *ips, int max) { int num = 0; @@ -1353,9 +1660,8 @@ int socket_getips(uint32* ips, int max) return num; } -void socket_init(void) +static void socket_init(void) { - char *SOCKET_CONF_FILENAME = "conf/packet.conf"; uint64 rlim_cur = FD_SETSIZE; #ifdef WIN32 @@ -1401,20 +1707,42 @@ void socket_init(void) } } } -#endif +#endif // defined(HAVE_SETRLIMIT) && !defined(CYGWIN) + + VECTOR_INIT(access_allow); + VECTOR_INIT(access_deny); // Get initial local ips sockt->naddr_ = sockt->getips(sockt->addr_,16); + socket_config_read(sockt->SOCKET_CONF_FILENAME, false); + +#ifndef SOCKET_EPOLL + // Select based Event Dispatcher: sFD_ZERO(&readfds); + ShowInfo("Server uses '" CL_WHITE "select" CL_RESET "' as event dispatcher\n"); + +#else // SOCKET_EPOLL + // Epoll based Event Dispatcher: + epfd = epoll_create(FD_SETSIZE); // 2.6.8 or newer ignores the expected socket amount argument + if(epfd == SOCKET_ERROR){ + ShowError("Failed to Create Epoll Event Dispatcher: %s\n", error_msg()); + exit(EXIT_FAILURE); + } + + memset(&epevent, 0x00, sizeof(struct epoll_event)); + epevents = aCalloc(epoll_maxevents, sizeof(struct epoll_event)); + + ShowInfo("Server uses '" CL_WHITE "epoll" CL_RESET "' with up to " CL_WHITE "%d" CL_RESET " events per cycle as event dispatcher\n", epoll_maxevents); + +#endif // SOCKET_EPOLL + #if defined(SEND_SHORTLIST) memset(send_shortlist_set, 0, sizeof(send_shortlist_set)); -#endif +#endif // defined(SEND_SHORTLIST) CREATE(sockt->session, struct socket_data *, FD_SETSIZE); - socket_config_read(SOCKET_CONF_FILENAME); - // initialize last send-receive tick sockt->last_tick = time(NULL); @@ -1422,30 +1750,30 @@ void socket_init(void) // should hold enough buffer (it is a vacuum so to speak) as it is never flushed. [Skotlex] create_session(0, null_recv, null_send, null_parse); -#ifndef MINICORE // Delete old connection history every 5 minutes connect_history = uidb_alloc(DB_OPT_RELEASE_DATA); timer->add_func_list(connect_check_clear, "connect_check_clear"); timer->add_interval(timer->gettick()+1000, connect_check_clear, 0, 0, 5*60*1000); -#endif ShowInfo("Server supports up to '"CL_WHITE"%"PRIu64""CL_RESET"' concurrent connections.\n", rlim_cur); } -bool session_is_valid(int fd) +static bool session_is_valid(int fd) { return ( fd > 0 && fd < FD_SETSIZE && sockt->session[fd] != NULL ); } -bool session_is_active(int fd) +static bool session_is_active(int fd) { return ( sockt->session_is_valid(fd) && !sockt->session[fd]->flag.eof ); } // Resolves hostname into a numeric ip. -uint32 host2ip(const char* hostname) +static uint32 host2ip(const char *hostname) { - struct hostent* h = gethostbyname(hostname); + struct hostent* h; + nullpo_ret(hostname); + h = gethostbyname(hostname); return (h != NULL) ? ntohl(*(uint32*)h->h_addr) : 0; } @@ -1457,7 +1785,7 @@ uint32 host2ip(const char* hostname) * * @return A pointer to the output string. */ -const char *ip2str(uint32 ip, char *ip_str) +static const char *ip2str(uint32 ip, char *ip_str) { struct in_addr addr; addr.s_addr = htonl(ip); @@ -1465,20 +1793,21 @@ const char *ip2str(uint32 ip, char *ip_str) } // Converts a dot-formatted ip string into a numeric ip. -uint32 str2ip(const char* ip_str) +static uint32 str2ip(const char *ip_str) { return ntohl(inet_addr(ip_str)); } // Reorders bytes from network to little endian (Windows). // Necessary for sending port numbers to the RO client until Gravity notices that they forgot ntohs() calls. -uint16 ntows(uint16 netshort) +static uint16 ntows(uint16 netshort) { return ((netshort & 0xFF) << 8) | ((netshort & 0xFF00) >> 8); } /* [Ind/Hercules] - socket_datasync */ -void socket_datasync(int fd, bool send) { +static void socket_datasync(int fd, bool send) +{ struct { unsigned int length;/* short is not enough for some */ } data_list[] = { @@ -1508,6 +1837,11 @@ void socket_datasync(int fd, bool send) { { sizeof(struct guild_castle) }, { sizeof(struct fame_list) }, { PACKETVER }, + { PACKETVER_MAIN_NUM }, + { PACKETVER_RE_NUM }, + { PACKETVER_ZERO_NUM }, + { PACKETVER_AD_NUM }, + { PACKETVER_SAK_NUM }, }; unsigned short i; unsigned int alen = ARRAYLENGTH(data_list); @@ -1522,7 +1856,7 @@ void socket_datasync(int fd, bool send) { WFIFOL(fd, 4 + ( i * 4 ) ) = data_list[i].length; } - WFIFOSET(fd, p_len); + WFIFOSET2(fd, p_len); } else { for( i = 0; i < alen; i++ ) { if( RFIFOL(fd, 4 + (i * 4) ) != data_list[i].length ) { @@ -1531,7 +1865,7 @@ void socket_datasync(int fd, bool send) { WFIFOW(fd, 0) = 0x2b0a; WFIFOW(fd, 2) = 8; WFIFOL(fd, 4) = 0; - WFIFOSET(fd, 8); + WFIFOSET2(fd, 8); sockt->flush(fd); /* shut down */ ShowFatalError("Servers are out of sync! recompile from scratch (%d)\n",i); @@ -1544,7 +1878,7 @@ void socket_datasync(int fd, bool send) { #ifdef SEND_SHORTLIST // Add a fd to the shortlist so that it'll be recognized as a fd that needs // sending or eof handling. -void send_shortlist_add_fd(int fd) +static void send_shortlist_add_fd(int fd) { int i; int bit; @@ -1571,7 +1905,7 @@ void send_shortlist_add_fd(int fd) } // Do pending network sends and eof handling from the shortlist. -void send_shortlist_do_sends(void) +static void send_shortlist_do_sends(void) { int i; @@ -1617,7 +1951,7 @@ void send_shortlist_do_sends(void) } } } -#endif +#endif // SEND_SHORTLIST /** * Checks whether the given IP comes from LAN or WAN. @@ -1627,7 +1961,7 @@ void send_shortlist_do_sends(void) * @retval 0 if it is a WAN IP. * @return the appropriate LAN server address to send, if it is a LAN IP. */ -uint32 socket_lan_subnet_check(uint32 ip, struct s_subnet *info) +static uint32 socket_lan_subnet_check(uint32 ip, struct s_subnet *info) { int i; ARR_FIND(0, VECTOR_LENGTH(sockt->lan_subnets), i, SUBNET_MATCH(ip, VECTOR_INDEX(sockt->lan_subnets, i).ip, VECTOR_INDEX(sockt->lan_subnets, i).mask)); @@ -1651,7 +1985,7 @@ uint32 socket_lan_subnet_check(uint32 ip, struct s_subnet *info) * @retval true if we allow server connections from the given IP. * @retval false otherwise. */ -bool socket_allowed_ip_check(uint32 ip) +static bool socket_allowed_ip_check(uint32 ip) { int i; ARR_FIND(0, VECTOR_LENGTH(sockt->allowed_ips), i, SUBNET_MATCH(ip, VECTOR_INDEX(sockt->allowed_ips, i).ip, VECTOR_INDEX(sockt->allowed_ips, i).mask)); @@ -1667,7 +2001,7 @@ bool socket_allowed_ip_check(uint32 ip) * @retval true if we trust the given IP. * @retval false otherwise. */ -bool socket_trusted_ip_check(uint32 ip) +static bool socket_trusted_ip_check(uint32 ip) { int i; ARR_FIND(0, VECTOR_LENGTH(sockt->trusted_ips), i, SUBNET_MATCH(ip, VECTOR_INDEX(sockt->trusted_ips, i).ip, VECTOR_INDEX(sockt->trusted_ips, i).mask)); @@ -1687,7 +2021,7 @@ bool socket_trusted_ip_check(uint32 ip) * @param[in] groupname Current group name, for output/logging reasons. * @return The amount of entries read, zero in case of errors. */ -int socket_net_config_read_sub(struct config_setting_t *t, struct s_subnet_vector *list, const char *filename, const char *groupname) +static int socket_net_config_read_sub(struct config_setting_t *t, struct s_subnet_vector *list, const char *filename, const char *groupname) { int i, len; char ipbuf[64], maskbuf[64]; @@ -1721,7 +2055,7 @@ int socket_net_config_read_sub(struct config_setting_t *t, struct s_subnet_vecto * * @param filename The filename to read from. */ -void socket_net_config_read(const char *filename) +static void socket_net_config_read(const char *filename) { struct config_t network_config; int i; @@ -1752,17 +2086,66 @@ void socket_net_config_read(const char *filename) ShowError("No allowed server IP ranges configured. This server won't be able to accept connections from any char servers.\n"); } ARR_FIND(0, VECTOR_LENGTH(sockt->allowed_ips), i, SUBNET_MATCH(0, VECTOR_INDEX(sockt->allowed_ips, i).ip, VECTOR_INDEX(sockt->allowed_ips, i).mask)); +#ifndef BUILDBOT if (i != VECTOR_LENGTH(sockt->allowed_ips)) { ShowWarning("Using a wildcard IP range in the allowed server IPs is NOT RECOMMENDED.\n"); ShowNotice("Please edit your '%s' allowed list to fit your network configuration.\n", filename); } +#endif // BUILDBOT libconfig->destroy(&network_config); return; } -void socket_defaults(void) { +static void socket_validateWfifo(int fd, size_t len) +{ + if (len < 2) { + ShowError("Sent packet with size smaller than 2\n"); + Assert_retv(0); + return; + } + const uint32 cmd = (uint32)WFIFOW(fd, 0); + const uint32 last_head_size = sockt->session[fd]->last_head_size; + sockt->session[fd]->last_head_size = 0; + + if (cmd < MIN_PACKET_DB || cmd > MAX_PACKET_DB) { + ShowError("Sent wrong packet id: 0x%04X\n", cmd); + Assert_retv(0); + return; + } + if (len > 65535) { + ShowError("Sent packet with size bigger than 65535\n"); + Assert_retv(0); + return; + } + int packet_len = packets->db[cmd]; + const int len2 = (int)len; + if (packet_len == -1) { + if (len2 < 4) { + ShowError("Sent packet with size smaller than 4\n"); + Assert_retv(0); + return; + } + packet_len = WFIFOW(fd, 2); + if (packet_len != len2) { + ShowError("Sent packet 0x%04X with dynamic size %d, but must be size %d\n", cmd, len2, packet_len); + Assert_retv(0); + } + } else if (packet_len != len2) { + ShowError("Sent packet 0x%04X with size %d, but must be size %d\n", cmd, len2, packet_len); + Assert_retv(0); + } + if (last_head_size < (uint32)packet_len) { + ShowError("Reserved too small packet buffer for packet 0x%04X with size %u, but must be size %d\n", cmd, last_head_size, packet_len); + Assert_retv(0); + } +} + +void socket_defaults(void) +{ sockt = &sockt_s; + sockt->SOCKET_CONF_FILENAME = "conf/common/socket.conf"; + sockt->fd_max = 0; /* */ sockt->stall_time = 60; @@ -1770,6 +2153,7 @@ void socket_defaults(void) { /* */ memset(&sockt->addr_, 0, sizeof(sockt->addr_)); sockt->naddr_ = 0; + sockt->validate = false; /* */ VECTOR_INIT(sockt->lan_subnets); VECTOR_INIT(sockt->allowed_ips); @@ -1787,6 +2171,7 @@ void socket_defaults(void) { sockt->realloc_fifo = realloc_fifo; sockt->realloc_writefifo = realloc_writefifo; sockt->wfifoset = wfifoset; + sockt->wfifohead = wfifohead; sockt->rfifoskip = rfifoskip; sockt->close = socket_close; /* */ @@ -1795,6 +2180,7 @@ void socket_defaults(void) { /* */ sockt->flush = flush_fifo; sockt->flush_fifos = flush_fifos; + sockt->connect_client = connect_client; sockt->set_nonblocking = set_nonblocking; sockt->set_defaultparse = set_defaultparse; sockt->host2ip = host2ip; @@ -1809,4 +2195,5 @@ void socket_defaults(void) { sockt->trusted_ip_check = socket_trusted_ip_check; sockt->net_config_read_sub = socket_net_config_read_sub; sockt->net_config_read = socket_net_config_read; + sockt->validateWfifo = socket_validateWfifo; } |