diff options
-rw-r--r-- | .gitignore | 1 | ||||
-rw-r--r-- | conf/common/socket.conf | 106 | ||||
-rw-r--r-- | conf/import-tmpl/packet_conf.txt | 0 | ||||
-rw-r--r-- | conf/import-tmpl/socket.conf | 32 | ||||
-rw-r--r-- | conf/packet.conf | 80 | ||||
-rw-r--r-- | src/char/char.c | 1 | ||||
-rw-r--r-- | src/common/socket.c | 263 | ||||
-rw-r--r-- | src/login/login.c | 1 | ||||
-rw-r--r-- | src/map/map.c | 1 | ||||
-rwxr-xr-x | tools/configconverter.pl | 17 |
10 files changed, 354 insertions, 148 deletions
diff --git a/.gitignore b/.gitignore index 877d93439..ce3a5c14f 100644 --- a/.gitignore +++ b/.gitignore @@ -62,7 +62,6 @@ Thumbs.db /conf/import/*.conf /conf/import/battle_conf.txt /conf/import/msg_conf.txt -/conf/import/packet_conf.txt # /log/ /log/*.log diff --git a/conf/common/socket.conf b/conf/common/socket.conf new file mode 100644 index 000000000..0dd5386cb --- /dev/null +++ b/conf/common/socket.conf @@ -0,0 +1,106 @@ +//================= Hercules Configuration ================================ +//= _ _ _ +//= | | | | | | +//= | |_| | ___ _ __ ___ _ _| | ___ ___ +//= | _ |/ _ \ '__/ __| | | | |/ _ \/ __| +//= | | | | __/ | | (__| |_| | | __/\__ \ +//= \_| |_/\___|_| \___|\__,_|_|\___||___/ +//================= License =============================================== +//= This file is part of Hercules. +//= http://herc.ws - http://github.com/HerculesWS/Hercules +//= +//= Copyright (C) 2014-2016 Hercules Dev Team +//= +//= Hercules is free software: you can redistribute it and/or modify +//= it under the terms of the GNU General Public License as published by +//= the Free Software Foundation, either version 3 of the License, or +//= (at your option) any later version. +//= +//= This program is distributed in the hope that it will be useful, +//= but WITHOUT ANY WARRANTY; without even the implied warranty of +//= MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//= GNU General Public License for more details. +//= +//= You should have received a copy of the GNU General Public License +//= along with this program. If not, see <http://www.gnu.org/licenses/>. +//========================================================================= +//= Hercules Sockets configuration file +//========================================================================= + +socket_configuration: { + // How long can a socket stall before closing the connection (in seconds)? + stall_time: 60 + + // Display debug reports (When something goes wrong during the report, the report is saved.) + debug: false + + // Linux/Epoll: Maxmimum Events per cycle + // Default Value: + // (Maxmimum Supported Connections)/2 + // NOTE: this controls the maximum collected socket-events per-cycle (call to epoll_wait()) + // for example settings this to 32 will allow up to 32 events (incomming data/new connections + // per server-cycle. + // NOTE: Recommended Settings is at least half the maxmimum supported connections + // Settings this to a lower value, may cause lags/delays + // Depending on available CPU Time + // NOTE: This Setting is only available on Linux when build using EPoll as event dispatcher! + // + //epoll_maxevents: 1024 + + // Maximum allowed size for clients packets in bytes. + // Default Values: + // 24576 (Clients < 20131223) + // 65535 (Clients >= 20131223) + // NOTE: To reduce the size of reported packets, lower the values of defines, which + // have been customized, such as MAX_STORAGE, MAX_GUILD_STORAGE or MAX_CART. + // NOTE: Do not modify this setting, unless the client has been modified to support + // larger packets. The client will crash, when it receives larger packets. + //socket_max_client_packet: 65535 + + //----- IP Rules Settings ----- + ip_rules: { + // If IP's are checked when connecting. + // This also enables DDoS protection. + enable: true + + // Order of the checks + // deny,allow : Checks deny rules, then allow rules. Allows if no rules match. + // allow,deny : Checks allow rules, then deny rules. Allows if no rules match. + // mutual-failure : Allows only if an allow rule matches and no deny rules match. + // (default is deny,allow) + order: "deny,allow" + + // IP rules + // allow : Accepts connections from the ip range (even if flagged as DDoS) + // deny : Rejects connections from the ip range + // The rules are processed in order, the first matching rule of each list (allow and deny) is used + allow_list: ( + //"127.0.0.1", + //"192.168.0.0/16", + //"10.0.0.0/255.0.0.0", + //"all", + ) + deny_list: ( + //"127.0.0.1", + ) + } + + //---- DDoS Protection Settings ---- + // If ddos.count connection request are made within ddos.interval ms, it assumes it's a DDoS attack + ddos: { + // Consecutive attempts interval (msec) + // (default is 3000 msecs, 3 seconds) + interval: 3000 //ddos_interval + + // Consecutive attempts trigger + // (default is 5 attemps) + count: 5 //ddos_count + + // The time interval after which the threat of DDoS is assumed to be gone (ms) + // After this amount of time, the DDoS restrictions are lifted. + // (default is 600000ms, 10min) + autoreset: 600000 //ddos_autoreset + } +} + +import: "conf/import/socket.conf" diff --git a/conf/import-tmpl/packet_conf.txt b/conf/import-tmpl/packet_conf.txt deleted file mode 100644 index e69de29bb..000000000 --- a/conf/import-tmpl/packet_conf.txt +++ /dev/null diff --git a/conf/import-tmpl/socket.conf b/conf/import-tmpl/socket.conf new file mode 100644 index 000000000..04d0a40c4 --- /dev/null +++ b/conf/import-tmpl/socket.conf @@ -0,0 +1,32 @@ +//================= Hercules Configuration ================================ +//= _ _ _ +//= | | | | | | +//= | |_| | ___ _ __ ___ _ _| | ___ ___ +//= | _ |/ _ \ '__/ __| | | | |/ _ \/ __| +//= | | | | __/ | | (__| |_| | | __/\__ \ +//= \_| |_/\___|_| \___|\__,_|_|\___||___/ +//================= License =============================================== +//= This file is part of Hercules. +//= http://herc.ws - http://github.com/HerculesWS/Hercules +//= +//= Copyright (C) 2014-2016 Hercules Dev Team +//= +//= Hercules is free software: you can redistribute it and/or modify +//= it under the terms of the GNU General Public License as published by +//= the Free Software Foundation, either version 3 of the License, or +//= (at your option) any later version. +//= +//= This program is distributed in the hope that it will be useful, +//= but WITHOUT ANY WARRANTY; without even the implied warranty of +//= MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//= GNU General Public License for more details. +//= +//= You should have received a copy of the GNU General Public License +//= along with this program. If not, see <http://www.gnu.org/licenses/>. +//========================================================================= +//= Hercules Sockets local configuration file +//========================================================================= + +socket_configuration: { + // See conf/common/socket.conf for details. +} diff --git a/conf/packet.conf b/conf/packet.conf deleted file mode 100644 index 1780d1b4c..000000000 --- a/conf/packet.conf +++ /dev/null @@ -1,80 +0,0 @@ -//===== Hercules Sockets Configuration ======================= -//= Hercules Sockets Configuration File -//===== Translated by: ======================================= -// Davidsiaw -//============================================================ - -// Display debug reports (When something goes wrong during the report, the report is saved.) -debug: no - -// How long can a socket stall before closing the connection (in seconds)? -stall_time: 60 - -// Linux/Epoll: Maxmimum Events per cycle -// Default Value: -// (Maxmimum Supported Connections)/2 -// NOTE: this controls the maximum collected socket-events per-cycle (call to epoll_wait()) -// for example settings this to 32 will allow up to 32 events (incomming data/new connections -// per server-cycle. -// NOTE: Recommended Settings is at least half the maxmimum supported connections -// Settings this to a lower value, may cause lags/delays -// Depending on available CPU Time -// NOTE: This Setting is only available on Linux when build using EPoll as event dispatcher! -// -//epoll_maxevents: 1024 - -// Maximum allowed size for clients packets in bytes (default: 65535). -// Default Values: -// 24576 (Clients < 20131223) -// 65535 (Clients >= 20131223) -// NOTE: To reduce the size of reported packets, lower the values of defines, which -// have been customized, such as MAX_STORAGE, MAX_GUILD_STORAGE or MAX_CART. -// NOTE: Do not modify this setting, unless the client has been modified to support -// larger packets. The client will crash, when it receives larger packets. -//socket_max_client_packet: 65535 - -//----- IP Rules Settings ----- - -// If IP's are checked when connecting. -// This also enables DDoS protection. -enable_ip_rules: yes - -// Order of the checks -// deny,allow : Checks deny rules, then allow rules. Allows if no rules match. -// allow,deny : Checks allow rules, then deny rules. Allows if no rules match. -// mutual-failure : Allows only if an allow rule matches and no deny rules match. -// (default is deny,allow) - -order: deny,allow -// order: allow,deny -// order: mutual-failture - -// IP rules -// allow : Accepts connections from the ip range (even if flagged as DDoS) -// deny : Rejects connections from the ip range -// The rules are processed in order, the first matching rule of each list (allow and deny) is used - -// allow: 127.0.0.1 -// allow: 192.168.0.0/16 -// allow: 10.0.0.0/255.0.0.0 -// allow: all - -// deny: 127.0.0.1 - -//---- DDoS Protection Settings ---- -// If ddos_count connection request are made within ddos_interval msec, it assumes it's a DDoS attack - -// Consecutive attempts interval (msec) -// (default is 3000 msecs, 3 seconds) -ddos_interval: 3000 - -// Consecutive attempts trigger -// (default is 5 attemps) -ddos_count: 5 - -// The time interval after which the threat of DDoS is assumed to be gone. (msec) -// After this amount of time, the DDoS restrictions are lifted. -// (default is 600000 msecs, 10 minutes) -ddos_autoreset: 600000 - -import: conf/import/packet_conf.txt diff --git a/src/char/char.c b/src/char/char.c index 62b1d8f4c..6f79a55e3 100644 --- a/src/char/char.c +++ b/src/char/char.c @@ -6211,6 +6211,7 @@ int do_init(int argc, char **argv) { CHECK_OLD_LOCAL_CONF("conf/import/char_conf.txt", "conf/import/char-server.conf"); CHECK_OLD_LOCAL_CONF("conf/import/inter_conf.txt", "conf/import/inter-server.conf"); + CHECK_OLD_LOCAL_CONF("conf/import/packet_conf.txt", "conf/import/socket.conf"); #undef CHECK_OLD_LOCAL_CONF } diff --git a/src/common/socket.c b/src/common/socket.c index c87afb2a3..cee2875d5 100644 --- a/src/common/socket.c +++ b/src/common/socket.c @@ -80,6 +80,8 @@ struct socket_interface *sockt; struct socket_data **session; +const char *SOCKET_CONF_FILENAME = "conf/common/socket.conf"; + #ifdef SEND_SHORTLIST // Add a fd to the shortlist so that it'll be recognized as a fd that needs // sending done on it. @@ -1071,14 +1073,16 @@ struct access_control { uint32 mask; }; +VECTOR_STRUCT_DECL(access_control_list, struct access_control); + enum aco { ACO_DENY_ALLOW, ACO_ALLOW_DENY, ACO_MUTUAL_FAILURE }; -static VECTOR_DECL(struct access_control) access_allow; -static VECTOR_DECL(struct access_control) access_deny; +static struct access_control_list access_allow; +static struct access_control_list access_deny; static int access_order = ACO_DENY_ALLOW; static int access_debug = 0; static int ddos_count = 10; @@ -1270,84 +1274,210 @@ int access_ipmask(const char *str, struct access_control *acc) acc->mask = mask; return 1; } + +/** + * 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 + */ +bool access_list_add(struct config_setting_t *setting, const char *list_name, struct access_control_list *access_list) +{ + const char *temp = NULL; + int i, setting_length; + + nullpo_retr(false, setting); + nullpo_retr(false, list_name); + nullpo_retr(false, access_list); + + if ((setting_length = libconfig->setting_length(setting)) <= 0) + return false; + + 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 (!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; +} + ////////////////////////////// #endif // MINICORE ////////////////////////////// -int socket_config_read(const char* cfgName) +/** + * 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. + */ +bool socket_config_read_iprules(const char *filename, struct config_t *config, bool imported) { - char line[1024],w1[1024],w2[1024]; - FILE *fp; +#ifndef MINICORE + struct config_setting_t *setting = NULL; + const char *temp = NULL; + + nullpo_retr(false, filename); + nullpo_retr(false, config); - fp = fopen(cfgName, "r"); - if(fp == NULL) { - ShowError("File not found: %s\n", cfgName); - return 1; + 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); - while (fgets(line, sizeof(line), fp)) { - if(line[0] == '/' && line[1] == '/') - continue; - if (sscanf(line, "%1023[^:]: %1023[^\r\n]", w1, w2) != 2) - continue; + if (!ip_rules) + return true; - 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 */ - } -#ifdef SOCKET_EPOLL - else if(!strcmpi(w1, "epoll_maxevents")) { - epoll_maxevents = atoi(w2); - if(epoll_maxevents < 16){ - epoll_maxevents = 16; // minimum that seems to be useful - } + 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); } + } + + 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); + } +#endif // ! MINICORE + + 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. + */ +bool socket_config_read_ddos(const char *filename, struct config_t *config, bool imported) +{ +#ifndef MINICORE + 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); + +#endif // ! MINICORE + 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. + */ +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 + #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")) { - struct access_control acc; - VECTOR_ENSURE(access_allow, 1, 1); - if (access_ipmask(w2, &acc)) - VECTOR_PUSH(access_allow, acc); - else - ShowError("socket_config_read: Invalid ip or ip range '%s'!\n", line); - } else if (!strcmpi(w1, "deny")) { - struct access_control acc; - VECTOR_ENSURE(access_deny, 1, 1); - if (access_ipmask(w2, &acc)) - VECTOR_PUSH(access_deny, acc); - else - ShowError("socket_config_read: Invalid ip or ip range '%s'!\n", line); + { + 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 // MINICORE - 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; +#endif // MINICORE + + // 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, 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) @@ -1496,7 +1626,6 @@ int socket_getips(uint32* ips, int max) void socket_init(void) { - char *SOCKET_CONF_FILENAME = "conf/packet.conf"; uint64 rlim_cur = FD_SETSIZE; #ifdef WIN32 @@ -1552,7 +1681,7 @@ void socket_init(void) // Get initial local ips sockt->naddr_ = sockt->getips(sockt->addr_,16); - socket_config_read(SOCKET_CONF_FILENAME); + socket_config_read(SOCKET_CONF_FILENAME, false); #ifndef SOCKET_EPOLL // Select based Event Dispatcher: diff --git a/src/login/login.c b/src/login/login.c index f1302b673..19293d61b 100644 --- a/src/login/login.c +++ b/src/login/login.c @@ -2032,6 +2032,7 @@ int do_init(int argc, char** argv) CHECK_OLD_LOCAL_CONF("conf/import/login_conf.txt", "conf/import/login-server.conf"); CHECK_OLD_LOCAL_CONF("conf/import/inter_conf.txt", "conf/import/inter-server.conf"); + CHECK_OLD_LOCAL_CONF("conf/import/packet_conf.txt", "conf/import/socket.conf"); #undef CHECK_OLD_LOCAL_CONF } diff --git a/src/map/map.c b/src/map/map.c index afb81c33c..17c025ff3 100644 --- a/src/map/map.c +++ b/src/map/map.c @@ -6401,6 +6401,7 @@ int do_init(int argc, char *argv[]) CHECK_OLD_LOCAL_CONF("conf/import/inter_conf.txt", "conf/import/inter-server.conf"); CHECK_OLD_LOCAL_CONF("conf/import/log_conf.txt", "conf/import/logs.conf"); CHECK_OLD_LOCAL_CONF("conf/import/script_conf.txt", "conf/import/script.conf"); + CHECK_OLD_LOCAL_CONF("conf/import/packet_conf.txt", "conf/import/socket.conf"); #undef CHECK_OLD_LOCAL_CONF } diff --git a/tools/configconverter.pl b/tools/configconverter.pl index 958286c49..669e3741c 100755 --- a/tools/configconverter.pl +++ b/tools/configconverter.pl @@ -514,6 +514,23 @@ my @defaults = ( import => {parse => \&parsecfg_string, print => \&printcfg_nil, path => "", default => "conf/import/script_conf.txt"}, } }, + { + files => ['packet.conf', 'import/packet_conf.txt'], + settings => { + debug => {parse => \&parsecfg_bool, print => \&printcfg_bool, path => "socket:socket_configuration/", default => "false"}, + stall_time => {parse => \&parsecfg_int, print => \&printcfg_int, path => "socket:socket_configuration/", default => 60}, + epoll_maxevents => {parse => \&parsecfg_int, print => \&printcfg_int, path => "socket:socket_configuration/", default => 1024}, + socket_max_client_packet => {parse => \&parsecfg_int, print => \&printcfg_int, path => "socket:socket_configuration/", default => 65535}, + enable_ip_rules => {parse => \&parsecfg_bool, print => \&printcfg_bool, path => "socket:socket_configuration/ip_rules/enable", default => "true"}, + order => {parse => \&parsecfg_string, print => \&printcfg_string, path => "socket:socket_configuration/ip_rules/", default => "deny,allow"}, + allow => {parse => \&parsecfg_stringarr, print => \&printcfg_strlist, path => "socket:socket_configuration/ip_rules/allow_list", default => []}, + deny => {parse => \&parsecfg_stringarr, print => \&printcfg_strlist, path => "socket:socket_configuration/ip_rules/deny_list", default => []}, + ddos_interval => {parse => \&parsecfg_int, print => \&printcfg_int, path => "socket:socket_configuration/ddos/interval", default => 3000}, + ddos_count => {parse => \&parsecfg_int, print => \&printcfg_int, path => "socket:socket_configuration/ddos/count", default => 5}, + ddos_autoreset => {parse => \&parsecfg_int, print => \&printcfg_int, path => "socket:socket_configuration/ddos/autoreset", default => 600000}, + import => {parse => \&parsecfg_string, print => \&printcfg_nil, path => "", default => "conf/import/packet_conf.txt"}, + } + }, ); for (@ARGV) { |