summaryrefslogtreecommitdiff
path: root/src/common/socket.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/common/socket.c')
-rw-r--r--src/common/socket.c851
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;
}