From 9ff2f48d7065c8b67001a03878a93ca9b3cca244 Mon Sep 17 00:00:00 2001 From: Zach Toogood Date: Sat, 15 Mar 2025 09:49:16 +0000 Subject: [PATCH] Core: Remove unused TCP socket handling --- src/common/kernel.cpp | 3 - src/common/kernel.h | 25 +- src/common/socket.cpp | 1117 ++--------------------------------------- src/common/socket.h | 169 +------ src/map/map.cpp | 13 +- 5 files changed, 72 insertions(+), 1255 deletions(-) diff --git a/src/common/kernel.cpp b/src/common/kernel.cpp index d0c357e13ee..2f1e1aac24a 100644 --- a/src/common/kernel.cpp +++ b/src/common/kernel.cpp @@ -90,8 +90,6 @@ void operator delete[](void* ptr, std::size_t count) noexcept std::atomic gRunFlag = true; -std::array, MAX_FD> sessions; - // This must be manually created std::unique_ptr gConsoleService; @@ -234,7 +232,6 @@ int main(int argc, char** argv) #endif // _WIN32 log_init(argc, argv); - set_socket_type(); signals_init(); timer_init(); diff --git a/src/common/kernel.h b/src/common/kernel.h index 4c7967476f7..75951e57846 100644 --- a/src/common/kernel.h +++ b/src/common/kernel.h @@ -19,21 +19,26 @@ =========================================================================== */ -#ifndef _KERNEL_H_ -#define _KERNEL_H_ +#pragma once #include "cbasetypes.h" #include "console_service.h" #include "settings.h" +#include "socket.h" -extern std::atomic gRunFlag; - -extern void log_init(int, char**); -extern int32 do_init(int32, char**); -extern void set_socket_type(void); -extern void do_abort(void); -extern void do_final(int); +// +// Global variables +// +extern std::atomic gRunFlag; extern std::unique_ptr gConsoleService; -#endif // _KERNEL_H_ +// +// Functions +// + +extern void log_init(int, char**); +extern auto do_init(int32, char**) -> int32; +extern auto do_sockets(fd_set*, duration) -> int32; +extern void do_abort(void); +extern void do_final(int); diff --git a/src/common/socket.cpp b/src/common/socket.cpp index 71474f317f8..e7a7e44e86a 100644 --- a/src/common/socket.cpp +++ b/src/common/socket.cpp @@ -61,10 +61,7 @@ #endif #endif -///////////////////////////////////////////////////////////////////// -#if defined(WIN32) -///////////////////////////////////////////////////////////////////// -// windows portability layer +#if defined(WIN32) // windows portability layer typedef int socklen_t; @@ -157,85 +154,81 @@ int sSocket(int af, int type, int protocol) return -1; // error return sock2newfd(s); } -/////////////////////////////////////////////////////////////////////// #else // *nix sys -/////////////////////////////////////////////////////////////////////// - -///////////////////////////////////////////////////////////////////// #endif -///////////////////////////////////////////////////////////////////// - -/* - * - * COMMON LEVEL - * - */ - -socket_type SOCKET_TYPE; fd_set readfds; int32 fd_max; time_t last_tick; time_t tick_time; -time_t stall_time = 60; -int32 makeConnection(uint32 ip, uint16 port, int32 type) +// hostname/ip conversion functions +std::string ip2str(uint32 ip) { - TracyZoneScoped; + uint32 reversed_ip = htonl(ip); + char address[INET_ADDRSTRLEN]; + inet_ntop(AF_INET, &reversed_ip, address, INET_ADDRSTRLEN); + + // This is internal, so we can trust it. + return fmt::format("{}", asStringFromUntrustedSource(address)); +} + +uint32 str2ip(const char* ip_str) +{ + uint32 ip = 0; + inet_pton(AF_INET, ip_str, &ip); + + return ntohl(ip); +} - sockaddr_in remote_address{}; +void set_nonblocking(int fd, unsigned long yes) +{ + TracyZoneScoped; + // 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. + if (sIoctl(fd, FIONBIO, &yes) != 0) + { + ShowError("set_nonblocking: Failed to set socket #%d to non-blocking mode (code %d) - Please report this!!!", fd, sErrno); + } +} - int32 fd = 0; - int32 result = 0; +int32 makeBind_udp(uint32 ip, uint16 port) +{ + TracyZoneScoped; + sockaddr_in server_address{}; + int fd = 0; + int result = 0; - fd = sSocket(AF_INET, type, 0); + fd = sSocket(AF_INET, SOCK_DGRAM, 0); if (fd == -1) { - ShowError("make_connection: socket creation failed (port %d, code %d)!", port, sErrno); - return -1; + ShowError("make_listen_bind: socket creation failed (port %d, code %d)!", port, sErrno); + do_final(EXIT_FAILURE); } if (fd == 0) { // reserved - ShowError("make_connection: Socket #0 is reserved - Please report this!!!"); + ShowError("make_listen_bind: Socket #0 is reserved - Please report this!!!"); sClose(fd); return -1; } if (fd >= MAX_FD) { // socket number too big - ShowError("make_connection: New socket #%d is greater than can we handle! Increase the value of MAX_FD (currently %d) for your OS to fix this!", + ShowError("make_listen_bind: New socket #%d is greater than can we handle! Increase the value of MAX_FD (currently %d) for your OS to fix this!", fd, MAX_FD); sClose(fd); return -1; } - linger opt{}; - opt.l_onoff = 0; // SO_DONTLINGER - opt.l_linger = 0; // Do not care - if (sSetsockopt(fd, SOL_SOCKET, SO_LINGER, (char*)&opt, sizeof(opt))) - { - ShowWarning("setsocketopts: Unable to set SO_LINGER mode for connection #%d!", fd); - } - - remote_address.sin_family = AF_INET; - remote_address.sin_addr.s_addr = htonl(ip); - remote_address.sin_port = htons(port); - - ShowInfo(fmt::format("Connecting to {}:{}", ip2str(ip), port)); + server_address.sin_family = AF_INET; + server_address.sin_addr.s_addr = htonl(ip); + server_address.sin_port = htons(port); - result = sConnect(fd, (struct sockaddr*)(&remote_address), sizeof(struct sockaddr_in)); + result = sBind(fd, (struct sockaddr*)&server_address, sizeof(server_address)); if (result == SOCKET_ERROR) { - ShowError("make_connection: connect failed (socket #%d, port %d, code %d)!", fd, port, sErrno); - do_close(fd); - return -1; - } - // Now the socket can be made non-blocking. [Skotlex] - // set_nonblocking(fd, 1); - u_long yes = 1; - if (sIoctl(fd, FIONBIO, &yes) != 0) - { - ShowError("set_nonblocking: Failed to set socket #%d to non-blocking mode (code %d) - Please report this!!!", fd, sErrno); + ShowError("make_listen_bind: bind failed (socket #%d, port %d, code %d)!", fd, port, sErrno); + do_final(EXIT_FAILURE); } if (fd_max <= fd) @@ -243,21 +236,10 @@ int32 makeConnection(uint32 ip, uint16 port, int32 type) fd_max = fd + 1; } sFD_SET(fd, &readfds); - return fd; } -void do_close(int32 fd) -{ - TracyZoneScoped; -#ifdef __APPLE__ - sFD_CLR(fd, &readfds); // this needs to be done before closing the socket -#endif - 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. -} - -bool _vsocket_init() +void socket_init_udp() { TracyZoneScoped; #ifdef WIN32 @@ -267,12 +249,12 @@ bool _vsocket_init() if (WSAStartup(wVersionRequested, &wsaData) != 0) { ShowError("socket_init: WinSock not available!"); - return false; + return; } if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 0) { ShowError("socket_init: WinSock version mismatch (2.0 or compatible required)!"); - return false; + return; } } #elif defined(HAVE_SETRLIMIT) && !defined(CYGWIN) @@ -305,1014 +287,23 @@ bool _vsocket_init() // initialize last send-receive tick last_tick = time(nullptr); - return true; -} - -bool _vsocket_final() -{ - return true; -} - -// hostname/ip conversion functions -std::string ip2str(uint32 ip) -{ - uint32 reversed_ip = htonl(ip); - char address[INET_ADDRSTRLEN]; - inet_ntop(AF_INET, &reversed_ip, address, INET_ADDRSTRLEN); - - // This is internal, so we can trust it. - return fmt::format("{}", asStringFromUntrustedSource(address)); -} - -uint32 str2ip(const char* ip_str) -{ - uint32 ip = 0; - inet_pton(AF_INET, ip_str, &ip); - - return ntohl(ip); -} - -/*****************************************************************************/ -/* - * - * TCP LEVEL - * - */ - -int ip_rules = 1; -static int connect_check(uint32 ip); - -////////////////////////////// -// IP rules and connection limits - -typedef struct _connect_history -{ - struct _connect_history* next; - uint32 ip; - time_point tick; - int count; - unsigned ddos : 1; - _connect_history() - { - next = nullptr; - ip = 0; - count = 0; - ddos = 0; - } -} ConnectHistory; - -using AccessControl = struct _access_control -{ - uint32 ip; - uint32 mask; -}; - -enum _aco : uint8 -{ - ACO_DENY_ALLOW, - ACO_ALLOW_DENY, - ACO_MUTUAL_FAILURE -}; - -static std::vector access_allow; -static std::vector access_deny; -static int access_order = ACO_DENY_ALLOW; -static int access_debug = 0; -static bool udp_debug = false; -static bool tcp_debug = false; -//-- -static int connect_count = 10; -static duration connect_interval = 3s; -static duration connect_lockout = 10min; - -/// Connection history, an array of linked lists. -/// The array's index for any ip is ip&0xFFFF -static ConnectHistory* connect_history[0x10000]; - -static int connect_check_(uint32 ip); - -/// Verifies if the IP can connect. (with debug info) -/// @see connect_check_() -static int connect_check(uint32 ip) -{ - TracyZoneScoped; - int result = connect_check_(ip); - if (access_debug) - { - ShowInfo(fmt::format("connect_check: Connection from {} {}", ip2str(ip), result ? "allowed." : "denied!")); - } - return result; -} - -/// Verifies if the IP can connect. -/// 0 : Connection Rejected -/// 1 or 2 : Connection Accepted -static int connect_check_(uint32 ip) -{ - TracyZoneScoped; - ConnectHistory* hist = connect_history[ip & 0xFFFF]; - int is_allowip = 0; - int is_denyip = 0; - int connect_ok = 0; - - // Search the allow list - for (auto const& entry : access_allow) - { - if ((ip & entry.mask) == (entry.ip & entry.mask)) - { - if (access_debug) - { - ShowInfo( - fmt::format("connect_check: Found match from allow list:{} IP:{} Mask:{}", - ip2str(ip), - ip2str(entry.ip), - ip2str(entry.mask))); - } - is_allowip = 1; - break; - } - } - // Search the deny list - for (auto const& entry : access_deny) - { - if ((ip & entry.mask) == (entry.ip & entry.mask)) - { - if (access_debug) - { - ShowInfo( - fmt::format("connect_check: Found match from deny list:{} IP:{} Mask:{}", - ip2str(ip), - ip2str(entry.ip), - ip2str(entry.mask))); - } - is_denyip = 1; - break; - } - } - // Decide connection status - // 0 : Reject - // 1 : Accept - // 2 : Unconditional Accept (accepts even if flagged as possible DDoS) - switch (access_order) - { - case ACO_DENY_ALLOW: - default: - if (is_denyip) - { - connect_ok = 0; // Reject - } - else if (is_allowip) - { - connect_ok = 2; // Unconditional Accept - } - else - { - connect_ok = 1; // Accept - } - break; - case ACO_ALLOW_DENY: - if (is_allowip) - { - connect_ok = 2; // Unconditional Accept - } - else if (is_denyip) - { - connect_ok = 0; // Reject - } - else - { - connect_ok = 1; // Accept - } - break; - case ACO_MUTUAL_FAILURE: - if (is_allowip && !is_denyip) - { - connect_ok = 2; // Unconditional Accept - } - else - { - connect_ok = 0; // Reject - } - break; - } - - // Inspect connection history - while (hist) - { - if (ip == hist->ip) - { // IP found - if (hist->ddos) - { // flagged as possible DDoS - return (connect_ok == 2 ? 1 : 0); - } - if ((server_clock::now() - hist->tick) < connect_interval) - { // connection within connect_interval limit - hist->tick = server_clock::now(); - if (hist->count++ >= connect_count) - { // to many attempts detected - hist->ddos = 1; - ShowWarning(fmt::format("connect_check: too many connection attempts detected from {}!", ip2str(ip))); - return (connect_ok == 2 ? 1 : 0); - } - return connect_ok; - } - - // not within connect_interval, clear data - hist->tick = server_clock::now(); - hist->count = 0; - return connect_ok; - } - hist = hist->next; - } - // IP not found, add to history - hist = new ConnectHistory{}; - hist->ip = ip; - hist->tick = server_clock::now(); - hist->next = connect_history[ip & 0xFFFF]; - connect_history[ip & 0xFFFF] = hist; - return connect_ok; -} - -/// Timer function. -/// Deletes old connection history records. -static int connect_check_clear(time_point tick, CTaskMgr::CTask* PTask) -{ - TracyZoneScoped; - int clear = 0; - int list = 0; - ConnectHistory root{}; - ConnectHistory* prev_hist = nullptr; - ConnectHistory* hist = nullptr; - - for (int i = 0; i < 0x10000; ++i) - { - prev_hist = &root; - root.next = hist = connect_history[i]; - while (hist) - { - if ((!hist->ddos && (tick - hist->tick) > connect_interval * 3) || (hist->ddos && (tick - hist->tick) > connect_lockout)) - { // Remove connection history - prev_hist->next = hist->next; - destroy(hist); - hist = prev_hist->next; - clear++; - } - else - { - prev_hist = hist; - hist = hist->next; - } - list++; - } - connect_history[i] = root.next; - } - if (access_debug) - { - ShowInfo("connect_check_clear: Cleared %d of %d from IP list.", clear, list); - } - return list; -} - -/// 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) -{ - TracyZoneScoped; - uint32 ip = 0; - uint32 mask = 0; - unsigned int a[4]{}; - unsigned int m[4]{}; - int n = 0; - - if (strcmp(str, "all") == 0) - { - ip = 0; - mask = 0; - } - else - { - if (((n = sscanf(str, "%u.%u.%u.%u/%u.%u.%u.%u", a, a + 1, a + 2, a + 3, m, m + 1, m + 2, m + 3)) != 8 && // not an ip + standard mask - (n = sscanf(str, "%u.%u.%u.%u/%u", a, a + 1, a + 2, a + 3, m)) != 5 && // not an ip + bit mask - (n = sscanf(str, "%u.%u.%u.%u", a, a + 1, a + 2, a + 3)) != 4) || // not an ip - a[0] > 255 || - a[1] > 255 || a[2] > 255 || a[3] > 255 || // invalid ip - (n == 8 && (m[0] > 255 || m[1] > 255 || m[2] > 255 || m[3] > 255)) || // invalid standard mask - (n == 5 && m[0] > 32)) - { // invalid bit mask - return 0; - } - ip = (a[0] | (a[1] << 8) | (a[2] << 16) | (a[3] << 24)); - if (n == 8) - { // standard mask - mask = (a[0] | (a[1] << 8) | (a[2] << 16) | (a[3] << 24)); - } - else if (n == 5) - { // bit mask - mask = 0; - while (m[0]) - { - mask = (mask >> 1) | 0x80000000; - --m[0]; - } - } - else - { // just this ip - mask = 0xFFFFFFFF; - } - } - - ip = ntohl(ip); - - if (access_debug) - { - ShowInfo(fmt::format("access_ipmask: Loaded IP:{} mask:{}", ip2str(ip), ip2str(mask))); - } - acc->ip = ip; - acc->mask = mask; - return 1; -} - -////////////////////////////// -int recv_to_fifo(int fd) -{ - TracyZoneScoped; - int len = 0; - - if (!session_isActive(fd)) - { - return -1; - } - - auto prev_length = sessions[fd]->rdata.size(); - sessions[fd]->rdata.resize(prev_length + 0x7FF); - len = sRecv(fd, sessions[fd]->rdata.data() + prev_length, (int)(sessions[fd]->rdata.capacity() - prev_length), 0); - - if (len == SOCKET_ERROR) - { // An exception has occured - if (sErrno != S_EWOULDBLOCK) - { - set_eof(fd); - } - return 0; - } - - if (len == 0) - { // Normal connection end. - set_eof(fd); - return 0; - } - - sessions[fd]->rdata.resize(prev_length + len); - sessions[fd]->rdata_tick = last_tick; - return 0; } -int send_from_fifo(int fd) +int32 recvudp(int32 fd, void* buff, size_t nbytes, int32 flags, struct sockaddr* from, socklen_t* addrlen) { TracyZoneScoped; - int len = 0; - - if (!session_isValid(fd)) - { - return -1; - } - - if (sessions[fd]->wdata.empty()) - { - return 0; // nothing to send - } - - len = sSend(fd, sessions[fd]->wdata.data(), (int)sessions[fd]->wdata.size(), 0); - - if (len == SOCKET_ERROR) - { // An exception has occured - if (sErrno != S_EWOULDBLOCK) - { - // ShowDebug("send_from_fifo: error %d, ending connection #%d", sErrno, fd); - sessions[fd]->wdata.clear(); // Clear the send queue as we can't send anymore. [Skotlex] - set_eof(fd); - } - return 0; - } - - if (len > 0) - { - // some data could not be transferred? - // shift unsent data to the beginning of the queue - if ((size_t)len < sessions[fd]->wdata.size()) - { - sessions[fd]->wdata.erase(0, len); - } - else - { - sessions[fd]->wdata.clear(); - } - } - - return 0; -} - -/*====================================== - * 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; + return sRecvfrom(fd, (char*)buff, (int)nbytes, flags, from, addrlen); } -ParseFunc default_func_parse = null_parse; - -bool session_isValid(int fd) -{ - TracyZoneScoped; - return (fd > 0 && fd < MAX_FD && sessions[fd] != nullptr); -} -bool session_isActive(int fd) +int32 sendudp(int32 fd, void* buff, size_t nbytes, int32 flags, const struct sockaddr* from, socklen_t addrlen) { TracyZoneScoped; - return (session_isValid(fd) && !sessions[fd]->flag.eof); + return sSendto(fd, (const char*)buff, (int)nbytes, flags, from, addrlen); } -int32 makeConnection_tcp(uint32 ip, uint16 port) -{ - TracyZoneScoped; - int fd = makeConnection(ip, port, SOCK_STREAM); - if (fd > 0) - { - create_session(fd, recv_to_fifo, send_from_fifo, default_func_parse); - sessions[fd]->client_addr = ip; - } - return fd; -} -/*====================================== - * CORE : Connection functions - *--------------------------------------*/ -int connect_client(int listen_fd, sockaddr_in& client_address) +void socket_init() { TracyZoneScoped; - int fd = 0; - socklen_t len{}; - - len = sizeof(client_address); - - fd = sAccept(listen_fd, (struct sockaddr*)&client_address, &len); - if (fd == -1) - { - ShowError("connect_client: accept failed (code %d, listen_fd %d)!", sErrno, listen_fd); - return -1; - } - if (fd == 0) - { // reserved - ShowError("connect_client: Socket #0 is reserved - Please report this!!!"); - sClose(fd); - return -1; - } - if (fd >= MAX_FD) - { // socket number too big - ShowError("connect_client: New socket #%d is greater than can we handle! Increase the value of MAX_FD (currently %d) for your OS to fix this!", - fd, MAX_FD); - sClose(fd); - return -1; - } - - if (ip_rules && !connect_check(ntohl(client_address.sin_addr.s_addr))) - { - do_close(fd); - return -1; - } - - if (fd_max <= fd) - { - fd_max = fd + 1; - } -#ifdef __APPLE__ - sFD_SET(fd, &readfds); -#endif - return fd; -} - -int32 makeListenBind_tcp(const char* ip, uint16 port, RecvFunc connect_client) -{ - TracyZoneScoped; - sockaddr_in server_address{}; - int fd = 0; - int result = 0; - - fd = sSocket(AF_INET, SOCK_STREAM, 0); - - if (fd == -1) - { - ShowError("make_listen_bind: socket creation failed (port %d, code %d)!", port, sErrno); - ShowError("Is another process using this port?"); - do_final(EXIT_FAILURE); - } - - if (fd == 0) - { // reserved - ShowError("make_listen_bind: Socket #0 is reserved - Please report this!!!"); - sClose(fd); - return -1; - } - - if (fd >= MAX_FD) - { // socket number too big - ShowError("make_listen_bind: New socket #%d is greater than can we handle! Increase the value of MAX_FD (currently %d) for your OS to fix this!", - fd, MAX_FD); - sClose(fd); - return -1; - } - - server_address.sin_family = AF_INET; - inet_pton(AF_INET, ip, &server_address.sin_addr.s_addr); - server_address.sin_port = htons(port); - - // https://stackoverflow.com/questions/3229860/what-is-the-meaning-of-so-reuseaddr-setsockopt-option-linux - // Avoid hangs in TIME_WAIT state of TCP -#ifdef WIN32 - // Windows doesn't seem to have this problem, but apparently this would be the right way to explicitly mimic SO_REUSEADDR unix's behavior. - setsockopt(sock_arr[fd], SOL_SOCKET, SO_DONTLINGER, "\x00\x00\x00\x00", 4); -#else - int enable = 1; - if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(int)) < 0) - { - ShowError("setsockopt SO_REUSEADDR failed!"); - } -#endif - - result = sBind(fd, (struct sockaddr*)&server_address, sizeof(server_address)); - if (result == SOCKET_ERROR) - { - ShowError("make_listen_bind: bind failed (socket #%d, port %d, code %d)!", fd, port, sErrno); - do_final(EXIT_FAILURE); - } - result = sListen(fd, 5); - if (result == SOCKET_ERROR) - { - ShowError("make_listen_bind: listen failed (socket #%d, port %d, code %d)!", fd, port, sErrno); - do_final(EXIT_FAILURE); - } - - if (fd_max <= fd) - { - fd_max = fd + 1; - } - sFD_SET(fd, &readfds); - - create_session(fd, connect_client, null_send, null_parse); - sessions[fd]->client_addr = 0; // just listens - sessions[fd]->rdata_tick = 0; // disable timeouts on this socket - - return fd; -} - -int32 RFIFOSKIP(int32 fd, size_t len) -{ - TracyZoneScoped; - struct socket_data* s = nullptr; - - if (!session_isActive(fd)) - { - return 0; - } - - s = sessions[fd].get(); - - if (s->rdata.size() < s->rdata_pos + len) - { - ShowError("RFIFOSKIP: skipped past end of read buffer! Adjusting from %d to %d (session #%d)", len, RFIFOREST(fd), fd); - len = RFIFOREST(fd); - } - - s->rdata_pos = s->rdata_pos + len; - return 0; -} - -void do_close_tcp(int32 fd) -{ - TracyZoneScoped; - flush_fifo(fd); - do_close(fd); - if (sessions[fd]) - { - delete_session(fd); - } -} - -/// -/// -/// Get the access list object collection from the provided string. The string -/// provided is in the form of "127.0.0.1,192.168.0.0/16" where each entry is -/// separated by a comma. This will break apart all individual entries and then -/// for each entry that is validated will be pushed into our result collection, -/// otherwise an error will be displayed. -/// -/// -/// The access list that we are parsing for individual entries. -/// std::vector collection that contains all AccessControl entries. -/// -std::vector get_access_list(std::string const& access_list) -{ - // with the provided comma delimited access list, we will convert into a - // vector of string entries - std::vector result{}; - - std::stringstream ss(access_list); - while (ss.good()) - { - std::string entry; - // get string delimited by comma character - getline(ss, entry, ','); - - if (entry == "") - { - // skip - continue; - } - - // validate our entry before pushing it into our results list - AccessControl acc{}; - if (access_ipmask(entry.c_str(), &acc)) - { - result.emplace_back(acc); - } - else - { - ShowError("socket_config_read: Invalid ip or ip range '%s'!", entry); - } - } - - return result; -} - -/// -/// -/// Setting up the UDP settings, currently just has a debug flag. -/// -/// @NOTE I've added a UDP debug flag, the access debug flag is shared between -/// both UDP and TCP. Can be confusing if you believe you are setting the -/// flag and then it gets overwritten by the other setting. The new flags -/// are just not being leveraged just yet (not sure where they would go). -/// -/// -void socket_udp_setup() -{ - // debug setting - udp_debug = settings::get("network.UDP_DEBUG"); - access_debug = settings::get("network.UDP_DEBUG"); -} - -/// -/// -/// Handling the TCP setup properties for the socket. All leveraging the new -/// settings handling of LUA files. The only issue I had was related to the -/// allow and deny lists. There would need to be an extension to the get() -/// method in order to allow LUA lists { "a", "b" }. To allieviate this I've -/// implemented a get_access_list() method in order to parse a string that -/// splits entries with commas. All other settings are straight forward using -/// the settings get() method. Added all "packet_tcp.conf" settings to the -/// new "settings/default/network.lua" file. -/// -/// -void socket_tcp_setup() -{ - // debug setting (shared?) - access_debug = settings::get("network.TCP_DEBUG"); - - // sockets configuration - tcp_debug = settings::get("network.TCP_DEBUG"); - stall_time = settings::get("network.TCP_STALL_TIME"); - - // IP rules settings - ip_rules = settings::get("network.TCP_ENABLE_IP_RULES"); - - // ordering of the checks - auto ordering = settings::get("network.TCP_ORDER"); - if (ordering == "deny,allow") - { - access_order = ACO_DENY_ALLOW; - } - else if (ordering == "allow,deny") - { - access_order = ACO_ALLOW_DENY; - } - else if (ordering == "mutual-failure") - { - access_order = ACO_MUTUAL_FAILURE; - } - - // get the allow and deny list - if (access_debug) - { - ShowInfo("Loading allow access list..."); - } - auto allow_list_str = settings::get("network.TCP_ALLOW"); - access_allow = get_access_list(allow_list_str); - if (access_debug) - { - ShowInfo("Size of allow access list: %d", access_allow.size()); - } - - if (access_debug) - { - ShowInfo("Loading deny access list..."); - } - auto deny_list_str = settings::get("network.TCP_DENY"); - access_deny = get_access_list(deny_list_str); - if (access_debug) - { - ShowInfo("Size of deny access list: %d", access_deny.size()); - } - - // connection limit settings - connect_interval = std::chrono::milliseconds( - settings::get("network.TCP_CONNECT_INTERVAL")); - connect_count = settings::get("network.TCP_CONNECT_COUNT"); - connect_lockout = std::chrono::milliseconds( - settings::get("network.TCP_CONNECT_LOCKOUT")); -} - -void socket_init_tcp() -{ - TracyZoneScoped; - if (!_vsocket_init()) - { - return; - } - - // setup our socket - socket_tcp_setup(); - - // sessions[0] is now currently used for disconnected sessions of the map - // server, and as such, 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); - - // Delete old connection history every 5 minutes - memset(connect_history, 0, sizeof(connect_history)); - CTaskMgr::getInstance()->AddTask( - "connect_check_clear", - server_clock::now() + 1s, - nullptr, - CTaskMgr::TASK_INTERVAL, - 5min, - connect_check_clear); -} - -void socket_final_tcp() -{ - TracyZoneScoped; - if (!_vsocket_final()) - { - return; - } - - ConnectHistory* hist = nullptr; - ConnectHistory* next_hist = nullptr; - - for (int i = 0; i < 0x10000; ++i) - { - hist = connect_history[i]; - while (hist) - { - next_hist = hist->next; - destroy(hist); - hist = next_hist; - } - } - - for (int i = 1; i < fd_max; i++) - { - if (sessions[i]) - { - do_close_tcp(i); - } - } -} - -void flush_fifo(int32 fd) -{ - TracyZoneScoped; - if (sessions[fd] != nullptr) - { - sessions[fd]->func_send(fd); - } -} - -void flush_fifos() -{ - TracyZoneScoped; - for (int i = 1; i < fd_max; i++) - { - flush_fifo(i); - } -} - -void set_defaultparse(ParseFunc defaultparse) -{ - TracyZoneScoped; - default_func_parse = defaultparse; -} - -void set_eof(int32 fd) -{ - TracyZoneScoped; - if (session_isActive(fd)) - { - sessions[fd]->flag.eof = 1; - } -} - -int create_session(int fd, RecvFunc func_recv, SendFunc func_send, ParseFunc func_parse) -{ - TracyZoneScoped; - - DebugSockets(fmt::format("create_session fd: {}", fd).c_str()); - - sessions[fd] = std::make_unique(func_recv, func_send, func_parse); - - sessions[fd]->rdata.reserve(RFIFO_SIZE); - sessions[fd]->wdata.reserve(WFIFO_SIZE); - - sessions[fd]->rdata_tick = last_tick; - - return 0; -} - -int delete_session(int fd) -{ - TracyZoneScoped; - - DebugSockets(fmt::format("delete_session fd: {}", fd).c_str()); - - if (fd <= 0 || fd >= MAX_FD) - { - return -1; - } - - sessions[fd] = nullptr; - - // In order to resize fd_max to the minimum possible size, we have to find - // the fd in use with the highest value. We will iterate through the session - // list backwards until we find the first non-nullptr entry. - // clang-format off - auto result = std::find_if(sessions.rbegin(), sessions.rend(), - [](std::unique_ptr& entry) - { - return entry != nullptr; - }); - // clang-format on - - auto old_fd_max = fd_max; - - fd_max = std::distance(result, sessions.rend()); - - DebugSockets(fmt::format("Resizing fd_max from {} to {}.", old_fd_max, fd_max).c_str()); - - return 0; -} - -/*====================================== - * CORE : Socket options - *--------------------------------------*/ -void set_nonblocking(int fd, unsigned long yes) -{ - TracyZoneScoped; - // 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. - if (sIoctl(fd, FIONBIO, &yes) != 0) - { - ShowError("set_nonblocking: Failed to set socket #%d to non-blocking mode (code %d) - Please report this!!!", fd, sErrno); - } -} - -/* - * - * UDP LEVEL - * - */ -int32 makeBind_udp(uint32 ip, uint16 port) -{ - TracyZoneScoped; - sockaddr_in server_address{}; - int fd = 0; - int result = 0; - - fd = sSocket(AF_INET, SOCK_DGRAM, 0); - - if (fd == -1) - { - ShowError("make_listen_bind: socket creation failed (port %d, code %d)!", port, sErrno); - do_final(EXIT_FAILURE); - } - if (fd == 0) - { // reserved - ShowError("make_listen_bind: Socket #0 is reserved - Please report this!!!"); - sClose(fd); - return -1; - } - if (fd >= MAX_FD) - { // socket number too big - ShowError("make_listen_bind: New socket #%d is greater than can we handle! Increase the value of MAX_FD (currently %d) for your OS to fix this!", - fd, MAX_FD); - sClose(fd); - return -1; - } - - server_address.sin_family = AF_INET; - server_address.sin_addr.s_addr = htonl(ip); - server_address.sin_port = htons(port); - - result = sBind(fd, (struct sockaddr*)&server_address, sizeof(server_address)); - if (result == SOCKET_ERROR) - { - ShowError("make_listen_bind: bind failed (socket #%d, port %d, code %d)!", fd, port, sErrno); - do_final(EXIT_FAILURE); - } - - if (fd_max <= fd) - { - fd_max = fd + 1; - } - sFD_SET(fd, &readfds); - return fd; -} - -void socket_init_udp() -{ - TracyZoneScoped; - if (!_vsocket_init()) - { - return; - } - - // setup our socket - socket_udp_setup(); -} - -void do_close_udp(int32 fd) -{ - TracyZoneScoped; - do_close(fd); -} - -void socket_final_udp() -{ - TracyZoneScoped; - if (!_vsocket_final()) - { - return; - } -} - -int32 recvudp(int32 fd, void* buff, size_t nbytes, int32 flags, struct sockaddr* from, socklen_t* addrlen) -{ - TracyZoneScoped; - return sRecvfrom(fd, (char*)buff, (int)nbytes, flags, from, addrlen); -} - -int32 sendudp(int32 fd, void* buff, size_t nbytes, int32 flags, const struct sockaddr* from, socklen_t addrlen) -{ - TracyZoneScoped; - return sSendto(fd, (const char*)buff, (int)nbytes, flags, from, addrlen); -} - -void socket_init() -{ - TracyZoneScoped; - switch (SOCKET_TYPE) - { - case socket_type::TCP: - socket_init_tcp(); - break; - case socket_type::UDP: - socket_init_udp(); - break; - default: - break; - } -} - -void socket_final() -{ - TracyZoneScoped; - switch (SOCKET_TYPE) - { - case socket_type::TCP: - socket_final_tcp(); - break; - case socket_type::UDP: - socket_final_udp(); - break; - default: - break; - } + socket_init_udp(); } diff --git a/src/common/socket.h b/src/common/socket.h index 00efcabe934..ffddf642aba 100644 --- a/src/common/socket.h +++ b/src/common/socket.h @@ -53,15 +53,7 @@ typedef long in_addr_t; #include #include -/* - * - * COMMON LEVEL - * - */ -///////////////////////////////////////////////////////////////////// -#if defined(WIN32) -///////////////////////////////////////////////////////////////////// -// windows portability layer +#if defined(WIN32) // windows portability layer typedef int socklen_t; #define sErrno WSAGetLastError() @@ -122,8 +114,7 @@ int sSocket(int af, int type, int protocol); #define sFD_CLR(fd, set) FD_CLR(fd2sock(fd), set) #define sFD_ISSET(fd, set) FD_ISSET(fd2sock(fd), set) #define sFD_ZERO FD_ZERO -#else -// nix portability layer +#else // nix portability layer #define SOCKET_ERROR (-1) @@ -155,178 +146,22 @@ int sSocket(int af, int type, int protocol); #endif -#define TOB(n) ((uint8)((n) & std::numeric_limits::max())) -#define TOW(n) ((uint16)((n) & std::numeric_limits::max())) -#define TOL(n) ((uint32)((n) & std::numeric_limits::max())) - -enum class socket_type -{ - TCP, - UDP -}; - -extern socket_type SOCKET_TYPE; - extern fd_set readfds; extern int fd_max; extern time_t last_tick; -extern time_t stall_time; - -int32 makeConnection(uint32 ip, uint16 port, int32 type); - -int32 do_sockets(fd_set* rfd, duration next); - -void do_close(int32 fd); void socket_init(); -void socket_final(); - // hostname/ip conversion functions std::string ip2str(uint32 ip); uint32 str2ip(const char* ip_str); -/************************************************/ -/* - * - * TCP LEVEL - * - */ - -// initial recv buffer size (this will also be the max. size) -// biggest known packet: S 0153 .w .?B -> 24x24 256 color .bmp (0153 + len.w + 1618/1654/1756 bytes) -#define RFIFO_SIZE (2 * 1024) -// initial send buffer size (will be resized as needed) -#define WFIFO_SIZE (16 * 1024) - -// Maximum size of pending data in the write fifo. (for non-server connections) -// The connection is closed if it goes over the limit. -#define WFIFO_MAX (1 * 1024 * 1024) - -// Struct declaration -typedef int (*RecvFunc)(int fd); -typedef int (*SendFunc)(int fd); -typedef int (*ParseFunc)(int fd); - -// socket I/O macros -#define RFIFOHEAD(fd) -#define WFIFOHEAD(fd, size) \ - do \ - { \ - if ((fd) && sessions[fd]->wdata_size + (size) > sessions[fd]->max_wdata) \ - realloc_writefifo(fd, size); \ - } while (0) -//------------------- -#define RFIFOP(fd, pos) (sessions[fd]->rdata + sessions[fd]->rdata_pos + (pos)) -#define WFIFOP(fd, pos) (sessions[fd]->wdata + sessions[fd]->wdata_size + (pos)) - -#define RFIFOB(fd, pos) (*(uint8*)RFIFOP(fd, pos)) -#define WFIFOB(fd, pos) (*(uint8*)WFIFOP(fd, pos)) -#define RFIFOW(fd, pos) (*(uint16*)RFIFOP(fd, pos)) -#define WFIFOW(fd, pos) (*(uint16*)WFIFOP(fd, pos)) -#define RFIFOL(fd, pos) (*(uint32*)RFIFOP(fd, pos)) -#define WFIFOL(fd, pos) (*(uint32*)WFIFOP(fd, pos)) - -#define RFIFOREST(fd) (sessions[fd]->flag.eof ? 0 : sessions[fd]->rdata.size() - sessions[fd]->rdata_pos) -#define RFIFOFLUSH(fd) \ - do \ - { \ - if (sessions[fd]->rdata.size() == sessions[fd]->rdata_pos) \ - { \ - sessions[fd]->rdata_pos = 0; \ - sessions[fd]->rdata.clear(); \ - } \ - else \ - { \ - sessions[fd]->rdata.erase(0, sessions[fd]->rdata_pos); \ - sessions[fd]->rdata_pos = 0; \ - } \ - } while (0) - -struct socket_data -{ - struct - { - unsigned char eof : 1; - unsigned char server : 1; - } flag; - - uint32 client_addr; // remote client address - - std::string rdata, wdata; - size_t rdata_pos; - time_t rdata_tick; // time of last recv (for detecting timeouts); zero when timeout is disabled - - RecvFunc func_recv; - SendFunc func_send; - ParseFunc func_parse; - - bool ver_mismatch; - void* session_data; // stores application-specific data related to the session - - socket_data(RecvFunc _func_recv, SendFunc _func_send, ParseFunc _func_parse) - : rdata_tick(time(0)) - , func_recv(_func_recv) - , func_send(_func_send) - , func_parse(_func_parse) - { - client_addr = 0; - flag.eof = '\0'; - flag.server = '\0'; - rdata_pos = 0; - ver_mismatch = 0; - session_data = nullptr; - } -}; - -// Data prototype declaration -extern std::array, MAX_FD> sessions; - -////////////////////////////////// -// some checking on sockets -bool session_isValid(int fd); -bool session_isActive(int fd); - -int create_session(int fd, RecvFunc func_recv, SendFunc func_send, ParseFunc func_parse); -int delete_session(int fd); -////////////////////////////////// -int32 recv_to_fifo(int32 fd); - -int32 send_from_fifo(int32 fd); - -int32 connect_client(int32 listen_fd, sockaddr_in& client_address); - -int32 makeConnection_tcp(uint32 ip, uint16 port); - -int32 makeListenBind_tcp(const char* ip, uint16 port, RecvFunc connect_client); - -int32 RFIFOSKIP(int32 fd, size_t len); - -void socket_init_tcp(void); -void socket_final_tcp(void); - -void do_close_tcp(int32 fd); - -void flush_fifo(int32 fd); -void flush_fifos(void); - -void set_defaultparse(ParseFunc defaultparse); - -void set_eof(int32 fd); - void set_nonblocking(int fd, unsigned long yes); -/* - * - * UDP LEVEL - * - */ int32 makeBind_udp(uint32 ip, uint16 port); void socket_init_udp(void); -void do_close_udp(int32 fd); -void socket_final_udp(void); int32 recvudp(int32 fd, void* buff, size_t nbytes, int32 flags, struct sockaddr* from, socklen_t* addrlen); int32 sendudp(int32 fd, void* buff, size_t nbytes, int32 flags, const struct sockaddr* from, socklen_t addrlen); diff --git a/src/map/map.cpp b/src/map/map.cpp index c0bcf527819..9033509a23f 100644 --- a/src/map/map.cpp +++ b/src/map/map.cpp @@ -27,6 +27,7 @@ #include "common/database.h" #include "common/debug.h" #include "common/logging.h" +#include "common/socket.h" #include "common/timer.h" #include "common/utils.h" #include "common/vana_time.h" @@ -480,7 +481,6 @@ void do_final(int code) Async::delInstance(); timer_final(); - socket_final(); for (auto session : map_session_list) { @@ -515,17 +515,6 @@ void do_abort() do_final(EXIT_FAILURE); } -/************************************************************************ - * * - * set_socket_type * - * * - ************************************************************************/ - -void set_socket_type() -{ - SOCKET_TYPE = socket_type::UDP; -} - void ReportTracyStats() { TracyReportLuaMemory(lua.lua_state());