/* * Unreal Internet Relay Chat Daemon, src/s_conf.c * (C) 1998-2000 Chris Behrens & Fred Jacobs (comstud, moogle) * (C) 2000-2002 Carsten V. Munk and the UnrealIRCd Team * * This program 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 1, 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, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "struct.h" #include "url.h" #include "common.h" #include "sys.h" #include "numeric.h" #include "channel.h" #include "macros.h" #include #ifndef _WIN32 #include #include #else #include #endif #include #ifdef __hpux #include "inet.h" #endif #if defined(PCS) || defined(AIX) || defined(SVR3) #include #endif #include #ifdef GLOBH #include #endif #include "h.h" #include "inet.h" #include "proto.h" #ifdef _WIN32 #undef GLOBH #endif /* * Some typedefs.. */ typedef struct _confcommand ConfigCommand; struct _confcommand { char *name; int (*conffunc)(ConfigFile *conf, ConfigEntry *ce); int (*testfunc)(ConfigFile *conf, ConfigEntry *ce); }; typedef struct _conf_namevalue NameValue; struct _conf_namevalue { long flag; char *name; }; /* Config commands */ static int _conf_admin (ConfigFile *conf, ConfigEntry *ce); static int _conf_me (ConfigFile *conf, ConfigEntry *ce); static int _conf_files (ConfigFile *conf, ConfigEntry *ce); static int _conf_oper (ConfigFile *conf, ConfigEntry *ce); static int _conf_operclass (ConfigFile *conf, ConfigEntry *ce); static int _conf_class (ConfigFile *conf, ConfigEntry *ce); static int _conf_drpass (ConfigFile *conf, ConfigEntry *ce); static int _conf_ulines (ConfigFile *conf, ConfigEntry *ce); static int _conf_include (ConfigFile *conf, ConfigEntry *ce); static int _conf_tld (ConfigFile *conf, ConfigEntry *ce); static int _conf_listen (ConfigFile *conf, ConfigEntry *ce); static int _conf_allow (ConfigFile *conf, ConfigEntry *ce); static int _conf_except (ConfigFile *conf, ConfigEntry *ce); static int _conf_vhost (ConfigFile *conf, ConfigEntry *ce); static int _conf_link (ConfigFile *conf, ConfigEntry *ce); static int _conf_ban (ConfigFile *conf, ConfigEntry *ce); static int _conf_set (ConfigFile *conf, ConfigEntry *ce); static int _conf_deny (ConfigFile *conf, ConfigEntry *ce); static int _conf_deny_dcc (ConfigFile *conf, ConfigEntry *ce); static int _conf_deny_link (ConfigFile *conf, ConfigEntry *ce); static int _conf_deny_channel (ConfigFile *conf, ConfigEntry *ce); static int _conf_deny_version (ConfigFile *conf, ConfigEntry *ce); static int _conf_allow_channel (ConfigFile *conf, ConfigEntry *ce); static int _conf_allow_dcc (ConfigFile *conf, ConfigEntry *ce); static int _conf_loadmodule (ConfigFile *conf, ConfigEntry *ce); static int _conf_log (ConfigFile *conf, ConfigEntry *ce); static int _conf_alias (ConfigFile *conf, ConfigEntry *ce); static int _conf_help (ConfigFile *conf, ConfigEntry *ce); static int _conf_offchans (ConfigFile *conf, ConfigEntry *ce); static int _conf_spamfilter (ConfigFile *conf, ConfigEntry *ce); /* * Validation commands */ static int _test_admin (ConfigFile *conf, ConfigEntry *ce); static int _test_me (ConfigFile *conf, ConfigEntry *ce); static int _test_files (ConfigFile *conf, ConfigEntry *ce); static int _test_oper (ConfigFile *conf, ConfigEntry *ce); static int _test_operclass (ConfigFile *conf, ConfigEntry *ce); static int _test_class (ConfigFile *conf, ConfigEntry *ce); static int _test_drpass (ConfigFile *conf, ConfigEntry *ce); static int _test_ulines (ConfigFile *conf, ConfigEntry *ce); static int _test_include (ConfigFile *conf, ConfigEntry *ce); static int _test_tld (ConfigFile *conf, ConfigEntry *ce); static int _test_listen (ConfigFile *conf, ConfigEntry *ce); static int _test_allow (ConfigFile *conf, ConfigEntry *ce); static int _test_except (ConfigFile *conf, ConfigEntry *ce); static int _test_vhost (ConfigFile *conf, ConfigEntry *ce); static int _test_link (ConfigFile *conf, ConfigEntry *ce); static int _test_ban (ConfigFile *conf, ConfigEntry *ce); static int _test_set (ConfigFile *conf, ConfigEntry *ce); static int _test_deny (ConfigFile *conf, ConfigEntry *ce); static int _test_allow_channel (ConfigFile *conf, ConfigEntry *ce); static int _test_allow_dcc (ConfigFile *conf, ConfigEntry *ce); static int _test_loadmodule (ConfigFile *conf, ConfigEntry *ce); static int _test_log (ConfigFile *conf, ConfigEntry *ce); static int _test_alias (ConfigFile *conf, ConfigEntry *ce); static int _test_help (ConfigFile *conf, ConfigEntry *ce); static int _test_offchans (ConfigFile *conf, ConfigEntry *ce); static int _test_spamfilter (ConfigFile *conf, ConfigEntry *ce); /* This MUST be alphabetized */ static ConfigCommand _ConfigCommands[] = { { "admin", _conf_admin, _test_admin }, { "alias", _conf_alias, _test_alias }, { "allow", _conf_allow, _test_allow }, { "ban", _conf_ban, _test_ban }, { "class", _conf_class, _test_class }, { "deny", _conf_deny, _test_deny }, { "drpass", _conf_drpass, _test_drpass }, { "except", _conf_except, _test_except }, { "files", _conf_files, _test_files }, { "help", _conf_help, _test_help }, { "include", NULL, _test_include }, { "link", _conf_link, _test_link }, { "listen", _conf_listen, _test_listen }, { "loadmodule", NULL, _test_loadmodule}, { "log", _conf_log, _test_log }, { "me", _conf_me, _test_me }, { "official-channels", _conf_offchans, _test_offchans }, { "oper", _conf_oper, _test_oper }, { "operclass", _conf_operclass, _test_operclass }, { "set", _conf_set, _test_set }, { "spamfilter", _conf_spamfilter, _test_spamfilter }, { "tld", _conf_tld, _test_tld }, { "ulines", _conf_ulines, _test_ulines }, { "vhost", _conf_vhost, _test_vhost }, }; /* This MUST be alphabetized */ static NameValue _ListenerFlags[] = { { LISTENER_CLIENTSONLY, "clientsonly"}, { LISTENER_DEFER_ACCEPT, "defer-accept"}, { LISTENER_SERVERSONLY, "serversonly"}, { LISTENER_SSL, "ssl"}, { LISTENER_NORMAL, "standard"}, }; /* This MUST be alphabetized */ static NameValue _LinkFlags[] = { { CONNECT_AUTO, "autoconnect" }, { CONNECT_INSECURE, "insecure" }, { CONNECT_QUARANTINE, "quarantine"}, { CONNECT_SSL, "ssl" }, }; /* This MUST be alphabetized */ static NameValue _LogFlags[] = { { LOG_CHGCMDS, "chg-commands" }, { LOG_CLIENT, "connects" }, { LOG_ERROR, "errors" }, { LOG_KILL, "kills" }, { LOG_KLINE, "kline" }, { LOG_OPER, "oper" }, { LOG_OVERRIDE, "oper-override" }, { LOG_SACMDS, "sadmin-commands" }, { LOG_SERVER, "server-connects" }, { LOG_SPAMFILTER, "spamfilter" }, { LOG_TKL, "tkl" }, }; /* This MUST be alphabetized */ static NameValue ExceptTklFlags[] = { { 0, "all" }, { TKL_GLOBAL|TKL_KILL, "gline" }, { TKL_GLOBAL|TKL_NICK, "gqline" }, { TKL_GLOBAL|TKL_ZAP, "gzline" }, { TKL_NICK, "qline" }, { TKL_GLOBAL|TKL_SHUN, "shun" } }; /* This MUST be alphabetized */ static NameValue _SSLFlags[] = { { SSLFLAG_FAILIFNOCERT, "fail-if-no-clientcert" }, { SSLFLAG_DONOTACCEPTSELFSIGNED, "no-self-signed" }, { SSLFLAG_NOSTARTTLS, "no-starttls" }, { SSLFLAG_VERIFYCERT, "verify-certificate" }, }; struct { unsigned conf_me : 1; unsigned conf_admin : 1; unsigned conf_listen : 1; } requiredstuff; struct SetCheck settings; /* * Utilities */ void port_range(char *string, int *start, int *end); long config_checkval(char *value, unsigned short flags); /* * Parser */ ConfigFile *config_load(char *filename); void config_free(ConfigFile *cfptr); static ConfigFile *config_parse(char *filename, char *confdata); static void config_entry_free(ConfigEntry *ceptr); ConfigEntry *config_find_entry(ConfigEntry *ce, char *name); /* * Error handling */ void config_warn(char *format, ...); void config_error(char *format, ...); void config_status(char *format, ...); void config_progress(char *format, ...); #ifdef _WIN32 extern void win_log(char *format, ...); extern void win_error(); #endif extern void add_entropy_configfile(struct stat *st, char *buf); extern void unload_all_unused_snomasks(void); extern void unload_all_unused_umodes(void); extern void unload_all_unused_extcmodes(void); extern int charsys_test_language(char *name); extern void charsys_add_language(char *name); extern void charsys_reset_pretest(void); int charsys_postconftest(void); void charsys_finish(void); int reloadable_perm_module_unloaded(void); /* * Config parser (IRCd) */ int init_conf(char *rootconf, int rehash); int load_conf(char *filename, const char *original_path); void config_rehash(); int config_run(); /* * Configuration linked lists */ ConfigItem_me *conf_me = NULL; ConfigItem_files *conf_files = NULL; ConfigItem_class *conf_class = NULL; ConfigItem_class *default_class = NULL; ConfigItem_admin *conf_admin = NULL; ConfigItem_admin *conf_admin_tail = NULL; ConfigItem_drpass *conf_drpass = NULL; ConfigItem_ulines *conf_ulines = NULL; ConfigItem_tld *conf_tld = NULL; ConfigItem_oper *conf_oper = NULL; ConfigItem_operclass *conf_operclass = NULL; ConfigItem_listen *conf_listen = NULL; ConfigItem_allow *conf_allow = NULL; ConfigItem_except *conf_except = NULL; ConfigItem_vhost *conf_vhost = NULL; ConfigItem_link *conf_link = NULL; ConfigItem_ban *conf_ban = NULL; ConfigItem_deny_dcc *conf_deny_dcc = NULL; ConfigItem_deny_channel *conf_deny_channel = NULL; ConfigItem_allow_channel *conf_allow_channel = NULL; ConfigItem_allow_dcc *conf_allow_dcc = NULL; ConfigItem_deny_link *conf_deny_link = NULL; ConfigItem_deny_version *conf_deny_version = NULL; ConfigItem_log *conf_log = NULL; ConfigItem_alias *conf_alias = NULL; ConfigItem_include *conf_include = NULL; ConfigItem_help *conf_help = NULL; ConfigItem_offchans *conf_offchans = NULL; MODVAR aConfiguration iConf; MODVAR aConfiguration tempiConf; MODVAR ConfigFile *conf = NULL; MODVAR int ipv6_disabled = 0; MODVAR aClient *remote_rehash_client = NULL; MODVAR int config_error_flag = 0; int config_verbose = 0; MODVAR int need_34_upgrade = 0; void add_include(const char *filename, const char *included_from, int included_from_line); #ifdef USE_LIBCURL void add_remote_include(const char *, const char *, int, const char *, const char *included_from, int included_from_line); void update_remote_include(ConfigItem_include *inc, const char *file, int, const char *errorbuf); int remote_include(ConfigEntry *ce); #endif void unload_notloaded_includes(void); void load_includes(void); void unload_loaded_includes(void); int rehash_internal(aClient *cptr, aClient *sptr, int sig); void port_range(char *string, int *start, int *end) { char *c = strchr(string, '-'); if (!c) { int tmp = atoi(string); *start = tmp; *end = tmp; return; } *c = '\0'; *start = atoi(string); *end = atoi((c+1)); } /** Parses '5:60s' config values. * orig: original string * times: pointer to int, first value (before the :) * period: pointer to int, second value (after the :) in seconds * RETURNS: 0 for parse error, 1 if ok. * REMARK: times&period should be ints! */ int config_parse_flood(char *orig, int *times, int *period) { char *x; *times = *period = 0; x = strchr(orig, ':'); /* 'blah', ':blah', '1:' */ if (!x || (x == orig) || (*(x+1) == '\0')) return 0; *x = '\0'; *times = atoi(orig); *period = config_checkval(x+1, CFG_TIME); *x = ':'; /* restore */ return 1; } long config_checkval(char *orig, unsigned short flags) { char *value; char *text; long ret = 0; value = strdup(orig); if (flags == CFG_YESNO) { for (text = value; *text; text++) { if (!isalnum(*text)) continue; if (tolower(*text) == 'y' || (tolower(*text) == 'o' && tolower(*(text+1)) == 'n') || *text == '1' || tolower(*text) == 't') { ret = 1; break; } } } else if (flags == CFG_SIZE) { int mfactor = 1; char *sz; for (text = value; *text; text++) { if (isalpha(*text)) { if (tolower(*text) == 'k') mfactor = 1024; else if (tolower(*text) == 'm') mfactor = 1048576; else if (tolower(*text) == 'g') mfactor = 1073741824; else mfactor = 1; sz = text; while (isalpha(*text)) text++; *sz-- = 0; while (sz-- > value && *sz) { if (isspace(*sz)) *sz = 0; if (!isdigit(*sz)) break; } ret += atoi(sz+1)*mfactor; if (*text == '\0') { text++; break; } } } mfactor = 1; sz = text; sz--; while (sz-- > value) { if (isspace(*sz)) *sz = 0; if (!isdigit(*sz)) break; } ret += atoi(sz+1)*mfactor; } else if (flags == CFG_TIME) { int mfactor = 1; char *sz; for (text = value; *text; text++) { if (isalpha(*text)) { if (tolower(*text) == 'w') mfactor = 604800; else if (tolower(*text) == 'd') mfactor = 86400; else if (tolower(*text) == 'h') mfactor = 3600; else if (tolower(*text) == 'm') mfactor = 60; else mfactor = 1; sz = text; while (isalpha(*text)) text++; *sz-- = 0; while (sz-- > value && *sz) { if (isspace(*sz)) *sz = 0; if (!isdigit(*sz)) break; } ret += atoi(sz+1)*mfactor; if (*text == '\0') { text++; break; } } } mfactor = 1; sz = text; sz--; while (sz-- > value) { if (isspace(*sz)) *sz = 0; if (!isdigit(*sz)) break; } ret += atoi(sz+1)*mfactor; } free(value); return ret; } void set_channelmodes(char *modes, struct ChMode *store, int warn) { aCtab *tab; char *params = strchr(modes, ' '); char *parambuf = NULL; char *param = NULL; char *save = NULL; warn = 0; // warn is broken if (params) { params++; parambuf = strdup(params); param = strtoken(&save, parambuf, " "); } for (; *modes && *modes != ' '; modes++) { if (*modes == '+') continue; if (*modes == '-') /* When a channel is created it has no modes, so just ignore if the * user asks us to unset anything -- codemastr */ { while (*modes && *modes != '+') modes++; continue; } for (tab = &cFlagTab[0]; tab->mode; tab++) { if (tab->flag == *modes) { if (tab->parameters) { /* INCOMPATIBLE */ break; } store->mode |= tab->mode; break; } } /* Try extcmodes */ if (!tab->mode) { int i; for (i=0; i <= Channelmode_highest; i++) { if (!(Channelmode_Table[i].flag)) continue; if (*modes == Channelmode_Table[i].flag) { if (Channelmode_Table[i].paracount) { if (!param) break; param = Channelmode_Table[i].conv_param(param, NULL); if (!param) break; /* invalid parameter fmt, do not set mode. */ store->extparams[i] = strdup(param); /* Get next parameter */ param = strtoken(&save, NULL, " "); } store->extmodes |= Channelmode_Table[i].mode; break; } } } } if (parambuf) free(parambuf); } void chmode_str(struct ChMode *modes, char *mbuf, char *pbuf, size_t mbuf_size, size_t pbuf_size) { aCtab *tab; int i; if (!(mbuf_size && pbuf_size)) return; *pbuf = 0; *mbuf++ = '+'; if (--mbuf_size == 0) return; for (tab = &cFlagTab[0]; tab->mode; tab++) { if (modes->mode & tab->mode) { if (!tab->parameters) { *mbuf++ = tab->flag; if (!--mbuf_size) { *--mbuf=0; break; } } } } for (i=0; i <= Channelmode_highest; i++) { if (!(Channelmode_Table[i].flag)) continue; if (modes->extmodes & Channelmode_Table[i].mode) { if (mbuf_size) { *mbuf++ = Channelmode_Table[i].flag; if (!--mbuf_size) { *--mbuf=0; break; } } if (Channelmode_Table[i].paracount) { strncat(pbuf, modes->extparams[i], pbuf_size-1); pbuf_size-=strlen(modes->extparams[i]); if (!pbuf_size) break; strncat(pbuf, " ", pbuf_size-1); if (!--pbuf_size) break; } } } *mbuf=0; } int channellevel_to_int(char *s) { /* Requested at http://bugs.unrealircd.org/view.php?id=3852 */ if (!strcmp(s, "none")) return CHFL_DEOPPED; if (!strcmp(s, "voice")) return CHFL_VOICE; if (!strcmp(s, "halfop")) return CHFL_HALFOP; if (!strcmp(s, "op") || !strcmp(s, "chanop")) return CHFL_CHANOP; if (!strcmp(s, "protect") || !strcmp(s, "chanprot")) #ifdef PREFIX_AQ return CHFL_CHANPROT; #else return CHFL_CHANOP|CHFL_CHANPROT; #endif if (!strcmp(s, "owner") || !strcmp(s, "chanowner")) #ifdef PREFIX_AQ return CHFL_CHANOWNER; #else return CHFL_CHANOP|CHFL_CHANOWNER; #endif return 0; /* unknown or unsupported */ } /* Channel flag (eg: CHFL_CHANOWNER) to SJOIN symbol (eg: *). * WARNING: Do not confuse SJOIN symbols with prefixes in /NAMES! */ char *chfl_to_sjoin_symbol(int s) { switch(s) { case CHFL_VOICE: return "+"; case CHFL_HALFOP: return "%"; case CHFL_CHANOP: return "@"; case CHFL_CHANPROT: #ifdef PREFIX_AQ return "~"; #else return "~@"; #endif case CHFL_CHANOWNER: #ifdef PREFIX_AQ return "*"; #else return "*@"; #endif case CHFL_DEOPPED: default: return ""; } /* NOT REACHED */ } char chfl_to_chanmode(int s) { switch(s) { case CHFL_VOICE: return 'v'; case CHFL_HALFOP: return 'h'; case CHFL_CHANOP: return 'o'; case CHFL_CHANPROT: return 'a'; case CHFL_CHANOWNER: return 'q'; case CHFL_DEOPPED: default: return '\0'; } /* NOT REACHED */ } ConfigFile *config_load(char *filename) { struct stat sb; int fd; int ret; char *buf = NULL; ConfigFile *cfptr; #ifndef _WIN32 fd = open(filename, O_RDONLY); #else fd = open(filename, O_RDONLY|O_BINARY); #endif if (fd == -1) { config_error("Couldn't open \"%s\": %s\n", filename, strerror(errno)); return NULL; } if (fstat(fd, &sb) == -1) { config_error("Couldn't fstat \"%s\": %s\n", filename, strerror(errno)); close(fd); return NULL; } if (!sb.st_size) { /* Workaround for empty files */ cfptr = config_parse(filename, " "); close(fd); return cfptr; } buf = MyMalloc(sb.st_size+1); if (buf == NULL) { config_error("Out of memory trying to load \"%s\"\n", filename); close(fd); return NULL; } ret = read(fd, buf, sb.st_size); if (ret != sb.st_size) { config_error("Error reading \"%s\": %s\n", filename, ret == -1 ? strerror(errno) : strerror(EFAULT)); free(buf); close(fd); return NULL; } /* Just me or could this cause memory corrupted when ret <0 ? */ buf[ret] = '\0'; close(fd); add_entropy_configfile(&sb, buf); cfptr = config_parse(filename, buf); free(buf); return cfptr; } void config_free(ConfigFile *cfptr) { ConfigFile *nptr; for(;cfptr;cfptr=nptr) { nptr = cfptr->cf_next; if (cfptr->cf_entries) config_entry_free(cfptr->cf_entries); if (cfptr->cf_filename) free(cfptr->cf_filename); free(cfptr); } } /** Remove quotes so that 'hello \"all\" \\ lala' becomes 'hello "all" \ lala' */ void unreal_delquotes(char *i) { char *o; for (o = i; *i; i++) { if (*i == '\\') { if ((i[1] == '\\') || (i[1] == '"')) { i++; /* skip original \ */ if (*i == '\0') break; } } *o++ = *i; } *o = '\0'; } /* This is the internal parser, made by Chris Behrens & Fred Jacobs */ static ConfigFile *config_parse(char *filename, char *confdata) { char *ptr; char *start; int linenumber = 1; int errors = 0; ConfigEntry *curce; ConfigEntry **lastce; ConfigEntry *cursection; ConfigFile *curcf; ConfigFile *lastcf; lastcf = curcf = MyMalloc(sizeof(ConfigFile)); memset(curcf, 0, sizeof(ConfigFile)); curcf->cf_filename = strdup(filename); lastce = &(curcf->cf_entries); curce = NULL; cursection = NULL; /* Replace \r's with spaces .. ugly ugly -Stskeeps */ for (ptr=confdata; *ptr; ptr++) if (*ptr == '\r') *ptr = ' '; for(ptr=confdata;*ptr;ptr++) { switch(*ptr) { case ';': if (!curce) { config_status("%s:%i Ignoring extra semicolon\n", filename, linenumber); break; } *lastce = curce; lastce = &(curce->ce_next); curce->ce_fileposend = (ptr - confdata); curce = NULL; break; case '{': if (!curce) { config_error("%s:%i: New section start detected on line %d but the section has no name. " "Sections should start with a name like 'oper {' or 'set {'.", filename, linenumber, linenumber); errors++; continue; } else if (curce->ce_entries) { config_error("%s:%i: New section start but previous section did not end properly. " "Check line %d and the line(s) before, you are likely missing a '};' there.\n", filename, linenumber, linenumber); errors++; continue; } curce->ce_sectlinenum = linenumber; lastce = &(curce->ce_entries); cursection = curce; curce = NULL; break; case '}': if (curce) { config_error("%s:%i: Missing semicolon (';') before close brace. Check line %d and the line(s) before.\n", filename, linenumber, linenumber); config_entry_free(curce); config_free(curcf); errors++; return NULL; } else if (!cursection) { config_error("%s:%i: You have a close brace ('};') too many. " "Check line %d AND the lines above it from the previous block.\n", filename, linenumber, linenumber); errors++; continue; } curce = cursection; cursection->ce_fileposend = (ptr - confdata); cursection = cursection->ce_prevlevel; if (!cursection) lastce = &(curcf->cf_entries); else lastce = &(cursection->ce_entries); for(;*lastce;lastce = &((*lastce)->ce_next)) continue; break; case '#': ptr++; while(*ptr && (*ptr != '\n')) ptr++; if (!*ptr) break; ptr--; continue; case '/': if (*(ptr+1) == '/') { ptr += 2; while(*ptr && (*ptr != '\n')) ptr++; if (!*ptr) break; ptr--; /* grab the \n on next loop thru */ continue; } else if (*(ptr+1) == '*') { int commentstart = linenumber; int commentlevel = 1; for(ptr+=2;*ptr;ptr++) { if ((*ptr == '/') && (*(ptr+1) == '*')) { commentlevel++; ptr++; } else if ((*ptr == '*') && (*(ptr+1) == '/')) { commentlevel--; ptr++; } else if (*ptr == '\n') linenumber++; if (!commentlevel) break; } if (!*ptr) { config_error("%s:%i Comment on line %d does not end\n", filename, commentstart, commentstart); errors++; config_entry_free(curce); config_free(curcf); return NULL; } } break; case '\"': if (curce && curce->ce_varlinenum != linenumber && cursection) { config_error("%s:%i: Missing semicolon (';') at end of line. " "Line %d must end with a ; character\n", filename, curce->ce_varlinenum, curce->ce_varlinenum); errors++; *lastce = curce; lastce = &(curce->ce_next); curce->ce_fileposend = (ptr - confdata); curce = NULL; } start = ++ptr; for(;*ptr;ptr++) { if (*ptr == '\\') { if ((ptr[1] == '\\') || (ptr[1] == '"')) { /* \\ or \" in config file (escaped) */ ptr++; /* skip */ continue; } } else if ((*ptr == '\"') || (*ptr == '\n')) break; } if (!*ptr || (*ptr == '\n')) { config_error("%s:%i: Unterminated quote found\n", filename, linenumber); errors++; config_entry_free(curce); config_free(curcf); return NULL; } if (curce) { if (curce->ce_vardata) { config_error("%s:%i: Extra data detected. Perhaps missing a ';' or one too many?\n", filename, linenumber); errors++; } else { curce->ce_vardata = MyMalloc(ptr-start+1); strlcpy(curce->ce_vardata, start, ptr-start+1); unreal_delquotes(curce->ce_vardata); } } else { curce = MyMalloc(sizeof(ConfigEntry)); memset(curce, 0, sizeof(ConfigEntry)); curce->ce_varname = MyMalloc((ptr-start)+1); strlcpy(curce->ce_varname, start, ptr-start+1); unreal_delquotes(curce->ce_varname); curce->ce_varlinenum = linenumber; curce->ce_fileptr = curcf; curce->ce_prevlevel = cursection; curce->ce_fileposstart = (start - confdata); } break; case '\n': linenumber++; /* fall through */ case '\t': case ' ': case '=': case '\r': break; default: if ((*ptr == '*') && (*(ptr+1) == '/')) { config_status("%s:%i Ignoring extra end comment\n", filename, linenumber); ptr++; break; } start = ptr; for(;*ptr;ptr++) { if ((*ptr == ' ') || (*ptr == '=') || (*ptr == '\t') || (*ptr == '\n') || (*ptr == ';')) break; } if (!*ptr) { if (curce) config_error("%s: End of file reached but directive or block at line %i did not end properly. " "Perhaps a missing ; (semicolon) somewhere?\n", filename, curce->ce_varlinenum); else if (cursection) config_error("%s: End of file reached but the section which starts at line %i did never end properly. " "Perhaps a missing }; ?\n", filename, cursection->ce_sectlinenum); else config_error("%s: Unexpected end of file. Some line or block did not end properly. " "Look for any missing } and };\n", filename); errors++; config_entry_free(curce); config_free(curcf); return NULL; } if (curce) { if (curce->ce_vardata) { config_error("%s:%i: Extra data detected. Check for a missing ; character at or around line %d\n", filename, linenumber, linenumber-1); errors++; } else { curce->ce_vardata = MyMalloc(ptr-start+1); strlcpy(curce->ce_vardata, start, ptr-start+1); } } else { curce = MyMalloc(sizeof(ConfigEntry)); memset(curce, 0, sizeof(ConfigEntry)); curce->ce_varname = MyMalloc((ptr-start)+1); strlcpy(curce->ce_varname, start, ptr-start+1); curce->ce_varlinenum = linenumber; curce->ce_fileptr = curcf; curce->ce_prevlevel = cursection; curce->ce_fileposstart = (start - confdata); } if ((*ptr == ';') || (*ptr == '\n')) ptr--; break; } /* switch */ if (!*ptr) /* This IS possible. -- Syzop */ break; } /* for */ if (curce) { config_error("%s: End of file reached but directive or block at line %i did not end properly. " "Perhaps a missing ; (semicolon) somewhere?\n", filename, curce->ce_varlinenum); errors++; config_entry_free(curce); } else if (cursection) { config_error("%s: End of file reached but the section which starts at line %i did never end properly. " "Perhaps a missing }; ?\n", filename, cursection->ce_sectlinenum); errors++; } if (errors) { config_free(curcf); return NULL; } return curcf; } static void config_entry_free(ConfigEntry *ceptr) { ConfigEntry *nptr; for(;ceptr;ceptr=nptr) { nptr = ceptr->ce_next; if (ceptr->ce_entries) config_entry_free(ceptr->ce_entries); if (ceptr->ce_varname) free(ceptr->ce_varname); if (ceptr->ce_vardata) free(ceptr->ce_vardata); free(ceptr); } } ConfigEntry *config_find_entry(ConfigEntry *ce, char *name) { ConfigEntry *cep; for (cep = ce; cep; cep = cep->ce_next) if (cep->ce_varname && !strcmp(cep->ce_varname, name)) break; return cep; } void config_error(char *format, ...) { va_list ap; char buffer[1024]; char *ptr; va_start(ap, format); vsnprintf(buffer, sizeof(buffer), format, ap); va_end(ap); if ((ptr = strchr(buffer, '\n')) != NULL) *ptr = '\0'; #ifdef _WIN32 if (!loop.ircd_booted) win_log("[error] %s", buffer); #endif ircd_log(LOG_ERROR, "config error: %s", buffer); sendto_realops("error: %s", buffer); if (remote_rehash_client) sendto_one(remote_rehash_client, ":%s NOTICE %s :error: %s", me.name, remote_rehash_client->name, buffer); /* We cannot live with this */ config_error_flag = 1; } void config_error_missing(const char *filename, int line, const char *entry) { config_error("%s:%d: %s is missing", filename, line, entry); } void config_error_unknown(const char *filename, int line, const char *block, const char *entry) { config_error("%s:%d: Unknown directive '%s::%s'", filename, line, block, entry); } void config_error_unknownflag(const char *filename, int line, const char *block, const char *entry) { config_error("%s:%d: Unknown %s flag '%s'", filename, line, block, entry); } void config_error_unknownopt(const char *filename, int line, const char *block, const char *entry) { config_error("%s:%d: Unknown %s option '%s'", filename, line, block, entry); } void config_error_noname(const char *filename, int line, const char *block) { config_error("%s:%d: %s block has no name", filename, line, block); } void config_error_blank(const char *filename, int line, const char *block) { config_error("%s:%d: Blank %s entry", filename, line, block); } void config_error_empty(const char *filename, int line, const char *block, const char *entry) { config_error("%s:%d: %s::%s specified without a value", filename, line, block, entry); } void config_status(char *format, ...) { va_list ap; char buffer[1024]; char *ptr; va_start(ap, format); vsnprintf(buffer, 1023, format, ap); va_end(ap); if ((ptr = strchr(buffer, '\n')) != NULL) *ptr = '\0'; #ifdef _WIN32 if (!loop.ircd_booted) win_log("* %s", buffer); #endif ircd_log(LOG_ERROR, "%s", buffer); sendto_realops("%s", buffer); if (remote_rehash_client) sendto_one(remote_rehash_client, ":%s NOTICE %s :%s", me.name, remote_rehash_client->name, buffer); } void config_warn(char *format, ...) { va_list ap; char buffer[1024]; char *ptr; va_start(ap, format); vsnprintf(buffer, 1023, format, ap); va_end(ap); if ((ptr = strchr(buffer, '\n')) != NULL) *ptr = '\0'; #ifdef _WIN32 if (!loop.ircd_booted) win_log("[warning] %s", buffer); #endif ircd_log(LOG_ERROR, "[warning] %s", buffer); sendto_realops("[warning] %s", buffer); if (remote_rehash_client) sendto_one(remote_rehash_client, ":%s NOTICE %s :[warning] %s", me.name, remote_rehash_client->name, buffer); } void config_warn_duplicate(const char *filename, int line, const char *entry) { config_warn("%s:%d: Duplicate %s directive", filename, line, entry); } /* returns 1 if the test fails */ int config_test_openfile(ConfigEntry *cep, int flags, mode_t mode, const char *entry, int fatal, int allow_url) { int fd; if(!cep->ce_vardata) { if(fatal) config_error("%s:%i: %s: : no file specified", cep->ce_fileptr->cf_filename, cep->ce_varlinenum, entry); else config_warn("%s:%i: %s: : no file specified", cep->ce_fileptr->cf_filename, cep->ce_varlinenum, entry); return 1; } /* There's not much checking that can be done for asynchronously downloaded files */ #ifdef USE_LIBCURL if(url_is_valid(cep->ce_vardata)) { if(allow_url) return 0; /* but we can check if a URL is used wrongly :-) */ config_warn("%s:%i: %s: %s: URL used where not allowed", cep->ce_fileptr->cf_filename, cep->ce_varlinenum, entry, cep->ce_vardata); if(fatal) return 1; else return 0; } #endif /* USE_LIBCURL */ /* * Make sure that files are created with the correct mode. This is * because we don't feel like unlink()ing them...which would require * stat()ing them to make sure that we don't delete existing ones * and that we deal with all of the bugs that come with complexity. * The only files we may be creating are the tunefile and pidfile so far. */ if(flags & O_CREAT) fd = open(cep->ce_vardata, flags, mode); else fd = open(cep->ce_vardata, flags); if(fd == -1) { if(fatal) config_error("%s:%i: %s: %s: %s", cep->ce_fileptr->cf_filename, cep->ce_varlinenum, entry, cep->ce_vardata, strerror(errno)); else config_warn("%s:%i: %s: %s: %s", cep->ce_fileptr->cf_filename, cep->ce_varlinenum, entry, cep->ce_vardata, strerror(errno)); return 1; } close(fd); return 0; } void config_progress(char *format, ...) { va_list ap; char buffer[1024]; char *ptr; va_start(ap, format); vsnprintf(buffer, 1023, format, ap); va_end(ap); if ((ptr = strchr(buffer, '\n')) != NULL) *ptr = '\0'; #ifdef _WIN32 if (!loop.ircd_booted) win_log("* %s", buffer); #endif sendto_realops("%s", buffer); } int config_is_blankorempty(ConfigEntry *cep, const char *block) { if (!cep->ce_varname) { config_error_blank(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, block); return 1; } if (!cep->ce_vardata) { config_error_empty(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, block, cep->ce_varname); return 1; } return 0; } ConfigCommand *config_binary_search(char *cmd) { int start = 0; int stop = ARRAY_SIZEOF(_ConfigCommands)-1; int mid; while (start <= stop) { mid = (start+stop)/2; if (smycmp(cmd,_ConfigCommands[mid].name) < 0) { stop = mid-1; } else if (strcmp(cmd,_ConfigCommands[mid].name) == 0) { return &_ConfigCommands[mid]; } else start = mid+1; } return NULL; } void free_iConf(aConfiguration *i) { safefree(i->kline_address); safefree(i->gline_address); safefree(i->auto_join_chans); safefree(i->oper_auto_join_chans); safefree(i->oper_only_stats); safefree(i->channel_command_prefix); safefree(i->oper_snomask); safefree(i->user_snomask); safefree(i->egd_path); safefree(i->static_quit); safefree(i->x_server_cert_pem); safefree(i->x_server_key_pem); safefree(i->x_server_cipher_list); safefree(i->trusted_ca_file); safefree(i->restrict_usermodes); safefree(i->restrict_channelmodes); safefree(i->restrict_extendedbans); safefree(i->network.x_ircnetwork); safefree(i->network.x_ircnet005); safefree(i->network.x_defserv); safefree(i->network.x_services_name); safefree(i->network.x_hidden_host); safefree(i->network.x_prefix_quit); safefree(i->network.x_helpchan); safefree(i->network.x_stats_server); safefree(i->spamfilter_ban_reason); safefree(i->spamfilter_virus_help_channel); safefree(i->spamexcept_line); safefree(i->timesynch_server); safefree(i->link_bindip); } int config_test(); void config_setdefaultsettings(aConfiguration *i) { char tmp[512]; i->unknown_flood_amount = 4; i->unknown_flood_bantime = 600; i->oper_snomask = strdup(SNO_DEFOPER); i->ident_read_timeout = 30; i->ident_connect_timeout = 3; i->nick_count = 3; i->nick_period = 60; /* nickflood protection: max 3 per 60s */ #ifdef NO_FLOOD_AWAY i->away_count = 4; i->away_period = 120; /* awayflood protection: max 4 per 120s */ #endif i->throttle_count = 3; i->throttle_period = 60; /* throttle protection: max 3 per 60s */ i->modef_default_unsettime = 0; i->modef_max_unsettime = 60; /* 1 hour seems enough :p */ i->ban_version_tkl_time = 86400; /* 1d */ i->spamfilter_ban_time = 86400; /* 1d */ i->spamfilter_ban_reason = strdup("Spam/advertising"); i->spamfilter_virus_help_channel = strdup("#help"); i->spamfilter_detectslow_warn = 250; i->spamfilter_detectslow_fatal = 500; i->spamfilter_stop_on_first_match = 1; i->maxdccallow = 10; i->channel_command_prefix = strdup("`!."); i->check_target_nick_bans = 1; i->maxbans = 60; i->maxbanlength = 2048; i->timesynch_enabled = 1; i->timesynch_timeout = 3; i->timesynch_server = strdup("193.67.79.202,192.43.244.18,128.250.36.3"); /* nlnet (EU), NIST (US), uni melbourne (AU). All open acces, nonotify, nodns. */ i->level_on_join = CHFL_CHANOP; i->watch_away_notification = 1; i->new_linking_protocol = 1; i->uhnames = 1; i->ping_cookie = 1; i->default_ipv6_clone_mask = 64; i->nicklen = NICKLEN; i->link_bindip = strdup("*"); i->oper_only_stats = strdup("*"); snprintf(tmp, sizeof(tmp), "%s/ssl/server.cert.pem", CONFDIR); i->x_server_cert_pem = strdup(tmp); snprintf(tmp, sizeof(tmp), "%s/ssl/server.key.pem", CONFDIR); i->x_server_key_pem = strdup(tmp); if (!ipv6_capable()) DISABLE_IPV6 = 1; } /* 1: needed for set::options::allow-part-if-shunned, * we can't just make it M_SHUN and do a ALLOW_PART_IF_SHUNNED in * m_part itself because that will also block internal calls (like sapart). -- Syzop * 2: now also used by spamfilter entries added by config... * we got a chicken-and-egg problem here.. antries added without reason or ban-time * field should use the config default (set::spamfilter::ban-reason/ban-time) but * this isn't (or might not) be known yet when parsing spamfilter entries.. * so we do a VERY UGLY mass replace here.. unless someone else has a better idea. */ static void do_weird_shun_stuff() { aCommand *cmptr; aTKline *tk; char *encoded; if ((cmptr = find_Command_simple("PART"))) { if (ALLOW_PART_IF_SHUNNED) cmptr->flags |= M_SHUN; else cmptr->flags &= ~M_SHUN; } encoded = unreal_encodespace(SPAMFILTER_BAN_REASON); if (!encoded) abort(); /* hack to trace 'impossible' bug... */ for (tk = tklines[tkl_hash('q')]; tk; tk = tk->next) { if (tk->type != TKL_NICK) continue; if (!tk->setby) { if (me.name[0] != '\0') tk->setby = strdup(me.name); else tk->setby = strdup(conf_me->name ? conf_me->name : "~server~"); } } for (tk = tklines[tkl_hash('f')]; tk; tk = tk->next) { if (tk->type != TKL_SPAMF) continue; /* global entry or something else.. */ if (!strcmp(tk->ptr.spamf->tkl_reason, "")) { MyFree(tk->ptr.spamf->tkl_reason); tk->ptr.spamf->tkl_reason = strdup(encoded); tk->ptr.spamf->tkl_duration = SPAMFILTER_BAN_TIME; } /* This one is even more ugly, but our config crap is VERY confusing :[ */ if (!tk->setby) { if (me.name[0] != '\0') tk->setby = strdup(me.name); else tk->setby = strdup(conf_me->name ? conf_me->name : "~server~"); } } if (loop.ircd_booted) /* only has to be done for rehashes, api-isupport takes care of boot */ { if (WATCH_AWAY_NOTIFICATION) { IsupportAdd(NULL, "WATCHOPTS", "A"); } else { Isupport *hunted = IsupportFind("WATCHOPTS"); if (hunted) IsupportDel(hunted); } if (UHNAMES_ENABLED) { IsupportAdd(NULL, "UHNAMES", NULL); } else { Isupport *hunted = IsupportFind("UHNAMES"); if (hunted) IsupportDel(hunted); } } } static void make_default_logblock(void) { ConfigItem_log *ca = MyMallocEx(sizeof(ConfigItem_log)); config_status("No log { } block found -- using default: errors will be logged to 'ircd.log'"); ca->file = strdup("ircd.log"); ca->flags |= LOG_ERROR; ca->logfd = -1; AddListItem(ca, conf_log); } int isanyserverlinked(void) { return !list_empty(&server_list); } void applymeblock(void) { if (!conf_me) return; /* uh-huh? */ /* Info text may always change, just wouldn't show up on other servers, that's all.. */ strlcpy(me.info, conf_me->info, sizeof(me.info)); /* Name can only be set once (on boot) */ if (!*me.name) strlcpy(me.name, conf_me->name, sizeof(me.name)); if (!*me.id) strlcpy(me.id, conf_me->sid, sizeof(me.id)); } void upgrade_conf_to_34(void) { config_error("******************************************************************"); config_error("This *seems* an UnrealIRCd 3.2.x configuration file."); #ifdef _WIN32 if (!IsService) config_error("In next screen you will be prompted to automatically upgrade the configuration file(s)."); else { config_error("We offer a configuration file converter to convert 3.2.x conf's to 3.4.x, however this " "is not available when running as a service. If you want to use it, make UnrealIRCd " "run in GUI mode by running 'unreal uninstall'. Then start wircd.exe and when " "it prompts you to convert the configuration click 'Yes'. Check if UnrealIRCd boots properly. " "Once everything is looking good you can run 'unreal install' to make UnrealIRCd run " "as a service again."); /* TODO: make this unnecessary :D */ } #endif config_error("To upgrade it to the new 3.4.x format, run: ./unrealircd upgrade-conf"); config_error("******************************************************************"); /* TODO: win32 may require a different error */ } int init_conf(char *rootconf, int rehash) { char *old_pid_file = NULL; config_status("Loading IRCd configuration.."); if (conf) { config_error("%s:%i - Someone forgot to clean up", __FILE__, __LINE__); return -1; } bzero(&tempiConf, sizeof(iConf)); bzero(&settings, sizeof(settings)); bzero(&requiredstuff, sizeof(requiredstuff)); config_setdefaultsettings(&tempiConf); /* * the rootconf must be listed in the conf_include for include * recursion prevention code and sanity checking code to be * made happy :-). Think of it as us implicitly making an * in-memory config file that looks like: * * include "unrealircd.conf"; */ add_include(rootconf, "[thin air]", -1); if (load_conf(rootconf, rootconf) > 0) { charsys_reset_pretest(); if ((config_test() < 0) || (callbacks_check() < 0) || (efunctions_check() < 0) || (charsys_postconftest() < 0) || ssl_used_in_config_but_unavail() || reloadable_perm_module_unloaded()) { config_error("IRCd configuration failed to pass testing"); #ifdef _WIN32 if (!rehash) win_error(); #endif Unload_all_testing_modules(); unload_notloaded_includes(); config_free(conf); conf = NULL; free_iConf(&tempiConf); return -1; } callbacks_switchover(); efunctions_switchover(); if (rehash) { Hook *h; safestrdup(old_pid_file, conf_files->pid_file); unrealdns_delasyncconnects(); config_rehash(); Unload_all_loaded_modules(); /* Notify permanent modules of the rehash */ for (h = Hooks[HOOKTYPE_REHASH]; h; h = h->next) { if (!h->owner) continue; if (!(h->owner->options & MOD_OPT_PERM)) continue; (*(h->func.intfunc))(); } unload_loaded_includes(); } load_includes(); Init_all_testing_modules(); charsys_reset(); if (config_run() < 0) { config_error("Bad case of config errors. Server will now die. This really shouldn't happen"); #ifdef _WIN32 if (!rehash) win_error(); #endif abort(); } charsys_finish(); applymeblock(); if (old_pid_file && strcmp(old_pid_file, conf_files->pid_file)) { sendto_ops("pidfile is being rewritten to %s, please delete %s", conf_files->pid_file, old_pid_file); write_pidfile(); } safefree(old_pid_file); } else { config_error("IRCd configuration failed to load"); Unload_all_testing_modules(); unload_notloaded_includes(); config_free(conf); conf = NULL; free_iConf(&tempiConf); #ifdef _WIN32 if (!rehash) win_error(); #endif return -1; } config_free(conf); conf = NULL; if (rehash) { module_loadall(); RunHook0(HOOKTYPE_REHASH_COMPLETE); } do_weird_shun_stuff(); if (!conf_log) make_default_logblock(); config_status("Configuration loaded without any problems."); return 0; } /** * Processes filename as part of the IRCd's configuration. * * One _must_ call add_include() or add_remote_include() before * calling load_conf(). This way, include recursion may be detected * and reported to the user as an error instead of causing the IRCd to * hang in an infinite recursion, eat up memory, and eventually * overflow its stack ;-). (reported by warg). * * This function will set INCLUDE_USED on the config_include list * entry if the config file loaded without error. * * @param filename the file where the conf may be read from * @param original_path the path or URL used to refer to this file. * (mostly to support remote includes' URIs for recursive include detection). * @return 1 on success, a negative number on error */ int load_conf(char *filename, const char *original_path) { ConfigFile *cfptr, *cfptr2, **cfptr3; ConfigEntry *ce; ConfigItem_include *inc, *my_inc; int ret; int fatal_ret; int counter; if (config_verbose > 0) config_status("Loading config file %s ..", filename); need_34_upgrade = 0; /* * Check if we're accidentally including a file a second * time. We should expect to find one entry in this list: the * entry for our current file. */ counter = 0; my_inc = NULL; for (inc = conf_include; inc; inc = (ConfigItem_include *)inc->next) { /* * ignore files which were part of a _previous_ * successful rehash. */ if (!(inc->flag.type & INCLUDE_NOTLOADED)) continue; if (!counter) my_inc = inc; if (!strcmp(filename, inc->file)) { counter ++; continue; } #ifdef _WIN32 if (!stricmp(filename, inc->file)) { counter ++; continue; } #endif #ifdef USE_LIBCURL if (inc->url && !strcmp(original_path, inc->url)) { counter ++; continue; } #endif } if (counter < 1 || !my_inc) { /* * The following is simply for debugging/[sanity * checking]. To make sure that functions call * add_include() or add_remote_include() before * calling us. */ config_error("I don't have a record for %s being included." " Perhaps someone forgot to call add_include()?", filename); abort(); } if (counter > 1 || my_inc->flag.type & INCLUDE_USED) { config_error("%s:%d:include: Config file %s has been loaded before %d time." " You may include each file only once.", my_inc->included_from, my_inc->included_from_line, filename, counter - 1); return -1; } /* end include recursion checking code */ if ((cfptr = config_load(filename))) { for (cfptr3 = &conf, cfptr2 = conf; cfptr2; cfptr2 = cfptr2->cf_next) cfptr3 = &cfptr2->cf_next; *cfptr3 = cfptr; /* Load modules */ if (config_verbose > 1) config_status("Loading modules in %s", filename); fatal_ret = 0; for (ce = cfptr->cf_entries; ce; ce = ce->ce_next) if (!strcmp(ce->ce_varname, "loadmodule")) { ret = _conf_loadmodule(cfptr, ce); if (ret < fatal_ret) fatal_ret = ret; /* lowest wins */ } ret = fatal_ret; if (need_34_upgrade) upgrade_conf_to_34(); if (ret < 0) return ret; /* Load includes */ if (config_verbose > 1) config_status("Searching through %s for include files..", filename); for (ce = cfptr->cf_entries; ce; ce = ce->ce_next) if (!strcmp(ce->ce_varname, "include")) { ret = _conf_include(cfptr, ce); if (need_34_upgrade) upgrade_conf_to_34(); if (ret < 0) return ret; } my_inc->flag.type |= INCLUDE_USED; return 1; } else { config_error("Could not load config file %s", filename); #ifdef _WIN32 if (!strcmp(filename, "conf/unrealircd.conf")) { if (file_exists("unrealircd.conf")) { config_error("Note that 'unrealircd.conf' now belongs in the 'conf' subdirectory! (So move it to there)"); } else { config_error("New to UnrealIRCd? Be sure to read https://www.unrealircd.org/docs/Installing_%28Windows%29"); } } #endif return -1; } } void config_rehash() { ConfigItem_oper *oper_ptr; ConfigItem_class *class_ptr; ConfigItem_ulines *uline_ptr; ConfigItem_allow *allow_ptr; ConfigItem_except *except_ptr; ConfigItem_ban *ban_ptr; ConfigItem_link *link_ptr; ConfigItem_listen *listen_ptr; ConfigItem_tld *tld_ptr; ConfigItem_vhost *vhost_ptr; ConfigItem_deny_dcc *deny_dcc_ptr; ConfigItem_allow_dcc *allow_dcc_ptr; ConfigItem_deny_link *deny_link_ptr; ConfigItem_deny_channel *deny_channel_ptr; ConfigItem_allow_channel *allow_channel_ptr; ConfigItem_admin *admin_ptr; ConfigItem_deny_version *deny_version_ptr; ConfigItem_log *log_ptr; ConfigItem_alias *alias_ptr; ConfigItem_help *help_ptr; ConfigItem_offchans *of_ptr; OperStat *os_ptr; ListStruct *next, *next2; aTKline *tk, *tk_next; SpamExcept *spamex_ptr; int i; USE_BAN_VERSION = 0; for (admin_ptr = conf_admin; admin_ptr; admin_ptr = (ConfigItem_admin *)next) { next = (ListStruct *)admin_ptr->next; safefree(admin_ptr->line); DelListItem(admin_ptr, conf_admin); MyFree(admin_ptr); } for (oper_ptr = conf_oper; oper_ptr; oper_ptr = (ConfigItem_oper *)next) { ConfigItem_mask *oper_mask; SWhois *s, *s_next; next = (ListStruct *)oper_ptr->next; safefree(oper_ptr->name); safefree(oper_ptr->snomask); safefree(oper_ptr->operclass); safefree(oper_ptr->vhost); Auth_DeleteAuthStruct(oper_ptr->auth); unreal_delete_masks(oper_ptr->mask); DelListItem(oper_ptr, conf_oper); for (s = oper_ptr->swhois; s; s = s_next) { s_next = s->next; safefree(s->line); safefree(s->setby); MyFree(s); } MyFree(oper_ptr); } for (link_ptr = conf_link; link_ptr; link_ptr = (ConfigItem_link *) next) { next = (ListStruct *)link_ptr->next; if (link_ptr->refcount == 0) { Debug((DEBUG_ERROR, "s_conf: deleting block %s (refcount 0)", link_ptr->servername)); delete_linkblock(link_ptr); } else { Debug((DEBUG_ERROR, "s_conf: marking block %s (refcount %d) as temporary", link_ptr->servername, link_ptr->refcount)); link_ptr->flag.temporary = 1; } } for (class_ptr = conf_class; class_ptr; class_ptr = (ConfigItem_class *) next) { next = (ListStruct *)class_ptr->next; if (class_ptr->flag.permanent == 1) continue; class_ptr->flag.temporary = 1; /* We'll wipe it out when it has no clients */ if (!class_ptr->clients && !class_ptr->xrefcount) { delete_classblock(class_ptr); } } for (uline_ptr = conf_ulines; uline_ptr; uline_ptr = (ConfigItem_ulines *) next) { next = (ListStruct *)uline_ptr->next; /* We'll wipe it out when it has no clients */ safefree(uline_ptr->servername); DelListItem(uline_ptr, conf_ulines); MyFree(uline_ptr); } for (allow_ptr = conf_allow; allow_ptr; allow_ptr = (ConfigItem_allow *) next) { next = (ListStruct *)allow_ptr->next; safefree(allow_ptr->ip); safefree(allow_ptr->hostname); Auth_DeleteAuthStruct(allow_ptr->auth); DelListItem(allow_ptr, conf_allow); MyFree(allow_ptr); } for (except_ptr = conf_except; except_ptr; except_ptr = (ConfigItem_except *) next) { next = (ListStruct *)except_ptr->next; safefree(except_ptr->mask); DelListItem(except_ptr, conf_except); MyFree(except_ptr); } for (ban_ptr = conf_ban; ban_ptr; ban_ptr = (ConfigItem_ban *) next) { next = (ListStruct *)ban_ptr->next; if (ban_ptr->flag.type2 == CONF_BAN_TYPE_CONF || ban_ptr->flag.type2 == CONF_BAN_TYPE_TEMPORARY) { safefree(ban_ptr->mask); safefree(ban_ptr->reason); DelListItem(ban_ptr, conf_ban); MyFree(ban_ptr); } } for (listen_ptr = conf_listen; listen_ptr; listen_ptr = (ConfigItem_listen *)listen_ptr->next) { listen_ptr->flag.temporary = 1; } for (tld_ptr = conf_tld; tld_ptr; tld_ptr = (ConfigItem_tld *) next) { next = (ListStruct *)tld_ptr->next; safefree(tld_ptr->motd_file); safefree(tld_ptr->rules_file); safefree(tld_ptr->smotd_file); safefree(tld_ptr->opermotd_file); safefree(tld_ptr->botmotd_file); free_motd(&tld_ptr->motd); free_motd(&tld_ptr->rules); free_motd(&tld_ptr->smotd); free_motd(&tld_ptr->opermotd); free_motd(&tld_ptr->botmotd); DelListItem(tld_ptr, conf_tld); MyFree(tld_ptr); } for (vhost_ptr = conf_vhost; vhost_ptr; vhost_ptr = (ConfigItem_vhost *) next) { ConfigItem_mask *vhost_mask; SWhois *s, *s_next; next = (ListStruct *)vhost_ptr->next; safefree(vhost_ptr->login); Auth_DeleteAuthStruct(vhost_ptr->auth); safefree(vhost_ptr->virthost); safefree(vhost_ptr->virtuser); unreal_delete_masks(vhost_ptr->mask); for (s = vhost_ptr->swhois; s; s = s_next) { s_next = s->next; safefree(s->line); safefree(s->setby); MyFree(s); } DelListItem(vhost_ptr, conf_vhost); MyFree(vhost_ptr); } /* Clean up local spamfilter entries... */ for (tk = tklines[tkl_hash('f')]; tk; tk = tk_next) { if (tk->type == TKL_SPAMF) tk_next = tkl_del_line(tk); else /* global spamfilter.. don't touch! */ tk_next = tk->next; } for (tk = tklines[tkl_hash('q')]; tk; tk = tk_next) { if (tk->type == TKL_NICK) tk_next = tkl_del_line(tk); else tk_next = tk->next; } for (deny_dcc_ptr = conf_deny_dcc; deny_dcc_ptr; deny_dcc_ptr = (ConfigItem_deny_dcc *)next) { next = (ListStruct *)deny_dcc_ptr->next; if (deny_dcc_ptr->flag.type2 == CONF_BAN_TYPE_CONF) { safefree(deny_dcc_ptr->filename); safefree(deny_dcc_ptr->reason); DelListItem(deny_dcc_ptr, conf_deny_dcc); MyFree(deny_dcc_ptr); } } for (deny_link_ptr = conf_deny_link; deny_link_ptr; deny_link_ptr = (ConfigItem_deny_link *) next) { next = (ListStruct *)deny_link_ptr->next; safefree(deny_link_ptr->prettyrule); safefree(deny_link_ptr->mask); crule_free(&deny_link_ptr->rule); DelListItem(deny_link_ptr, conf_deny_link); MyFree(deny_link_ptr); } for (deny_version_ptr = conf_deny_version; deny_version_ptr; deny_version_ptr = (ConfigItem_deny_version *) next) { next = (ListStruct *)deny_version_ptr->next; safefree(deny_version_ptr->mask); safefree(deny_version_ptr->version); safefree(deny_version_ptr->flags); DelListItem(deny_version_ptr, conf_deny_version); MyFree(deny_version_ptr); } for (deny_channel_ptr = conf_deny_channel; deny_channel_ptr; deny_channel_ptr = (ConfigItem_deny_channel *) next) { next = (ListStruct *)deny_channel_ptr->next; safefree(deny_channel_ptr->redirect); safefree(deny_channel_ptr->channel); safefree(deny_channel_ptr->reason); safefree(deny_channel_ptr->class); DelListItem(deny_channel_ptr, conf_deny_channel); MyFree(deny_channel_ptr); } for (allow_channel_ptr = conf_allow_channel; allow_channel_ptr; allow_channel_ptr = (ConfigItem_allow_channel *) next) { next = (ListStruct *)allow_channel_ptr->next; safefree(allow_channel_ptr->channel); safefree(allow_channel_ptr->class); DelListItem(allow_channel_ptr, conf_allow_channel); MyFree(allow_channel_ptr); } for (allow_dcc_ptr = conf_allow_dcc; allow_dcc_ptr; allow_dcc_ptr = (ConfigItem_allow_dcc *)next) { next = (ListStruct *)allow_dcc_ptr->next; if (allow_dcc_ptr->flag.type2 == CONF_BAN_TYPE_CONF) { safefree(allow_dcc_ptr->filename); DelListItem(allow_dcc_ptr, conf_allow_dcc); MyFree(allow_dcc_ptr); } } if (conf_drpass) { Auth_DeleteAuthStruct(conf_drpass->restartauth); conf_drpass->restartauth = NULL; Auth_DeleteAuthStruct(conf_drpass->dieauth); conf_drpass->dieauth = NULL; safefree(conf_drpass); } for (log_ptr = conf_log; log_ptr; log_ptr = (ConfigItem_log *)next) { next = (ListStruct *)log_ptr->next; if (log_ptr->logfd != -1) fd_close(log_ptr->logfd); safefree(log_ptr->file); DelListItem(log_ptr, conf_log); MyFree(log_ptr); } for (alias_ptr = conf_alias; alias_ptr; alias_ptr = (ConfigItem_alias *)next) { aCommand *cmptr = find_Command(alias_ptr->alias, 0, 0); ConfigItem_alias_format *fmt; next = (ListStruct *)alias_ptr->next; safefree(alias_ptr->nick); if (cmptr) del_Command(alias_ptr->alias, cmptr->func); safefree(alias_ptr->alias); if (alias_ptr->format && (alias_ptr->type == ALIAS_COMMAND)) { for (fmt = (ConfigItem_alias_format *) alias_ptr->format; fmt; fmt = (ConfigItem_alias_format *) next2) { next2 = (ListStruct *)fmt->next; safefree(fmt->format); safefree(fmt->nick); safefree(fmt->parameters); unreal_delete_match(fmt->expr); DelListItem(fmt, alias_ptr->format); MyFree(fmt); } } DelListItem(alias_ptr, conf_alias); MyFree(alias_ptr); } for (help_ptr = conf_help; help_ptr; help_ptr = (ConfigItem_help *)next) { aMotdLine *text; next = (ListStruct *)help_ptr->next; safefree(help_ptr->command); while (help_ptr->text) { text = help_ptr->text->next; safefree(help_ptr->text->line); safefree(help_ptr->text); help_ptr->text = text; } DelListItem(help_ptr, conf_help); MyFree(help_ptr); } for (os_ptr = iConf.oper_only_stats_ext; os_ptr; os_ptr = (OperStat *)next) { next = (ListStruct *)os_ptr->next; safefree(os_ptr->flag); MyFree(os_ptr); } iConf.oper_only_stats_ext = NULL; for (spamex_ptr = iConf.spamexcept; spamex_ptr; spamex_ptr = (SpamExcept *)next) { next = (ListStruct *)spamex_ptr->next; MyFree(spamex_ptr); } iConf.spamexcept = NULL; for (of_ptr = conf_offchans; of_ptr; of_ptr = (ConfigItem_offchans *)next) { next = (ListStruct *)of_ptr->next; safefree(of_ptr->topic); MyFree(of_ptr); } conf_offchans = NULL; for (i = 0; i < EXTCMODETABLESZ; i++) { if (iConf.modes_on_join.extparams[i]) free(iConf.modes_on_join.extparams[i]); } /* reset conf_files -- should this be in its own function? no, because it's only used here */ safefree(conf_files->motd_file); safefree(conf_files->smotd_file); safefree(conf_files->opermotd_file); safefree(conf_files->svsmotd_file); safefree(conf_files->botmotd_file); safefree(conf_files->rules_file); safefree(conf_files->pid_file); safefree(conf_files->tune_file); /* Don't free conf_files->pid_file here; the old value is used to determine if the pidfile location has changed and write_pidfile() needs to be called again. */ safefree(conf_files); conf_files = NULL; } int config_post_test() { #define Error(x) { config_error((x)); errors++; } int errors = 0; Hook *h; if (!requiredstuff.conf_me) Error("me {} block is missing"); if (!requiredstuff.conf_admin) Error("admin {} block is missing"); if (!requiredstuff.conf_listen) Error("listen {} block is missing"); if (!settings.has_kline_address) Error("set::kline-address is missing"); if (!settings.has_maxchannelsperuser) Error("set::maxchannelsperuser is missing"); if (!settings.has_services_server) Error("set::services-server is missing"); if (!settings.has_default_server) Error("set::default-server is missing"); if (!settings.has_network_name) Error("set::network-name is missing"); if (!settings.has_help_channel) Error("set::help-channel is missing"); if (!settings.has_hiddenhost_prefix) Error("set::hiddenhost-prefix is missing"); for (h = Hooks[HOOKTYPE_CONFIGPOSTTEST]; h; h = h->next) { int value, errs = 0; if (h->owner && !(h->owner->flags & MODFLAG_TESTING) && !(h->owner->options & MOD_OPT_PERM)) continue; value = (*(h->func.intfunc))(&errs); if (value == -1) { errors += errs; break; } if (value == -2) errors += errs; } return errors; } int config_run() { ConfigEntry *ce; ConfigFile *cfptr; ConfigCommand *cc; int errors = 0; Hook *h; ConfigItem_allow *allow; for (cfptr = conf; cfptr; cfptr = cfptr->cf_next) { if (config_verbose > 1) config_status("Running %s", cfptr->cf_filename); for (ce = cfptr->cf_entries; ce; ce = ce->ce_next) { if ((cc = config_binary_search(ce->ce_varname))) { if ((cc->conffunc) && (cc->conffunc(cfptr, ce) < 0)) errors++; } else { int value; for (h = Hooks[HOOKTYPE_CONFIGRUN]; h; h = h->next) { value = (*(h->func.intfunc))(cfptr,ce,CONFIG_MAIN); if (value == 1) break; } } } } /* * transfer default values from set::ipv6_clones_mask into * each individual allow block. If other similar things like * this stack up here, perhaps this shoul be moved to another * function. */ for(allow = conf_allow; allow; allow = (ConfigItem_allow *)allow->next) if(!allow->ipv6_clone_mask) allow->ipv6_clone_mask = tempiConf.default_ipv6_clone_mask; close_listeners(); listen_cleanup(); close_listeners(); loop.do_bancheck = 1; free_iConf(&iConf); bcopy(&tempiConf, &iConf, sizeof(aConfiguration)); bzero(&tempiConf, sizeof(aConfiguration)); { EventInfo eInfo; long v; eInfo.flags = EMOD_EVERY; if (!THROTTLING_PERIOD) v = 120; else { v = THROTTLING_PERIOD/2; if (v > 5) v = 5; /* accuracy, please */ } eInfo.every = v; EventMod(EventFind("bucketcleaning"), &eInfo); } /* initialize conf_files with defaults if the block isn't set: */ if(!conf_files) _conf_files(NULL, NULL); if (errors > 0) { config_error("%i fatal errors encountered", errors); } return (errors > 0 ? -1 : 1); } NameValue *config_binary_flags_search(NameValue *table, char *cmd, int size) { int start = 0; int stop = size-1; int mid; while (start <= stop) { mid = (start+stop)/2; if (smycmp(cmd,table[mid].name) < 0) { stop = mid-1; } else if (strcmp(cmd,table[mid].name) == 0) { return &(table[mid]); } else start = mid+1; } return NULL; } int config_test() { ConfigEntry *ce; ConfigFile *cfptr; ConfigCommand *cc; int errors = 0; Hook *h; need_34_upgrade = 0; for (cfptr = conf; cfptr; cfptr = cfptr->cf_next) { if (config_verbose > 1) config_status("Testing %s", cfptr->cf_filename); for (ce = cfptr->cf_entries; ce; ce = ce->ce_next) { if (!ce->ce_varname) { config_error("%s:%i: %s:%i: null ce->ce_varname", ce->ce_fileptr->cf_filename, ce->ce_varlinenum, __FILE__, __LINE__); return -1; } if ((cc = config_binary_search(ce->ce_varname))) { if (cc->testfunc) errors += (cc->testfunc(cfptr, ce)); } else { int used = 0; for (h = Hooks[HOOKTYPE_CONFIGTEST]; h; h = h->next) { int value, errs = 0; if (h->owner && !(h->owner->flags & MODFLAG_TESTING) && !(h->owner->options & MOD_OPT_PERM)) continue; value = (*(h->func.intfunc))(cfptr,ce,CONFIG_MAIN,&errs); if (value == 2) used = 1; if (value == 1) { used = 1; break; } if (value == -1) { used = 1; errors += errs; break; } if (value == -2) { used = 1; errors += errs; } } if (!used) config_status("%s:%i: unknown directive %s", ce->ce_fileptr->cf_filename, ce->ce_varlinenum, ce->ce_varname); } } } errors += config_post_test(); if (errors > 0) { config_error("%i errors encountered", errors); } if (need_34_upgrade) { upgrade_conf_to_34(); } return (errors > 0 ? -1 : 1); } /* * Service functions */ ConfigItem_deny_dcc *Find_deny_dcc(char *name) { ConfigItem_deny_dcc *p; if (!name) return NULL; for (p = conf_deny_dcc; p; p = (ConfigItem_deny_dcc *) p->next) { if (!match(name, p->filename)) return (p); } return NULL; } ConfigItem_alias *Find_alias(char *name) { ConfigItem_alias *alias; if (!name) return NULL; for (alias = conf_alias; alias; alias = (ConfigItem_alias *)alias->next) { if (!stricmp(alias->alias, name)) return alias; } return NULL; } ConfigItem_class *Find_class(char *name) { ConfigItem_class *p; if (!name) return NULL; for (p = conf_class; p; p = (ConfigItem_class *) p->next) { if (!strcmp(name, p->name)) return (p); } return NULL; } ConfigItem_oper *Find_oper(char *name) { ConfigItem_oper *p; if (!name) return NULL; for (p = conf_oper; p; p = (ConfigItem_oper *) p->next) { if (!strcmp(name, p->name)) return (p); } return NULL; } ConfigItem_operclass *Find_operclass(char *name) { ConfigItem_operclass *p; if (!name) return NULL; for (p = conf_operclass; p; p= (ConfigItem_operclass *) p->next) { if (!strcmp(name,p->classStruct->name)) return (p); } return NULL; } int count_oper_sessions(char *name) { int count = 0; aClient *cptr; list_for_each_entry(cptr, &oper_list, special_node) { if (cptr->user->operlogin != NULL && !strcmp(cptr->user->operlogin, name)) count++; } return count; } ConfigItem_listen *Find_listen(char *ipmask, int port, int ipv6) { ConfigItem_listen *p; if (!ipmask) return NULL; for (p = conf_listen; p; p = (ConfigItem_listen *) p->next) { if (p->ipv6 != ipv6) continue; if (!match(p->ip, ipmask) && (port == p->port)) return (p); if (!match(ipmask, p->ip) && (port == p->port)) return (p); } return NULL; } ConfigItem_ulines *Find_uline(char *host) { ConfigItem_ulines *ulines; if (!host) return NULL; for(ulines = conf_ulines; ulines; ulines =(ConfigItem_ulines *) ulines->next) { if (!stricmp(host, ulines->servername)) return ulines; } return NULL; } ConfigItem_except *Find_except(aClient *sptr, short type) { ConfigItem_except *excepts; for(excepts = conf_except; excepts; excepts =(ConfigItem_except *) excepts->next) { if (excepts->flag.type == type) { if (match_user(excepts->mask, sptr, MATCH_CHECK_REAL)) return excepts; } } return NULL; } ConfigItem_tld *Find_tld(aClient *cptr) { ConfigItem_tld *tld; for (tld = conf_tld; tld; tld = (ConfigItem_tld *) tld->next) { if (match_user(tld->mask, cptr, MATCH_CHECK_REAL)) { if ((tld->options & TLD_SSL) && !IsSecure(cptr)) continue; if ((tld->options & TLD_REMOTE) && MyClient(cptr)) continue; return tld; } } return NULL; } ConfigItem_link *Find_link(char *servername, aClient *acptr) { ConfigItem_link *link; for (link = conf_link; link; link = (ConfigItem_link *)link->next) { if (!match(link->servername, servername) && unreal_mask_match(acptr, link->incoming.mask)) { return link; } } return NULL; } ConfigItem_ban *Find_ban(aClient *sptr, char *host, short type) { ConfigItem_ban *ban; /* Check for an except ONLY if we find a ban, makes it * faster since most users will not have a ban so excepts * don't need to be searched -- codemastr */ for (ban = conf_ban; ban; ban = (ConfigItem_ban *) ban->next) { if (ban->flag.type == type) { if (sptr) { if (match_user(ban->mask, sptr, MATCH_CHECK_REAL)) { /* Person got a exception */ if ((type == CONF_BAN_USER || type == CONF_BAN_IP) && Find_except(sptr, CONF_EXCEPT_BAN)) return NULL; return ban; } } else if (!match(ban->mask, host)) /* We don't worry about exceptions */ return ban; } } return NULL; } ConfigItem_ban *Find_banEx(aClient *sptr, char *host, short type, short type2) { ConfigItem_ban *ban; /* Check for an except ONLY if we find a ban, makes it * faster since most users will not have a ban so excepts * don't need to be searched -- codemastr */ for (ban = conf_ban; ban; ban = (ConfigItem_ban *) ban->next) { if ((ban->flag.type == type) && (ban->flag.type2 == type2)) { if (sptr) { if (match_user(ban->mask, sptr, MATCH_CHECK_REAL)) { /* Person got a exception */ if (Find_except(sptr, type)) return NULL; return ban; } } else if (!match(ban->mask, host)) /* We don't worry about exceptions */ return ban; } } return NULL; } int AllowClient(aClient *cptr, struct hostent *hp, char *sockhost, char *username) { ConfigItem_allow *aconf; char *hname; int i, ii = 0; static char uhost[HOSTLEN + USERLEN + 3]; static char fullname[HOSTLEN + 1]; for (aconf = conf_allow; aconf; aconf = (ConfigItem_allow *) aconf->next) { if (!aconf->hostname || !aconf->ip) goto attach; if (aconf->auth && !cptr->local->passwd && aconf->flags.nopasscont) continue; if (aconf->flags.ssl && !IsSecure(cptr)) continue; if (hp && hp->h_name) { hname = hp->h_name; strlcpy(fullname, hname, sizeof(fullname)); add_local_domain(fullname, HOSTLEN - strlen(fullname)); Debug((DEBUG_DNS, "a_il: %s->%s", sockhost, fullname)); if (index(aconf->hostname, '@')) { if (aconf->flags.noident) strlcpy(uhost, username, sizeof(uhost)); else strlcpy(uhost, cptr->username, sizeof(uhost)); strlcat(uhost, "@", sizeof(uhost)); } else *uhost = '\0'; strlcat(uhost, fullname, sizeof(uhost)); if (!match(aconf->hostname, uhost)) goto attach; } if (index(aconf->ip, '@')) { if (aconf->flags.noident) strlcpy(uhost, username, sizeof(uhost)); else strlcpy(uhost, cptr->username, sizeof(uhost)); (void)strlcat(uhost, "@", sizeof(uhost)); } else *uhost = '\0'; strlcat(uhost, sockhost, sizeof(uhost)); /* Check the IP */ if (match_user(aconf->ip, cptr, MATCH_CHECK_IP)) goto attach; /* Hmm, localhost is a special case, hp == NULL and sockhost contains * 'localhost' instead of an ip... -- Syzop. */ if (!strcmp(sockhost, "localhost")) { if (index(aconf->hostname, '@')) { if (aconf->flags.noident) strlcpy(uhost, username, sizeof(uhost)); else strlcpy(uhost, cptr->username, sizeof(uhost)); strlcat(uhost, "@localhost", sizeof(uhost)); } else strcpy(uhost, "localhost"); if (!match(aconf->hostname, uhost)) goto attach; } continue; attach: /* if (index(uhost, '@')) now flag based -- codemastr */ if (!aconf->flags.noident) cptr->flags |= FLAGS_DOID; if (!aconf->flags.useip && hp) strlcpy(uhost, fullname, sizeof(uhost)); else strlcpy(uhost, sockhost, sizeof(uhost)); set_sockhost(cptr, uhost); if (aconf->maxperip) { aClient *acptr, *acptr2; ii = 1; list_for_each_entry_safe(acptr, acptr2, &lclient_list, lclient_node) { if (!strcmp(GetIP(acptr), GetIP(cptr))) { ii++; if (ii > aconf->maxperip) { exit_client(cptr, cptr, &me, "Too many connections from your IP"); return -5; /* Already got too many with that ip# */ } } } } if ((i = Auth_Check(cptr, aconf->auth, cptr->local->passwd)) == -1) { exit_client(cptr, cptr, &me, "Password mismatch"); return -5; } if ((i == 2) && (cptr->local->passwd)) { MyFree(cptr->local->passwd); cptr->local->passwd = NULL; } if (!((aconf->class->clients + 1) > aconf->class->maxclients)) { cptr->local->class = aconf->class; cptr->local->class->clients++; } else { sendto_one(cptr, rpl_str(RPL_REDIR), me.name, cptr->name, aconf->server ? aconf->server : defserv, aconf->port ? aconf->port : 6667); return -3; } return 0; } return -1; } ConfigItem_vhost *Find_vhost(char *name) { ConfigItem_vhost *vhost; for (vhost = conf_vhost; vhost; vhost = (ConfigItem_vhost *)vhost->next) { if (!strcmp(name, vhost->login)) return vhost; } return NULL; } /** returns NULL if allowed and struct if denied */ ConfigItem_deny_channel *Find_channel_allowed(aClient *cptr, char *name) { ConfigItem_deny_channel *dchannel; ConfigItem_allow_channel *achannel; for (dchannel = conf_deny_channel; dchannel; dchannel = (ConfigItem_deny_channel *)dchannel->next) { if (!match(dchannel->channel, name) && (dchannel->class ? !strcmp(cptr->local->class->name, dchannel->class) : 1)) break; } if (dchannel) { for (achannel = conf_allow_channel; achannel; achannel = (ConfigItem_allow_channel *)achannel->next) { if (!match(achannel->channel, name) && (achannel->class ? !strcmp(cptr->local->class->name, achannel->class) : 1)) break; } if (achannel) return NULL; else return (dchannel); } return NULL; } void init_dynconf(void) { bzero(&iConf, sizeof(iConf)); bzero(&tempiConf, sizeof(iConf)); } char *pretty_time_val(long timeval) { static char buf[512]; if (timeval == 0) return "0"; buf[0] = 0; if (timeval/86400) snprintf(buf, sizeof(buf), "%ld day%s ", timeval/86400, timeval/86400 != 1 ? "s" : ""); if ((timeval/3600) % 24) snprintf(buf, sizeof(buf), "%s%ld hour%s ", buf, (timeval/3600)%24, (timeval/3600)%24 != 1 ? "s" : ""); if ((timeval/60)%60) snprintf(buf, sizeof(buf), "%s%ld minute%s ", buf, (timeval/60)%60, (timeval/60)%60 != 1 ? "s" : ""); if ((timeval%60)) snprintf(buf, sizeof(buf), "%s%ld second%s", buf, timeval%60, timeval%60 != 1 ? "s" : ""); return buf; } /* This converts a relative path to an absolute path, but only if necessary. */ void convert_to_absolute_path(char **path, char *reldir) { char *s; if (!*path || !**path) return; /* NULL or empty */ if (strstr(*path, "://")) return; /* URL: don't touch */ if ((**path == '/') || (**path == '\\')) return; /* already absolute path */ if (!strncmp(*path, reldir, strlen(reldir))) return; /* already contains reldir */ s = MyMallocEx(strlen(reldir) + strlen(*path) + 2); sprintf(s, "%s/%s", reldir, *path); /* safe, see line above */ MyFree(*path); *path = s; } /* * Actual config parser funcs */ int _conf_include(ConfigFile *conf, ConfigEntry *ce) { int ret = 0; #ifdef GLOBH glob_t files; int i; #elif defined(_WIN32) HANDLE hFind; WIN32_FIND_DATA FindData; char cPath[MAX_PATH], *cSlash = NULL, *path; #endif if (!ce->ce_vardata) { config_status("%s:%i: include: no filename given", ce->ce_fileptr->cf_filename, ce->ce_varlinenum); return -1; } if (!strcmp(ce->ce_vardata, "help.conf")) need_34_upgrade = 1; convert_to_absolute_path(&ce->ce_vardata, CONFDIR); #ifdef USE_LIBCURL if (url_is_valid(ce->ce_vardata)) return remote_include(ce); #endif #if !defined(_WIN32) && !defined(_AMIGA) && !defined(OSXTIGER) && DEFAULT_PERMISSIONS != 0 (void)chmod(ce->ce_vardata, DEFAULT_PERMISSIONS); #endif #ifdef GLOBH #if defined(__OpenBSD__) && defined(GLOB_LIMIT) glob(ce->ce_vardata, GLOB_NOSORT|GLOB_NOCHECK|GLOB_LIMIT, NULL, &files); #else glob(ce->ce_vardata, GLOB_NOSORT|GLOB_NOCHECK, NULL, &files); #endif if (!files.gl_pathc) { globfree(&files); config_status("%s:%i: include %s: invalid file given", ce->ce_fileptr->cf_filename, ce->ce_varlinenum, ce->ce_vardata); return -1; } for (i = 0; i < files.gl_pathc; i++) { add_include(files.gl_pathv[i], ce->ce_fileptr->cf_filename, ce->ce_varlinenum); ret = load_conf(files.gl_pathv[i], files.gl_pathv[i]); if (ret < 0) { globfree(&files); return ret; } } globfree(&files); #elif defined(_WIN32) bzero(cPath,MAX_PATH); if (strchr(ce->ce_vardata, '/') || strchr(ce->ce_vardata, '\\')) { strlcpy(cPath,ce->ce_vardata,MAX_PATH); cSlash=cPath+strlen(cPath); while(*cSlash != '\\' && *cSlash != '/' && cSlash > cPath) cSlash--; *(cSlash+1)=0; } if ( (hFind = FindFirstFile(ce->ce_vardata, &FindData)) == INVALID_HANDLE_VALUE ) { config_status("%s:%i: include %s: invalid file given", ce->ce_fileptr->cf_filename, ce->ce_varlinenum, ce->ce_vardata); return -1; } if (cPath) { path = MyMalloc(strlen(cPath) + strlen(FindData.cFileName)+1); strcpy(path, cPath); strcat(path, FindData.cFileName); add_include(path, ce->ce_fileptr->cf_filename, ce->ce_varlinenum); ret = load_conf(path, path); free(path); } else { add_include(FindData.cFileName, ce->ce_fileptr->cf_filename, ce->ce_varlinenum); ret = load_conf(FindData.cFileName, FindData.cFileName); } if (ret < 0) { FindClose(hFind); return ret; } ret = 0; while (FindNextFile(hFind, &FindData) != 0) { if (cPath) { path = MyMalloc(strlen(cPath) + strlen(FindData.cFileName)+1); strcpy(path,cPath); strcat(path,FindData.cFileName); add_include(path, ce->ce_fileptr->cf_filename, ce->ce_varlinenum); ret = load_conf(path, path); free(path); if (ret < 0) break; } else { add_include(FindData.cFileName, ce->ce_fileptr->cf_filename, ce->ce_varlinenum); ret = load_conf(FindData.cFileName, FindData.cFileName); } } FindClose(hFind); if (ret < 0) return ret; #else add_include(ce->ce_vardata, ce->ce_fileptr->cf_filename, ce->ce_varlinenum); ret = load_conf(ce->ce_vardata, ce->ce_vardata); return ret; #endif return 1; } int _test_include(ConfigFile *conf, ConfigEntry *ce) { return 0; } int _conf_admin(ConfigFile *conf, ConfigEntry *ce) { ConfigEntry *cep; ConfigItem_admin *ca; for (cep = ce->ce_entries; cep; cep = cep->ce_next) { ca = MyMallocEx(sizeof(ConfigItem_admin)); if (!conf_admin) conf_admin_tail = ca; safestrdup(ca->line, cep->ce_varname); AddListItem(ca, conf_admin); } return 1; } int _test_admin(ConfigFile *conf, ConfigEntry *ce) { ConfigEntry *cep; int errors = 0; if (requiredstuff.conf_admin) { config_warn_duplicate(ce->ce_fileptr->cf_filename, ce->ce_varlinenum, "admin"); return 0; } for (cep = ce->ce_entries; cep; cep = cep->ce_next) { if (!cep->ce_varname) { config_error("%s:%i: blank admin item", cep->ce_fileptr->cf_filename, cep->ce_varlinenum); errors++; continue; } } requiredstuff.conf_admin = 1; return errors; } int _conf_me(ConfigFile *conf, ConfigEntry *ce) { ConfigEntry *cep; if (!conf_me) conf_me = MyMallocEx(sizeof(ConfigItem_me)); for (cep = ce->ce_entries; cep; cep = cep->ce_next) { if (!strcmp(cep->ce_varname, "name")) { safestrdup(conf_me->name, cep->ce_vardata); } else if (!strcmp(cep->ce_varname, "info")) { safestrdup(conf_me->info, cep->ce_vardata); } else if (!strcmp(cep->ce_varname, "sid")) { safestrdup(conf_me->sid, cep->ce_vardata); } } return 1; } int _test_me(ConfigFile *conf, ConfigEntry *ce) { char has_name = 0, has_info = 0, has_sid = 0; ConfigEntry *cep; int errors = 0; if (requiredstuff.conf_me) { config_warn_duplicate(ce->ce_fileptr->cf_filename, ce->ce_varlinenum, "me"); return 0; } for (cep = ce->ce_entries; cep; cep = cep->ce_next) { if (config_is_blankorempty(cep, "me")) continue; /* me::name */ if (!strcmp(cep->ce_varname, "name")) { if (has_name) { config_warn_duplicate(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, "me::name"); continue; } has_name = 1; if (!strchr(cep->ce_vardata, '.')) { config_error("%s:%i: illegal me::name, must be fully qualified hostname", cep->ce_fileptr->cf_filename, cep->ce_varlinenum); errors++; } if (!valid_host(cep->ce_vardata)) { config_error("%s:%i: illegal me::name contains invalid character(s) [only a-z, 0-9, _, -, . are allowed]", cep->ce_fileptr->cf_filename, cep->ce_varlinenum); errors++; } if (strlen(cep->ce_vardata) > HOSTLEN) { config_error("%s:%i: illegal me::name, must be less or equal to %i characters", cep->ce_fileptr->cf_filename, cep->ce_varlinenum, HOSTLEN); errors++; } } /* me::info */ else if (!strcmp(cep->ce_varname, "info")) { char *p; char valid = 0; if (has_info) { config_warn_duplicate(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, "me::info"); continue; } has_info = 1; if (strlen(cep->ce_vardata) > (REALLEN-1)) { config_error("%s:%i: too long me::info, must be max. %i characters", cep->ce_fileptr->cf_filename, cep->ce_varlinenum, REALLEN-1); errors++; } /* Valid me::info? Any data except spaces is ok */ for (p=cep->ce_vardata; *p; p++) { if (*p != ' ') { valid = 1; break; } } if (!valid) { config_error("%s:%i: empty me::info, should be a server description.", cep->ce_fileptr->cf_filename, cep->ce_varlinenum); errors++; } } else if (!strcmp(cep->ce_varname, "numeric")) { config_error("%s:%i: me::numeric has been removed, you must now specify a Server ID (SID) instead. " "Edit your configuration file and change 'numeric' to 'sid' and make up " "a server id of exactly 3 characters, starting with a digit, eg: \"001\" or \"0AB\".", cep->ce_fileptr->cf_filename, cep->ce_varlinenum); errors++; } else if (!strcmp(cep->ce_varname, "sid")) { if (has_sid) { config_warn_duplicate(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, "me::sid"); continue; } has_sid = 1; if (strlen(cep->ce_vardata) != 3) { config_error("%s:%i: me::sid must be 3 characters long and begin with a number", cep->ce_fileptr->cf_filename, cep->ce_varlinenum); errors++; } if (!isdigit(*cep->ce_vardata)) { config_error("%s:%i: me::sid must be 3 characters long and begin with a number", cep->ce_fileptr->cf_filename, cep->ce_varlinenum); errors++; } } /* Unknown entry */ else { config_error_unknown(ce->ce_fileptr->cf_filename, ce->ce_varlinenum, "me", cep->ce_varname); errors++; } } if (!has_name) { config_error_missing(ce->ce_fileptr->cf_filename, ce->ce_varlinenum, "me::name"); errors++; } if (!has_info) { config_error_missing(ce->ce_fileptr->cf_filename, ce->ce_varlinenum, "me::info"); errors++; } if (!has_sid) { config_error_missing(ce->ce_fileptr->cf_filename, ce->ce_varlinenum, "me::sid"); errors++; } requiredstuff.conf_me = 1; return errors; } /* * The files {} block */ int _conf_files(ConfigFile *conf, ConfigEntry *ce) { ConfigEntry *cep; if (!conf_files) { conf_files = MyMallocEx(sizeof(ConfigItem_files)); /* set defaults */ conf_files->motd_file = strdup(MPATH); conf_files->rules_file = strdup(RPATH); conf_files->smotd_file = strdup(SMPATH); conf_files->botmotd_file = strdup(BPATH); conf_files->opermotd_file = strdup(OPATH); conf_files->svsmotd_file = strdup(VPATH); conf_files->pid_file = strdup(IRCD_PIDFILE); conf_files->tune_file = strdup(IRCDTUNE); /* we let actual files get read in later by the motd caching mechanism */ } /* * hack to allow initialization of conf_files (above) when there is no files block in * CPATH. The caller calls _conf_files(NULL, NULL); to do this. We return here because * the for loop's initialization of cep would segfault otherwise. We return 1 because * if config_run() calls us with a NULL ce, it's got a bug...but we can't detect that. */ if(!ce) return 1; for (cep = ce->ce_entries; cep; cep = cep->ce_next) { if (!strcmp(cep->ce_varname, "motd")) safestrdup(conf_files->motd_file, cep->ce_vardata); else if (!strcmp(cep->ce_varname, "shortmotd")) safestrdup(conf_files->smotd_file, cep->ce_vardata); else if (!strcmp(cep->ce_varname, "opermotd")) safestrdup(conf_files->opermotd_file, cep->ce_vardata); else if (!strcmp(cep->ce_varname, "svsmotd")) safestrdup(conf_files->svsmotd_file, cep->ce_vardata); else if (!strcmp(cep->ce_varname, "botmotd")) safestrdup(conf_files->botmotd_file, cep->ce_vardata); else if (!strcmp(cep->ce_varname, "rules")) safestrdup(conf_files->rules_file, cep->ce_vardata); else if (!strcmp(cep->ce_varname, "tunefile")) safestrdup(conf_files->tune_file, cep->ce_vardata); else if (!strcmp(cep->ce_varname, "pidfile")) safestrdup(conf_files->pid_file, cep->ce_vardata); } return 1; } int _test_files(ConfigFile *conf, ConfigEntry *ce) { ConfigEntry *cep; int errors = 0; char has_motd = 0, has_smotd = 0, has_rules = 0; char has_botmotd = 0, has_opermotd = 0, has_svsmotd = 0; char has_pidfile = 0, has_tunefile = 0; for (cep = ce->ce_entries; cep; cep = cep->ce_next) { /* files::motd */ if (!strcmp(cep->ce_varname, "motd")) { if (has_motd) { config_warn_duplicate(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, "files::motd"); continue; } config_test_openfile(cep, O_RDONLY, 0, "files::motd", 0, 1); has_motd = 1; } /* files::smotd */ else if (!strcmp(cep->ce_varname, "shortmotd")) { if (has_smotd) { config_warn_duplicate(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, "files::shortmotd"); continue; } config_test_openfile(cep, O_RDONLY, 0, "files::shortmotd", 0, 1); has_smotd = 1; } /* files::rules */ else if (!strcmp(cep->ce_varname, "rules")) { if (has_rules) { config_warn_duplicate(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, "files::rules"); continue; } config_test_openfile(cep, O_RDONLY, 0, "files::rules", 0, 1); has_rules = 1; } /* files::botmotd */ else if (!strcmp(cep->ce_varname, "botmotd")) { if (has_botmotd) { config_warn_duplicate(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, "files::botmotd"); continue; } config_test_openfile(cep, O_RDONLY, 0, "files::botmotd", 0, 1); has_botmotd = 1; } /* files::opermotd */ else if (!strcmp(cep->ce_varname, "opermotd")) { if (has_opermotd) { config_warn_duplicate(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, "files::opermotd"); continue; } config_test_openfile(cep, O_RDONLY, 0, "files::opermotd", 0, 1); has_opermotd = 1; } /* files::svsmotd * This config stuff should somehow be inside of modules/m_svsmotd.c!!!... right? */ else if (!strcmp(cep->ce_varname, "svsmotd")) { if (has_svsmotd) { config_warn_duplicate(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, "files::svsmotd"); continue; } /* svsmotd can't be a URL because we have to be able to write to it */ config_test_openfile(cep, O_RDONLY, 0, "files::svsmotd", 0, 0); has_svsmotd = 1; } /* files::pidfile */ else if (!strcmp(cep->ce_varname, "pidfile")) { if (has_pidfile) { config_warn_duplicate(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, "files::pidfile"); continue; } errors += config_test_openfile(cep, O_WRONLY | O_CREAT, 0600, "files::pidfile", 1, 0); has_pidfile = 1; } /* files::tunefile */ else if (!strcmp(cep->ce_varname, "tunefile")) { if (has_tunefile) { config_warn_duplicate(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, "files::tunefile"); continue; } errors += config_test_openfile(cep, O_RDWR | O_CREAT, 0600, "files::tunefile", 1, 0); has_tunefile = 1; } /* */ else { config_error("%s:%d: Unknown directive: \"%s\" in files {}", cep->ce_fileptr->cf_filename, cep->ce_varlinenum, cep->ce_varname); errors ++; } } return errors; } /* * The operclass {} block parser */ OperClassACLEntry* _conf_parseACLEntry(ConfigEntry *ce) { ConfigEntry *cep; OperClassACLEntry *entry = NULL; entry = MyMallocEx(sizeof(OperClassACLEntry)); if (!strcmp(ce->ce_varname,"allow")) entry->type = OPERCLASSENTRY_ALLOW; else entry->type = OPERCLASSENTRY_DENY; for (cep = ce->ce_entries; cep; cep = cep->ce_next) { OperClassACLEntryVar *var = MyMallocEx(sizeof(OperClassACLEntryVar)); var->name = strdup(cep->ce_varname); if (cep->ce_vardata) { var->value = strdup(cep->ce_vardata); } AddListItem(var,entry->variables); } return entry; } OperClassACL* _conf_parseACL(char* name, ConfigEntry *ce) { ConfigEntry *cep; ConfigEntry *cepp; OperClassACL *acl = NULL; acl = MyMallocEx(sizeof(OperClassACL)); acl->name = strdup(name); for (cep = ce->ce_entries; cep; cep = cep->ce_next) { if (!strcmp(cep->ce_varname, "deny") || !strcmp(cep->ce_varname, "allow")) { OperClassACLEntry *entry = _conf_parseACLEntry(cep); AddListItem(entry,acl->entries); } else { OperClassACL *subAcl = _conf_parseACL(cep->ce_varname,cep); AddListItem(subAcl,acl->acls); } } return acl; } int _conf_operclass(ConfigFile *conf, ConfigEntry *ce) { ConfigEntry *cep; ConfigEntry *cepp; ConfigItem_operclass *operClass = NULL; operClass = MyMallocEx(sizeof(ConfigItem_operclass)); operClass->classStruct = MyMallocEx(sizeof(OperClass)); operClass->classStruct->name = strdup(ce->ce_vardata); for (cep = ce->ce_entries; cep; cep = cep->ce_next) { if (!strcmp(cep->ce_varname, "parent")) { operClass->classStruct->ISA = strdup(cep->ce_vardata); } else if (!strcmp(cep->ce_varname, "privileges")) { for (cepp = cep->ce_entries; cepp; cepp = cepp->ce_next) { OperClassACL *acl = _conf_parseACL(cepp->ce_varname,cepp); AddListItem(acl,operClass->classStruct->acls); } } } AddListItem(operClass, conf_operclass); return 1; } int _test_operclass(ConfigFile *conf, ConfigEntry *ce) { char has_privileges = 0, has_parent = 0; ConfigEntry *cep; ConfigEntry *cepp; NameValue *ofp; int errors = 0; if (!ce->ce_vardata) { config_error_noname(ce->ce_fileptr->cf_filename, ce->ce_varlinenum, "operclass"); errors++; } for (cep = ce->ce_entries; cep; cep = cep->ce_next) { if (!cep->ce_varname) { config_error_blank(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, "operclass"); errors++; continue; } if (!strcmp(cep->ce_varname, "parent")) { if (has_parent) { config_warn_duplicate(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, "operclass::parent"); continue; } has_parent = 1; continue; } if (!strcmp(cep->ce_varname, "privileges")) { if (has_privileges) { config_warn_duplicate(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, "oper::privileges"); continue; } has_privileges = 1; continue; } /* Regular variables */ if (!cep->ce_entries) { if (!strcmp(cep->ce_varname, "privileges") && !cep->ce_vardata) { config_error_blank(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, "operclass::parent"); errors++; continue; } else { config_error_unknown(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, "operclass", cep->ce_varname); errors++; continue; } } /* Sections */ else { /* No that's not a typo, if it isn't privileges, we explode */ if (strcmp(cep->ce_varname, "privileges")) { config_error_unknown(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, "operclass", cep->ce_varname); errors++; continue; } } } if (!has_privileges) { config_error_missing(ce->ce_fileptr->cf_filename, ce->ce_varlinenum, "oper::privileges"); errors++; } return errors; } /* * The oper {} block parser */ int _conf_oper(ConfigFile *conf, ConfigEntry *ce) { ConfigEntry *cep; ConfigEntry *cepp; ConfigItem_oper *oper = NULL; oper = MyMallocEx(sizeof(ConfigItem_oper)); oper->name = strdup(ce->ce_vardata); for (cep = ce->ce_entries; cep; cep = cep->ce_next) { if (!strcmp(cep->ce_varname, "operclass")) oper->operclass = strdup(cep->ce_vardata); if (!strcmp(cep->ce_varname, "password")) oper->auth = Auth_ConvertConf2AuthStruct(cep); else if (!strcmp(cep->ce_varname, "class")) { oper->class = Find_class(cep->ce_vardata); if (!oper->class || (oper->class->flag.temporary == 1)) { config_status("%s:%i: illegal oper::class, unknown class '%s' using default of class 'default'", cep->ce_fileptr->cf_filename, cep->ce_varlinenum, cep->ce_vardata); oper->class = default_class; } } else if (!strcmp(cep->ce_varname, "swhois")) { SWhois *s; if (cep->ce_entries) { for (cepp = cep->ce_entries; cepp; cepp = cepp->ce_next) { s = MyMallocEx(sizeof(SWhois)); s->line = strdup(cepp->ce_varname); s->setby = strdup("oper"); AddListItem(s, oper->swhois); } } else if (cep->ce_vardata) { s = MyMallocEx(sizeof(SWhois)); s->line = strdup(cep->ce_vardata); s->setby = strdup("oper"); AddListItem(s, oper->swhois); } } else if (!strcmp(cep->ce_varname, "snomask")) { safestrdup(oper->snomask, cep->ce_vardata); } else if (!strcmp(cep->ce_varname, "modes")) { oper->modes = set_usermode(cep->ce_vardata); } else if (!strcmp(cep->ce_varname, "require-modes")) { oper->require_modes = set_usermode(cep->ce_vardata); } else if (!strcmp(cep->ce_varname, "maxlogins")) { oper->maxlogins = atoi(cep->ce_vardata); } else if (!strcmp(cep->ce_varname, "mask")) { unreal_add_masks(&oper->mask, cep); } else if (!strcmp(cep->ce_varname, "vhost")) { safestrdup(oper->vhost, cep->ce_vardata); } } AddListItem(oper, conf_oper); return 1; } int _test_oper(ConfigFile *conf, ConfigEntry *ce) { char has_class = 0, has_password = 0, has_swhois = 0, has_snomask = 0; char has_modes = 0, has_require_modes = 0, has_mask = 0, has_maxlogins = 0; char has_operclass = 0, has_vhost = 0; int oper_flags = 0; ConfigEntry *cep; ConfigEntry *cepp; NameValue *ofp; int errors = 0; if (!ce->ce_vardata) { config_error_noname(ce->ce_fileptr->cf_filename, ce->ce_varlinenum, "oper"); errors++; } for (cep = ce->ce_entries; cep; cep = cep->ce_next) { if (!cep->ce_varname) { config_error_blank(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, "oper"); errors++; continue; } /* Regular variables */ if (!cep->ce_entries) { if (config_is_blankorempty(cep, "oper")) { errors++; continue; } /* oper::password */ if (!strcmp(cep->ce_varname, "password")) { if (has_password) { config_warn_duplicate(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, "oper::password"); continue; } has_password = 1; if (Auth_CheckError(cep) < 0) errors++; if (ce->ce_vardata && cep->ce_vardata && !strcmp(ce->ce_vardata, "bobsmith") && !strcmp(cep->ce_vardata, "test")) { config_error("%s:%i: please change the the name and password of the " "default 'bobsmith' oper block", ce->ce_fileptr->cf_filename, ce->ce_varlinenum); errors++; } continue; } /* oper::operclass */ else if (!strcmp(cep->ce_varname, "operclass")) { if (has_operclass) { config_warn_duplicate(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, "oper::operclass"); continue; } has_operclass = 1; continue; } /* oper::class */ else if (!strcmp(cep->ce_varname, "class")) { if (has_class) { config_warn_duplicate(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, "oper::class"); continue; } has_class = 1; } /* oper::swhois */ else if (!strcmp(cep->ce_varname, "swhois")) { } /* oper::vhost */ else if (!strcmp(cep->ce_varname, "vhost")) { if (has_vhost) { config_warn_duplicate(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, "oper::vhost"); continue; } has_vhost = 1; } /* oper::snomask */ else if (!strcmp(cep->ce_varname, "snomask")) { if (has_snomask) { config_warn_duplicate(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, "oper::snomask"); continue; } has_snomask = 1; } /* oper::modes */ else if (!strcmp(cep->ce_varname, "modes")) { char *p; for (p = cep->ce_vardata; *p; p++) if (strchr("oOaANCrzS", *p)) { config_error("%s:%i: oper::modes may not include mode '%c'", cep->ce_fileptr->cf_filename, cep->ce_varlinenum, *p); errors++; } if (has_modes) { config_warn_duplicate(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, "oper::modes"); continue; } has_modes = 1; } /* oper::require-modes */ else if (!strcmp(cep->ce_varname, "require-modes")) { char *p; for (p = cep->ce_vardata; *p; p++) if (strchr("oOaANC", *p)) { config_warn("%s:%i: oper::require-modes probably shouldn't include mode '%c'", cep->ce_fileptr->cf_filename, cep->ce_varlinenum, *p); } if (has_require_modes) { config_warn_duplicate(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, "oper::require-modes"); continue; } has_require_modes = 1; } /* oper::maxlogins */ else if (!strcmp(cep->ce_varname, "maxlogins")) { int l; if (has_maxlogins) { config_warn_duplicate(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, "oper::maxlogins"); continue; } has_maxlogins = 1; l = atoi(cep->ce_vardata); if ((l < 0) || (l > 5000)) { config_error("%s:%i: oper::maxlogins: value out of range (%d) should be 0-5000", cep->ce_fileptr->cf_filename, cep->ce_varlinenum, l); errors++; continue; } } /* oper::flags */ else if (!strcmp(cep->ce_varname, "flags")) { config_error("%s:%i: oper::flags no longer exists. UnrealIRCd 3.4.x uses a new style oper block now.", cep->ce_fileptr->cf_filename, cep->ce_varlinenum); errors++; need_34_upgrade = 1; } else if (!strcmp(cep->ce_varname, "mask")) { if (cep->ce_vardata || cep->ce_entries) has_mask = 1; } else { config_error_unknown(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, "oper", cep->ce_varname); errors++; continue; } } /* Sections */ else { /* oper::flags {} */ if (!strcmp(cep->ce_varname, "flags")) { config_error("%s:%i: oper::flags no longer exists. UnrealIRCd 3.4.x uses a new style oper block now.", cep->ce_fileptr->cf_filename, cep->ce_varlinenum); errors++; need_34_upgrade = 1; continue; } /* oper::from {} */ else if (!strcmp(cep->ce_varname, "from")) { config_error("%s:%i: oper::from::userhost is now called oper::mask", cep->ce_fileptr->cf_filename, cep->ce_varlinenum); errors++; need_34_upgrade = 1; continue; } else if (!strcmp(cep->ce_varname, "swhois")) { /* ok */ } else if (!strcmp(cep->ce_varname, "mask")) { if (cep->ce_vardata || cep->ce_entries) has_mask = 1; } else { config_error_unknown(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, "oper", cep->ce_varname); errors++; continue; } } } if (!has_password) { config_error_missing(ce->ce_fileptr->cf_filename, ce->ce_varlinenum, "oper::password"); errors++; } if (!has_mask) { config_error_missing(ce->ce_fileptr->cf_filename, ce->ce_varlinenum, "oper::mask"); errors++; } if (!has_class) { config_error_missing(ce->ce_fileptr->cf_filename, ce->ce_varlinenum, "oper::class"); errors++; } if (!has_operclass) { config_error_missing(ce->ce_fileptr->cf_filename, ce->ce_varlinenum, "oper::operclass"); need_34_upgrade = 1; errors++; } return errors; } /* * The class {} block parser */ int _conf_class(ConfigFile *conf, ConfigEntry *ce) { ConfigEntry *cep, *cep2; ConfigItem_class *class; unsigned char isnew = 0; if (!(class = Find_class(ce->ce_vardata))) { class = MyMallocEx(sizeof(ConfigItem_class)); safestrdup(class->name, ce->ce_vardata); isnew = 1; } else { isnew = 0; class->flag.temporary = 0; class->options = 0; /* RESET OPTIONS */ } safestrdup(class->name, ce->ce_vardata); class->connfreq = 15; /* default */ for (cep = ce->ce_entries; cep; cep = cep->ce_next) { if (!strcmp(cep->ce_varname, "pingfreq")) class->pingfreq = config_checkval(cep->ce_vardata,CFG_TIME); else if (!strcmp(cep->ce_varname, "connfreq")) class->connfreq = config_checkval(cep->ce_vardata,CFG_TIME); else if (!strcmp(cep->ce_varname, "maxclients")) class->maxclients = atol(cep->ce_vardata); else if (!strcmp(cep->ce_varname, "sendq")) class->sendq = config_checkval(cep->ce_vardata,CFG_SIZE); else if (!strcmp(cep->ce_varname, "recvq")) class->recvq = config_checkval(cep->ce_vardata,CFG_SIZE); else if (!strcmp(cep->ce_varname, "options")) { for (cep2 = cep->ce_entries; cep2; cep2 = cep2->ce_next) if (!strcmp(cep2->ce_varname, "nofakelag")) class->options |= CLASS_OPT_NOFAKELAG; } } if (isnew) AddListItem(class, conf_class); return 1; } int _test_class(ConfigFile *conf, ConfigEntry *ce) { ConfigEntry *cep, *cep2; int errors = 0; char has_pingfreq = 0, has_connfreq = 0, has_maxclients = 0, has_sendq = 0; char has_recvq = 0; if (!ce->ce_vardata) { config_error_noname(ce->ce_fileptr->cf_filename, ce->ce_varlinenum, "class"); return 1; } if (!strcasecmp(ce->ce_vardata, "default")) { config_error("%s:%d: Class cannot be named 'default', this class name is reserved for internal use.", ce->ce_fileptr->cf_filename, ce->ce_varlinenum); errors++; } for (cep = ce->ce_entries; cep; cep = cep->ce_next) { if (!strcmp(cep->ce_varname, "options")) { for (cep2 = cep->ce_entries; cep2; cep2 = cep2->ce_next) { #ifdef FAKELAG_CONFIGURABLE if (!strcmp(cep2->ce_varname, "nofakelag")) ; else #endif { config_error("%s:%d: Unknown option '%s' in class::options", cep2->ce_fileptr->cf_filename, cep2->ce_varlinenum, cep2->ce_varname); errors++; } } } else if (config_is_blankorempty(cep, "class")) { errors++; continue; } /* class::pingfreq */ else if (!strcmp(cep->ce_varname, "pingfreq")) { int v = config_checkval(cep->ce_vardata,CFG_TIME); if (has_pingfreq) { config_warn_duplicate(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, "class::pingfreq"); continue; } has_pingfreq = 1; if ((v < 30) || (v > 600)) { config_error("%s:%i: class::pingfreq should be a reasonable value (30-600)", cep->ce_fileptr->cf_filename, cep->ce_varlinenum); errors++; continue; } } /* class::maxclients */ else if (!strcmp(cep->ce_varname, "maxclients")) { long l; if (has_maxclients) { config_warn_duplicate(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, "class::maxclients"); continue; } has_maxclients = 1; l = atol(cep->ce_vardata); if ((l < 1) || (l > 1000000)) { config_error("%s:%i: class::maxclients with illegal value", cep->ce_fileptr->cf_filename, cep->ce_varlinenum); errors++; } } /* class::connfreq */ else if (!strcmp(cep->ce_varname, "connfreq")) { long l; if (has_connfreq) { config_warn_duplicate(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, "class::connfreq"); continue; } has_connfreq = 1; l = config_checkval(cep->ce_vardata,CFG_TIME); if ((l < 5) || (l > 604800)) { config_error("%s:%i: class::connfreq with illegal value (must be >5 and <7d)", cep->ce_fileptr->cf_filename, cep->ce_varlinenum); errors++; } } /* class::sendq */ else if (!strcmp(cep->ce_varname, "sendq")) { long l; if (has_sendq) { config_warn_duplicate(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, "class::sendq"); continue; } has_sendq = 1; l = config_checkval(cep->ce_vardata,CFG_SIZE); if ((l <= 0) || (l > 2000000000)) { config_error("%s:%i: class::sendq with illegal value", cep->ce_fileptr->cf_filename, cep->ce_varlinenum); errors++; } } /* class::recvq */ else if (!strcmp(cep->ce_varname, "recvq")) { long l; if (has_recvq) { config_warn_duplicate(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, "class::recvq"); continue; } has_recvq = 1; l = config_checkval(cep->ce_vardata,CFG_SIZE); if ((l < 512) || (l > 32768)) { config_error("%s:%i: class::recvq with illegal value (must be >512 and <32k)", cep->ce_fileptr->cf_filename, cep->ce_varlinenum); errors++; } } /* Unknown */ else { config_error_unknown(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, "class", cep->ce_varname); errors++; continue; } } if (!has_pingfreq) { config_error_missing(ce->ce_fileptr->cf_filename, ce->ce_varlinenum, "class::pingfreq"); errors++; } if (!has_maxclients) { config_error_missing(ce->ce_fileptr->cf_filename, ce->ce_varlinenum, "class::maxclients"); errors++; } if (!has_sendq) { config_error_missing(ce->ce_fileptr->cf_filename, ce->ce_varlinenum, "class::sendq"); errors++; } return errors; } int _conf_drpass(ConfigFile *conf, ConfigEntry *ce) { ConfigEntry *cep; if (!conf_drpass) { conf_drpass = MyMallocEx(sizeof(ConfigItem_drpass)); } for (cep = ce->ce_entries; cep; cep = cep->ce_next) { if (!strcmp(cep->ce_varname, "restart")) { if (conf_drpass->restartauth) Auth_DeleteAuthStruct(conf_drpass->restartauth); conf_drpass->restartauth = Auth_ConvertConf2AuthStruct(cep); } else if (!strcmp(cep->ce_varname, "die")) { if (conf_drpass->dieauth) Auth_DeleteAuthStruct(conf_drpass->dieauth); conf_drpass->dieauth = Auth_ConvertConf2AuthStruct(cep); } } return 1; } int _test_drpass(ConfigFile *conf, ConfigEntry *ce) { ConfigEntry *cep; int errors = 0; char has_restart = 0, has_die = 0; for (cep = ce->ce_entries; cep; cep = cep->ce_next) { if (config_is_blankorempty(cep, "drpass")) { errors++; continue; } /* drpass::restart */ if (!strcmp(cep->ce_varname, "restart")) { if (has_restart) { config_warn_duplicate(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, "drpass::restart"); continue; } has_restart = 1; if (Auth_CheckError(cep) < 0) errors++; continue; } /* drpass::die */ else if (!strcmp(cep->ce_varname, "die")) { if (has_die) { config_warn_duplicate(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, "drpass::die"); continue; } has_die = 1; if (Auth_CheckError(cep) < 0) errors++; continue; } /* Unknown */ else { config_error_unknown(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, "drpass", cep->ce_varname); errors++; continue; } } return errors; } /* * The ulines {} block parser */ int _conf_ulines(ConfigFile *conf, ConfigEntry *ce) { ConfigEntry *cep; ConfigItem_ulines *ca; for (cep = ce->ce_entries; cep; cep = cep->ce_next) { ca = MyMallocEx(sizeof(ConfigItem_ulines)); safestrdup(ca->servername, cep->ce_varname); AddListItem(ca, conf_ulines); } return 1; } int _test_ulines(ConfigFile *conf, ConfigEntry *ce) { ConfigEntry *cep; int errors = 0; for (cep = ce->ce_entries; cep; cep = cep->ce_next) { if (!cep->ce_varname) { config_error_blank(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, "ulines"); errors++; continue; } } return errors; } int _conf_tld(ConfigFile *conf, ConfigEntry *ce) { ConfigEntry *cep; ConfigItem_tld *ca; ca = MyMallocEx(sizeof(ConfigItem_tld)); for (cep = ce->ce_entries; cep; cep = cep->ce_next) { if (!strcmp(cep->ce_varname, "mask")) ca->mask = strdup(cep->ce_vardata); else if (!strcmp(cep->ce_varname, "motd")) { ca->motd_file = strdup(cep->ce_vardata); read_motd(cep->ce_vardata, &ca->motd); } else if (!strcmp(cep->ce_varname, "shortmotd")) { ca->smotd_file = strdup(cep->ce_vardata); read_motd(cep->ce_vardata, &ca->smotd); } else if (!strcmp(cep->ce_varname, "opermotd")) { ca->opermotd_file = strdup(cep->ce_vardata); read_motd(cep->ce_vardata, &ca->opermotd); } else if (!strcmp(cep->ce_varname, "botmotd")) { ca->botmotd_file = strdup(cep->ce_vardata); read_motd(cep->ce_vardata, &ca->botmotd); } else if (!strcmp(cep->ce_varname, "rules")) { ca->rules_file = strdup(cep->ce_vardata); read_motd(cep->ce_vardata, &ca->rules); } else if (!strcmp(cep->ce_varname, "options")) { ConfigEntry *cepp; for (cepp = cep->ce_entries; cepp; cepp = cepp->ce_next) { if (!strcmp(cepp->ce_varname, "ssl")) ca->options |= TLD_SSL; else if (!strcmp(cepp->ce_varname, "remote")) ca->options |= TLD_REMOTE; } } else if (!strcmp(cep->ce_varname, "channel")) ca->channel = strdup(cep->ce_vardata); } AddListItem(ca, conf_tld); return 1; } int _test_tld(ConfigFile *conf, ConfigEntry *ce) { ConfigEntry *cep; int errors = 0; int fd = -1; char has_mask = 0, has_motd = 0, has_rules = 0, has_shortmotd = 0, has_channel = 0; char has_opermotd = 0, has_botmotd = 0, has_options = 0; for (cep = ce->ce_entries; cep; cep = cep->ce_next) { if (!cep->ce_varname) { config_error_blank(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, "tld"); errors++; continue; } if (!cep->ce_vardata && strcmp(cep->ce_varname, "options")) { config_error_empty(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, "tld", cep->ce_varname); errors++; continue; } /* tld::mask */ if (!strcmp(cep->ce_varname, "mask")) { if (has_mask) { config_warn_duplicate(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, "tld::mask"); continue; } has_mask = 1; } /* tld::motd */ else if (!strcmp(cep->ce_varname, "motd")) { if (has_motd) { config_warn_duplicate(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, "tld::motd"); continue; } has_motd = 1; if (((fd = open(cep->ce_vardata, O_RDONLY)) == -1)) { config_error("%s:%i: tld::motd: %s: %s", cep->ce_fileptr->cf_filename, cep->ce_varlinenum, cep->ce_vardata, strerror(errno)); errors++; } else close(fd); } /* tld::rules */ else if (!strcmp(cep->ce_varname, "rules")) { if (has_rules) { config_warn_duplicate(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, "tld::rules"); continue; } has_rules = 1; if (((fd = open(cep->ce_vardata, O_RDONLY)) == -1)) { config_error("%s:%i: tld::rules: %s: %s", cep->ce_fileptr->cf_filename, cep->ce_varlinenum, cep->ce_vardata, strerror(errno)); errors++; } else close(fd); } /* tld::channel */ else if (!strcmp(cep->ce_varname, "channel")) { if (has_channel) { config_warn_duplicate(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, "tld::channel"); continue; } has_channel = 1; } /* tld::shortmotd */ else if (!strcmp(cep->ce_varname, "shortmotd")) { if (has_shortmotd) { config_warn_duplicate(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, "tld::shortmotd"); continue; } has_shortmotd = 1; if (((fd = open(cep->ce_vardata, O_RDONLY)) == -1)) { config_error("%s:%i: tld::shortmotd: %s: %s", cep->ce_fileptr->cf_filename, cep->ce_varlinenum, cep->ce_vardata, strerror(errno)); errors++; } else close(fd); } /* tld::opermotd */ else if (!strcmp(cep->ce_varname, "opermotd")) { if (has_opermotd) { config_warn_duplicate(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, "tld::opermotd"); continue; } has_opermotd = 1; if (((fd = open(cep->ce_vardata, O_RDONLY)) == -1)) { config_error("%s:%i: tld::opermotd: %s: %s", cep->ce_fileptr->cf_filename, cep->ce_varlinenum, cep->ce_vardata, strerror(errno)); errors++; } else close(fd); } /* tld::botmotd */ else if (!strcmp(cep->ce_varname, "botmotd")) { if (has_botmotd) { config_warn_duplicate(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, "tld::botmotd"); continue; } has_botmotd = 1; if (((fd = open(cep->ce_vardata, O_RDONLY)) == -1)) { config_error("%s:%i: tld::botmotd: %s: %s", cep->ce_fileptr->cf_filename, cep->ce_varlinenum, cep->ce_vardata, strerror(errno)); errors++; } else close(fd); } /* tld::options */ else if (!strcmp(cep->ce_varname, "options")) { ConfigEntry *cep2; if (has_options) { config_warn_duplicate(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, "tld::options"); continue; } has_options = 1; for (cep2 = cep->ce_entries; cep2; cep2 = cep2->ce_next) { if (!cep2->ce_varname) { config_error_blank(cep2->ce_fileptr->cf_filename, cep2->ce_varlinenum, "tld::options"); continue; } if (strcmp(cep2->ce_varname, "ssl") && strcmp(cep2->ce_varname, "remote")) { config_error_unknownopt(cep2->ce_fileptr->cf_filename, cep2->ce_varlinenum, "tld", cep2->ce_varname); errors++; } } } else { config_error_unknown(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, "tld", cep->ce_varname); errors++; continue; } } if (!has_mask) { config_error_missing(ce->ce_fileptr->cf_filename, ce->ce_varlinenum, "tld::mask"); errors++; } if (!has_motd) { config_error_missing(ce->ce_fileptr->cf_filename, ce->ce_varlinenum, "tld::motd"); errors++; } if (!has_rules) { config_error_missing(ce->ce_fileptr->cf_filename, ce->ce_varlinenum, "tld::rules"); errors++; } return errors; } int _conf_listen(ConfigFile *conf, ConfigEntry *ce) { ConfigEntry *cep; ConfigEntry *cepp; ConfigItem_listen *listen = NULL; char *ip; int start=0, end=0, port, isnew; int tmpflags =0; for (cep = ce->ce_entries; cep; cep = cep->ce_next) { if (!strcmp(cep->ce_varname, "ip")) { ip = cep->ce_vardata; } else if (!strcmp(cep->ce_varname, "port")) { port_range(cep->ce_vardata, &start, &end); if ((start < 0) || (start > 65535) || (end < 0) || (end > 65535)) return -1; /* this is already validated in _test_listen, but okay.. */ } else if (!strcmp(cep->ce_varname, "options")) { for (cepp = cep->ce_entries; cepp; cepp = cepp->ce_next) { NameValue *ofp; if ((ofp = config_binary_flags_search(_ListenerFlags, cepp->ce_varname, ARRAY_SIZEOF(_ListenerFlags)))) tmpflags |= ofp->flag; } } } for (port = start; port <= end; port++) { /* First deal with IPv4 */ if (!strchr(ip, ':')) { if (!(listen = Find_listen(ip, port, 0))) { listen = MyMallocEx(sizeof(ConfigItem_listen)); listen->ip = strdup(ip); listen->port = port; listen->fd = -1; listen->ipv6 = 0; isnew = 1; } else isnew = 0; if (listen->options & LISTENER_BOUND) tmpflags |= LISTENER_BOUND; listen->options = tmpflags; if (isnew) AddListItem(listen, conf_listen); listen->flag.temporary = 0; } /* Then deal with IPv6 (if available/enabled) */ if (!DISABLE_IPV6) { if (strchr(ip, ':') || (*ip == '*')) { if (!(listen = Find_listen(ip, port, 1))) { listen = MyMallocEx(sizeof(ConfigItem_listen)); listen->ip = strdup(ip); listen->port = port; listen->fd = -1; listen->ipv6 = 1; isnew = 1; } else isnew = 0; if (listen->options & LISTENER_BOUND) tmpflags |= LISTENER_BOUND; listen->options = tmpflags; if (isnew) AddListItem(listen, conf_listen); listen->flag.temporary = 0; } } } return 1; } int _test_listen(ConfigFile *conf, ConfigEntry *ce) { ConfigEntry *cep; ConfigEntry *cepp; int errors = 0; char has_ip = 0, has_port = 0, has_options = 0; if (ce->ce_vardata) { config_error("%s:%i: listen block has a new syntax, see https://www.unrealircd.org/docs/Listen_block", ce->ce_fileptr->cf_filename, ce->ce_varlinenum); need_34_upgrade = 1; return 1; } for (cep = ce->ce_entries; cep; cep = cep->ce_next) { if (!cep->ce_varname) { config_error_blank(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, "listen"); errors++; continue; } if (!strcmp(cep->ce_varname, "options")) { if (has_options) { config_warn_duplicate(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, "listen::options"); continue; } has_options = 1; for (cepp = cep->ce_entries; cepp; cepp = cepp->ce_next) { NameValue *ofp; if (!cepp->ce_varname) { config_error_blank(cepp->ce_fileptr->cf_filename, cepp->ce_varlinenum, "listen::options"); errors++; continue; } if (!(ofp = config_binary_flags_search(_ListenerFlags, cepp->ce_varname, ARRAY_SIZEOF(_ListenerFlags)))) { config_error_unknownopt(cepp->ce_fileptr->cf_filename, cepp->ce_varlinenum, "listen::options", cepp->ce_varname); errors++; continue; } } } else if (!cep->ce_vardata) { config_error_empty(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, "listen", cep->ce_varname); errors++; continue; } else if (!strcmp(cep->ce_varname, "ip")) { has_ip = 1; if (strcmp(cep->ce_vardata, "*") && !is_valid_ip(cep->ce_vardata)) { config_error("%s:%i: listen: illegal listen::ip (%s). Must be either '*' or contain a valid IP.", cep->ce_fileptr->cf_filename, cep->ce_varlinenum, cep->ce_vardata); return 1; } } else if (!strcmp(cep->ce_varname, "host")) { config_error("%s:%i: listen: unknown option listen::host, did you mean listen::ip?", cep->ce_fileptr->cf_filename, cep->ce_varlinenum); errors++; } else if (!strcmp(cep->ce_varname, "port")) { int start = 0, end = 0; has_port = 1; port_range(cep->ce_vardata, &start, &end); if (start == end) { if ((start < 0) || (start > 65535)) { config_error("%s:%i: listen: illegal port (must be 0..65535)", cep->ce_fileptr->cf_filename, cep->ce_varlinenum); return 1; } } else { if (end < start) { config_error("%s:%i: listen: illegal port range end value is less than starting value", cep->ce_fileptr->cf_filename, cep->ce_varlinenum); return 1; } if (end - start >= 100) { config_error("%s:%i: listen: you requested port %d-%d, that's %d ports " "(and thus consumes %d sockets) this is probably not what you want.", cep->ce_fileptr->cf_filename, cep->ce_varlinenum, start, end, end - start + 1, end - start + 1); return 1; } if ((start < 0) || (start > 65535) || (end < 0) || (end > 65535)) { config_error("%s:%i: listen: illegal port range values must be between 0 and 65535", cep->ce_fileptr->cf_filename, cep->ce_varlinenum); return 1; } } } else { config_error_unknown(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, "listen", cep->ce_varname); errors++; continue; } } if (!has_ip) { config_error("%s:%d: listen block requires an listen::ip", ce->ce_fileptr->cf_filename, ce->ce_varlinenum); errors++; } if (!has_port) { config_error("%s:%d: listen block requires an listen::port", ce->ce_fileptr->cf_filename, ce->ce_varlinenum); errors++; } requiredstuff.conf_listen = 1; return errors; } int _conf_allow(ConfigFile *conf, ConfigEntry *ce) { ConfigEntry *cep, *cepp; ConfigItem_allow *allow; Hook *h; if (ce->ce_vardata) { if (!strcmp(ce->ce_vardata, "channel")) return (_conf_allow_channel(conf, ce)); else if (!strcmp(ce->ce_vardata, "dcc")) return (_conf_allow_dcc(conf, ce)); else { int value; for (h = Hooks[HOOKTYPE_CONFIGRUN]; h; h = h->next) { value = (*(h->func.intfunc))(conf,ce,CONFIG_ALLOW); if (value == 1) break; } return 0; } } allow = MyMallocEx(sizeof(ConfigItem_allow)); for (cep = ce->ce_entries; cep; cep = cep->ce_next) { if (!strcmp(cep->ce_varname, "ip")) { allow->ip = strdup(cep->ce_vardata); } else if (!strcmp(cep->ce_varname, "hostname")) allow->hostname = strdup(cep->ce_vardata); else if (!strcmp(cep->ce_varname, "password")) allow->auth = Auth_ConvertConf2AuthStruct(cep); else if (!strcmp(cep->ce_varname, "class")) { allow->class = Find_class(cep->ce_vardata); if (!allow->class || (allow->class->flag.temporary == 1)) { config_status("%s:%i: illegal allow::class, unknown class '%s' using default of class 'default'", cep->ce_fileptr->cf_filename, cep->ce_varlinenum, cep->ce_vardata); allow->class = default_class; } } else if (!strcmp(cep->ce_varname, "maxperip")) allow->maxperip = atoi(cep->ce_vardata); else if (!strcmp(cep->ce_varname, "redirect-server")) allow->server = strdup(cep->ce_vardata); else if (!strcmp(cep->ce_varname, "redirect-port")) allow->port = atoi(cep->ce_vardata); else if (!strcmp(cep->ce_varname, "ipv6-clone-mask")) { /* * If this item isn't set explicitly by the * user, the value will temporarily be * zero. Defaults are applied in config_run(). */ allow->ipv6_clone_mask = atoi(cep->ce_vardata); } else if (!strcmp(cep->ce_varname, "options")) { for (cepp = cep->ce_entries; cepp; cepp = cepp->ce_next) { if (!strcmp(cepp->ce_varname, "noident")) allow->flags.noident = 1; else if (!strcmp(cepp->ce_varname, "useip")) allow->flags.useip = 1; else if (!strcmp(cepp->ce_varname, "ssl")) allow->flags.ssl = 1; else if (!strcmp(cepp->ce_varname, "nopasscont")) allow->flags.nopasscont = 1; } } } if (!allow->hostname) allow->hostname = strdup("*@NOMATCH"); if (!allow->ip) allow->ip = strdup("*@NOMATCH"); AddListItem(allow, conf_allow); return 1; } int _test_allow(ConfigFile *conf, ConfigEntry *ce) { ConfigEntry *cep, *cepp; int errors = 0; Hook *h; char has_ip = 0, has_hostname = 0, has_maxperip = 0, has_password = 0, has_class = 0; char has_redirectserver = 0, has_redirectport = 0, has_options = 0; if (ce->ce_vardata) { if (!strcmp(ce->ce_vardata, "channel")) return (_test_allow_channel(conf, ce)); else if (!strcmp(ce->ce_vardata, "dcc")) return (_test_allow_dcc(conf, ce)); else { int used = 0; for (h = Hooks[HOOKTYPE_CONFIGTEST]; h; h = h->next) { int value, errs = 0; if (h->owner && !(h->owner->flags & MODFLAG_TESTING) && !(h->owner->options & MOD_OPT_PERM)) continue; value = (*(h->func.intfunc))(conf,ce,CONFIG_ALLOW,&errs); if (value == 2) used = 1; if (value == 1) { used = 1; break; } if (value == -1) { used = 1; errors += errs; break; } if (value == -2) { used = 1; errors += errs; } } if (!used) { config_error("%s:%i: allow item with unknown type", ce->ce_fileptr->cf_filename, ce->ce_varlinenum); return 1; } return errors; } } for (cep = ce->ce_entries; cep; cep = cep->ce_next) { if (strcmp(cep->ce_varname, "options") && config_is_blankorempty(cep, "allow")) { errors++; continue; } if (!strcmp(cep->ce_varname, "ip")) { if (has_ip) { config_warn_duplicate(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, "allow::ip"); continue; } has_ip = 1; } else if (!strcmp(cep->ce_varname, "maxperip")) { int v = atoi(cep->ce_vardata); if (has_maxperip) { config_warn_duplicate(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, "allow::maxperip"); continue; } has_maxperip = 1; if ((v <= 0) || (v > 65535)) { config_error("%s:%i: allow::maxperip with illegal value (must be 1-65535)", cep->ce_fileptr->cf_filename, cep->ce_varlinenum); errors++; } } else if (!strcmp(cep->ce_varname, "ipv6-clone-mask")) { /* keep this in sync with _test_set() */ int ipv6mask; ipv6mask = atoi(cep->ce_vardata); if (ipv6mask == 0) { config_error("%s:%d: allow::ipv6-clone-mask given a value of zero. This cannnot be correct, as it would treat all IPv6 hosts as one host.", cep->ce_fileptr->cf_filename, cep->ce_varlinenum); errors++; } if (ipv6mask > 128) { config_error("%s:%d: set::default-ipv6-clone-mask was set to %d. The maximum value is 128.", cep->ce_fileptr->cf_filename, cep->ce_varlinenum, ipv6mask); errors++; } if (ipv6mask <= 32) { config_warn("%s:%d: allow::ipv6-clone-mask was given a very small value.", cep->ce_fileptr->cf_filename, cep->ce_varlinenum); } } else if (!strcmp(cep->ce_varname, "hostname")) { if (has_hostname) { config_warn_duplicate(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, "allow::hostname"); continue; } has_hostname = 1; } else if (!strcmp(cep->ce_varname, "password")) { if (has_password) { config_warn_duplicate(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, "allow::password"); continue; } has_password = 1; /* some auth check stuff? */ if (Auth_CheckError(cep) < 0) errors++; } else if (!strcmp(cep->ce_varname, "class")) { if (has_class) { config_warn_duplicate(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, "allow::class"); continue; } has_class = 1; } else if (!strcmp(cep->ce_varname, "redirect-server")) { if (has_redirectserver) { config_warn_duplicate(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, "allow::redirect-server"); continue; } has_redirectserver = 1; } else if (!strcmp(cep->ce_varname, "redirect-port")) { if (has_redirectport) { config_warn_duplicate(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, "allow::redirect-port"); continue; } has_redirectport = 1; } else if (!strcmp(cep->ce_varname, "options")) { if (has_options) { config_warn_duplicate(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, "allow::options"); continue; } has_options = 1; for (cepp = cep->ce_entries; cepp; cepp = cepp->ce_next) { if (!strcmp(cepp->ce_varname, "noident")) {} else if (!strcmp(cepp->ce_varname, "useip")) {} else if (!strcmp(cepp->ce_varname, "ssl")) {} else if (!strcmp(cepp->ce_varname, "nopasscont")) {} else { config_error_unknownopt(cepp->ce_fileptr->cf_filename, cepp->ce_varlinenum, "allow", cepp->ce_varname); errors++; } } } else { config_error_unknown(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, "allow", cep->ce_varname); errors++; continue; } } if (!has_ip && !has_hostname) { config_error("%s:%d: allow block needs an allow::ip or allow::hostname", ce->ce_fileptr->cf_filename, ce->ce_varlinenum); errors++; } if (has_ip && has_hostname) { config_warn("%s:%d: allow block has both allow::ip and allow::hostname which is no longer permitted.", ce->ce_fileptr->cf_filename, ce->ce_varlinenum); need_34_upgrade = 1; } if (!has_class) { config_error_missing(ce->ce_fileptr->cf_filename, ce->ce_varlinenum, "allow::class"); errors++; } if (!has_maxperip) { config_error_missing(ce->ce_fileptr->cf_filename, ce->ce_varlinenum, "allow::maxperip"); errors++; } return errors; } int _conf_allow_channel(ConfigFile *conf, ConfigEntry *ce) { ConfigItem_allow_channel *allow = NULL; ConfigEntry *cep; char *class = NULL; /* First, search for ::class, if any */ for (cep = ce->ce_entries; cep; cep = cep->ce_next) { if (!strcmp(cep->ce_varname, "class")) class = cep->ce_vardata; } for (cep = ce->ce_entries; cep; cep = cep->ce_next) { if (!strcmp(cep->ce_varname, "channel")) { /* This way, we permit multiple ::channel items in one allow block */ allow = MyMallocEx(sizeof(ConfigItem_allow_channel)); safestrdup(allow->channel, cep->ce_vardata); if (class) safestrdup(allow->class, class); AddListItem(allow, conf_allow_channel); } } return 1; } int _test_allow_channel(ConfigFile *conf, ConfigEntry *ce) { ConfigEntry *cep; int errors = 0; char has_channel = 0, has_class = 0; for (cep = ce->ce_entries; cep; cep = cep->ce_next) { if (config_is_blankorempty(cep, "allow channel")) { errors++; continue; } if (!strcmp(cep->ce_varname, "channel")) { has_channel = 1; } else if (!strcmp(cep->ce_varname, "class")) { if (has_class) { config_warn_duplicate(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, "allow channel::class"); continue; } has_class = 1; } else { config_error_unknown(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, "allow channel", cep->ce_varname); errors++; } } if (!has_channel) { config_error_missing(ce->ce_fileptr->cf_filename, ce->ce_varlinenum, "allow channel::channel"); errors++; } return errors; } int _conf_allow_dcc(ConfigFile *conf, ConfigEntry *ce) { ConfigItem_allow_dcc *allow = NULL; ConfigEntry *cep; allow = MyMallocEx(sizeof(ConfigItem_allow_dcc)); for (cep = ce->ce_entries; cep; cep = cep->ce_next) { if (!strcmp(cep->ce_varname, "filename")) safestrdup(allow->filename, cep->ce_vardata); else if (!strcmp(cep->ce_varname, "soft")) { int x = config_checkval(cep->ce_vardata,CFG_YESNO); if (x) allow->flag.type = DCCDENY_SOFT; } } AddListItem(allow, conf_allow_dcc); return 1; } int _test_allow_dcc(ConfigFile *conf, ConfigEntry *ce) { ConfigEntry *cep; int errors = 0, has_filename = 0, has_soft = 0; for (cep = ce->ce_entries; cep; cep = cep->ce_next) { if (config_is_blankorempty(cep, "allow dcc")) { errors++; continue; } if (!strcmp(cep->ce_varname, "filename")) { if (has_filename) { config_warn_duplicate(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, "allow dcc::filename"); continue; } has_filename = 1; } else if (!strcmp(cep->ce_varname, "soft")) { if (has_soft) { config_warn_duplicate(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, "allow dcc::soft"); continue; } has_soft = 1; } else { config_error_unknown(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, "allow dcc", cep->ce_varname); errors++; } } if (!has_filename) { config_error_missing(ce->ce_fileptr->cf_filename, ce->ce_varlinenum, "allow dcc::filename"); errors++; } return errors; } void create_tkl_except_ii(char *mask, char *type) { ConfigItem_except *ca; NameValue *opf; ca = MyMallocEx(sizeof(ConfigItem_except)); ca->mask = strdup(mask); opf = config_binary_flags_search(ExceptTklFlags, type, ARRAY_SIZEOF(ExceptTklFlags)); ca->type = opf->flag; ca->flag.type = CONF_EXCEPT_TKL; AddListItem(ca, conf_except); } void create_tkl_except(char *mask, char *type) { if (!strcmp(type, "all")) { /* Special treatment */ int i; for (i = 0; i < ARRAY_SIZEOF(ExceptTklFlags); i++) if (ExceptTklFlags[i].flag) create_tkl_except_ii(mask, ExceptTklFlags[i].name); } else create_tkl_except_ii(mask, type); } int _conf_except(ConfigFile *conf, ConfigEntry *ce) { ConfigEntry *cep; ConfigItem_except *ca; Hook *h; if (!strcmp(ce->ce_vardata, "ban")) { for (cep = ce->ce_entries; cep; cep = cep->ce_next) { if (!strcmp(cep->ce_varname, "mask")) { ca = MyMallocEx(sizeof(ConfigItem_except)); ca->mask = strdup(cep->ce_vardata); ca->flag.type = CONF_EXCEPT_BAN; AddListItem(ca, conf_except); } else { } } } else if (!strcmp(ce->ce_vardata, "throttle")) { for (cep = ce->ce_entries; cep; cep = cep->ce_next) { if (!strcmp(cep->ce_varname, "mask")) { ca = MyMallocEx(sizeof(ConfigItem_except)); ca->mask = strdup(cep->ce_vardata); ca->flag.type = CONF_EXCEPT_THROTTLE; AddListItem(ca, conf_except); } else { } } } else if (!strcmp(ce->ce_vardata, "tkl")) { ConfigEntry *mask = NULL, *type = NULL; for (cep = ce->ce_entries; cep; cep = cep->ce_next) { if (!strcmp(cep->ce_varname, "mask")) mask = cep; else if (!strcmp(cep->ce_varname, "type")) type = cep; } if (type->ce_vardata) create_tkl_except(mask->ce_vardata, type->ce_vardata); else { ConfigEntry *cepp; for (cepp = type->ce_entries; cepp; cepp = cepp->ce_next) create_tkl_except(mask->ce_vardata, cepp->ce_varname); } } else { int value; for (h = Hooks[HOOKTYPE_CONFIGRUN]; h; h = h->next) { value = (*(h->func.intfunc))(conf,ce,CONFIG_EXCEPT); if (value == 1) break; } } return 1; } int _test_except(ConfigFile *conf, ConfigEntry *ce) { ConfigEntry *cep; int errors = 0; Hook *h; char has_mask = 0; if (!ce->ce_vardata) { config_error("%s:%i: except without type", ce->ce_fileptr->cf_filename, ce->ce_varlinenum); return 1; } if (!strcmp(ce->ce_vardata, "ban")) { for (cep = ce->ce_entries; cep; cep = cep->ce_next) { if (config_is_blankorempty(cep, "except ban")) { errors++; continue; } if (!strcmp(cep->ce_varname, "mask")) { if (has_mask) { config_warn_duplicate(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, "except ban::mask"); continue; } has_mask = 1; } else { config_error_unknown(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, "except ban", cep->ce_varname); errors++; continue; } } if (!has_mask) { config_error_missing(ce->ce_fileptr->cf_filename, ce->ce_varlinenum, "except ban::mask"); errors++; } return errors; } else if (!strcmp(ce->ce_vardata, "throttle")) { for (cep = ce->ce_entries; cep; cep = cep->ce_next) { if (config_is_blankorempty(cep, "except throttle")) { errors++; continue; } if (!strcmp(cep->ce_varname, "mask")) { if (has_mask) { config_warn_duplicate(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, "except throttle::mask"); continue; } has_mask = 1; } else { config_error_unknown(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, "except throttle", cep->ce_varname); errors++; continue; } } if (!has_mask) { config_error_missing(ce->ce_fileptr->cf_filename, ce->ce_varlinenum, "except throttle::mask"); errors++; } return errors; } else if (!strcmp(ce->ce_vardata, "tkl")) { char has_type = 0; for (cep = ce->ce_entries; cep; cep = cep->ce_next) { if (!cep->ce_varname) { config_error_blank(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, "except tkl"); errors++; continue; } if (!strcmp(cep->ce_varname, "mask")) { if (!cep->ce_vardata) { config_error_empty(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, "except tkl", "mask"); errors++; continue; } if (has_mask) { config_warn_duplicate(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, "except tkl::mask"); continue; } has_mask = 1; } else if (!strcmp(cep->ce_varname, "type")) { if (has_type) { config_warn_duplicate(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, "except tkl::type"); continue; } if (cep->ce_vardata) { if (!strcmp(cep->ce_vardata, "tkline") || !strcmp(cep->ce_vardata, "tzline")) { config_error("%s:%i: except tkl of type %s is" " deprecated. Use except ban {}" " instead", cep->ce_fileptr->cf_filename, cep->ce_varlinenum, cep->ce_vardata); errors++; } if (!config_binary_flags_search(ExceptTklFlags, cep->ce_vardata, ARRAY_SIZEOF(ExceptTklFlags))) { config_error("%s:%i: unknown except tkl type %s", cep->ce_fileptr->cf_filename, cep->ce_varlinenum, cep->ce_vardata); return 1; } } else if (cep->ce_entries) { ConfigEntry *cepp; for (cepp = cep->ce_entries; cepp; cepp = cepp->ce_next) { if (!strcmp(cepp->ce_varname, "tkline") || !strcmp(cepp->ce_varname, "tzline")) { config_error("%s:%i: except tkl of type %s is" " deprecated. Use except ban {}" " instead", cepp->ce_fileptr->cf_filename, cepp->ce_varlinenum, cepp->ce_varname); errors++; } if (!config_binary_flags_search(ExceptTklFlags, cepp->ce_varname, ARRAY_SIZEOF(ExceptTklFlags))) { config_error("%s:%i: unknown except tkl type %s", cepp->ce_fileptr->cf_filename, cepp->ce_varlinenum, cepp->ce_varname); return 1; } } } else { config_error_empty(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, "except tkl", "type"); errors++; continue; } has_type = 1; } else { config_error_unknown(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, "except tkl", cep->ce_varname); errors++; continue; } } if (!has_mask) { config_error("%s:%i: except tkl without mask item", ce->ce_fileptr->cf_filename, ce->ce_varlinenum); return 1; } if (!has_type) { config_error("%s:%i: except tkl without type item", ce->ce_fileptr->cf_filename, ce->ce_varlinenum); return 1; } return errors; } else { int used = 0; for (h = Hooks[HOOKTYPE_CONFIGTEST]; h; h = h->next) { int value, errs = 0; if (h->owner && !(h->owner->flags & MODFLAG_TESTING) && !(h->owner->options & MOD_OPT_PERM)) continue; value = (*(h->func.intfunc))(conf,ce,CONFIG_EXCEPT,&errs); if (value == 2) used = 1; if (value == 1) { used = 1; break; } if (value == -1) { used = 1; errors += errs; break; } if (value == -2) { used = 1; errors += errs; } } if (!used) { config_error("%s:%i: unknown except type %s", ce->ce_fileptr->cf_filename, ce->ce_varlinenum, ce->ce_vardata); return 1; } } return errors; } /* * vhost {} block parser */ int _conf_vhost(ConfigFile *conf, ConfigEntry *ce) { ConfigItem_vhost *vhost; ConfigItem_mask *mask; ConfigEntry *cep, *cepp; vhost = MyMallocEx(sizeof(ConfigItem_vhost)); for (cep = ce->ce_entries; cep; cep = cep->ce_next) { if (!strcmp(cep->ce_varname, "vhost")) { char *user, *host; user = strtok(cep->ce_vardata, "@"); host = strtok(NULL, ""); if (!host) vhost->virthost = strdup(user); else { vhost->virtuser = strdup(user); vhost->virthost = strdup(host); } } else if (!strcmp(cep->ce_varname, "login")) vhost->login = strdup(cep->ce_vardata); else if (!strcmp(cep->ce_varname, "password")) vhost->auth = Auth_ConvertConf2AuthStruct(cep); else if (!strcmp(cep->ce_varname, "mask")) { unreal_add_masks(&vhost->mask, cep); } else if (!strcmp(cep->ce_varname, "swhois")) { SWhois *s; if (cep->ce_entries) { for (cepp = cep->ce_entries; cepp; cepp = cepp->ce_next) { s = MyMallocEx(sizeof(SWhois)); s->line = strdup(cepp->ce_varname); s->setby = strdup("vhost"); AddListItem(s, vhost->swhois); } } else if (cep->ce_vardata) { s = MyMallocEx(sizeof(SWhois)); s->line = strdup(cep->ce_vardata); s->setby = strdup("vhost"); AddListItem(s, vhost->swhois); } } } AddListItem(vhost, conf_vhost); return 1; } int _test_vhost(ConfigFile *conf, ConfigEntry *ce) { int errors = 0; ConfigEntry *cep; char has_vhost = 0, has_login = 0, has_password = 0, has_swhois = 0, has_mask = 0; for (cep = ce->ce_entries; cep; cep = cep->ce_next) { if (!cep->ce_varname) { config_error_blank(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, "vhost"); errors++; continue; } if (!strcmp(cep->ce_varname, "vhost")) { char *at, *tmp, *host; if (has_vhost) { config_warn_duplicate(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, "vhost::vhost"); continue; } has_vhost = 1; if (!cep->ce_vardata) { config_error_empty(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, "vhost", "vhost"); errors++; continue; } if ((at = strchr(cep->ce_vardata, '@'))) { for (tmp = cep->ce_vardata; tmp != at; tmp++) { if (*tmp == '~' && tmp == cep->ce_vardata) continue; if (!isallowed(*tmp)) break; } if (tmp != at) { config_error("%s:%i: vhost::vhost contains an invalid ident", cep->ce_fileptr->cf_filename, cep->ce_varlinenum); errors++; } host = at+1; } else host = cep->ce_vardata; if (!*host) { config_error("%s:%i: vhost::vhost does not have a host set", cep->ce_fileptr->cf_filename, cep->ce_varlinenum); errors++; } else { if (!valid_host(host)) { config_error("%s:%i: vhost::vhost contains an invalid host", cep->ce_fileptr->cf_filename, cep->ce_varlinenum); errors++; } } } else if (!strcmp(cep->ce_varname, "login")) { if (has_login) { config_warn_duplicate(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, "vhost::login"); } has_login = 1; if (!cep->ce_vardata) { config_error_empty(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, "vhost", "login"); errors++; continue; } } else if (!strcmp(cep->ce_varname, "password")) { if (has_password) { config_warn_duplicate(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, "vhost::password"); } has_password = 1; if (!cep->ce_vardata) { config_error_empty(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, "vhost", "password"); errors++; continue; } if (Auth_CheckError(cep) < 0) errors++; } else if (!strcmp(cep->ce_varname, "from")) { ConfigEntry *cepp; config_error("%s:%i: vhost::from::userhost is now called oper::mask", cep->ce_fileptr->cf_filename, cep->ce_varlinenum); errors++; need_34_upgrade = 1; continue; } else if (!strcmp(cep->ce_varname, "mask")) { has_mask = 1; } else if (!strcmp(cep->ce_varname, "swhois")) { /* multiple is ok */ } else { config_error_unknown(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, "vhost", cep->ce_varname); errors++; } } if (!has_vhost) { config_error_missing(ce->ce_fileptr->cf_filename, ce->ce_varlinenum, "vhost::vhost"); errors++; } if (!has_login) { config_error_missing(ce->ce_fileptr->cf_filename, ce->ce_varlinenum, "vhost::login"); errors++; } if (!has_password) { config_error_missing(ce->ce_fileptr->cf_filename, ce->ce_varlinenum, "vhost::password"); errors++; } if (!has_mask) { config_error_missing(ce->ce_fileptr->cf_filename, ce->ce_varlinenum, "vhost::mask"); errors++; } // TODO: 3.2.x -> 3.4.x upgrading hints return errors; } int _conf_spamfilter(ConfigFile *conf, ConfigEntry *ce) { ConfigEntry *cep; ConfigEntry *cepp; aTKline *nl = MyMallocEx(sizeof(aTKline)); char *word = NULL, *reason = NULL, *bantime = NULL; int action = 0, target = 0; char has_reason = 0, has_bantime = 0; int match_type = 0; for (cep = ce->ce_entries; cep; cep = cep->ce_next) { if (!strcmp(cep->ce_varname, "match")) { nl->reason = strdup(cep->ce_vardata); word = cep->ce_vardata; } else if (!strcmp(cep->ce_varname, "target")) { if (cep->ce_vardata) target = spamfilter_getconftargets(cep->ce_vardata); else { for (cepp = cep->ce_entries; cepp; cepp = cepp->ce_next) target |= spamfilter_getconftargets(cepp->ce_varname); } } else if (!strcmp(cep->ce_varname, "action")) { action = banact_stringtoval(cep->ce_vardata); nl->hostmask = strdup(cep->ce_vardata); } else if (!strcmp(cep->ce_varname, "reason")) { has_reason = 1; reason = cep->ce_vardata; } else if (!strcmp(cep->ce_varname, "ban-time")) { has_bantime = 1; bantime = cep->ce_vardata; } else if (!strcmp(cep->ce_varname, "match-type")) { match_type = unreal_match_method_strtoval(cep->ce_vardata); } } nl->type = TKL_SPAMF; nl->expire_at = 0; nl->set_at = TStime(); strlcpy(nl->usermask, spamfilter_target_inttostring(target), sizeof(nl->usermask)); nl->subtype = target; nl->setby = BadPtr(me.name) ? NULL : strdup(me.name); /* Hmm! */ nl->ptr.spamf = MyMallocEx(sizeof(Spamfilter)); nl->ptr.spamf->expr = unreal_create_match(match_type, word, NULL); nl->ptr.spamf->action = action; if (has_reason && reason) nl->ptr.spamf->tkl_reason = strdup(unreal_encodespace(reason)); else nl->ptr.spamf->tkl_reason = strdup(""); if (has_bantime) nl->ptr.spamf->tkl_duration = config_checkval(bantime, CFG_TIME); else nl->ptr.spamf->tkl_duration = (SPAMFILTER_BAN_TIME ? SPAMFILTER_BAN_TIME : 86400); AddListItem(nl, tklines[tkl_hash('f')]); return 1; } int _test_spamfilter(ConfigFile *conf, ConfigEntry *ce) { ConfigEntry *cep, *cepp; int errors = 0; char *match = NULL, *reason = NULL; char has_target = 0, has_match = 0, has_action = 0, has_reason = 0, has_bantime = 0, has_match_type = 0; int match_type = 0; for (cep = ce->ce_entries; cep; cep = cep->ce_next) { if (!cep->ce_varname) { config_error_blank(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, "spamfilter"); errors++; continue; } if (!strcmp(cep->ce_varname, "target")) { if (has_target) { config_warn_duplicate(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, "spamfilter::target"); continue; } has_target = 1; if (cep->ce_vardata) { if (!spamfilter_getconftargets(cep->ce_vardata)) { config_error("%s:%i: unknown spamfiler target type '%s'", cep->ce_fileptr->cf_filename, cep->ce_varlinenum, cep->ce_vardata); errors++; } } else if (cep->ce_entries) { for (cepp = cep->ce_entries; cepp; cepp = cepp->ce_next) { if (!cepp->ce_varname) { config_error_blank(cepp->ce_fileptr->cf_filename, cepp->ce_varlinenum, "spamfilter::target"); errors++; continue; } if (!spamfilter_getconftargets(cepp->ce_varname)) { config_error("%s:%i: unknown spamfiler target type '%s'", cepp->ce_fileptr->cf_filename, cepp->ce_varlinenum, cepp->ce_varname); errors++; } } } else { config_error_empty(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, "spamfilter", cep->ce_varname); errors++; } continue; } if (!cep->ce_vardata) { config_error_empty(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, "spamfilter", cep->ce_varname); errors++; continue; } if (!strcmp(cep->ce_varname, "reason")) { if (has_reason) { config_warn_duplicate(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, "spamfilter::reason"); continue; } has_reason = 1; reason = cep->ce_vardata; } else if (!strcmp(cep->ce_varname, "match")) { if (has_match) { config_warn_duplicate(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, "spamfilter::match"); continue; } has_match = 1; match = cep->ce_vardata; } else if (!strcmp(cep->ce_varname, "action")) { if (has_action) { config_warn_duplicate(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, "spamfilter::action"); continue; } has_action = 1; if (!banact_stringtoval(cep->ce_vardata)) { config_error("%s:%i: spamfilter::action has unknown action type '%s'", cep->ce_fileptr->cf_filename, cep->ce_varlinenum, cep->ce_vardata); errors++; } } else if (!strcmp(cep->ce_varname, "ban-time")) { if (has_bantime) { config_warn_duplicate(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, "spamfilter::ban-time"); continue; } has_bantime = 1; } else if (!strcmp(cep->ce_varname, "match-type")) { if (has_match_type) { config_warn_duplicate(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, "spamfilter::match-type"); continue; } match_type = unreal_match_method_strtoval(cep->ce_vardata); if (match_type == 0) { config_error("%s:%i: spamfilter::match-type: unknown match type '%s', " "should be one of: 'simple', 'regex' or 'posix'", cep->ce_fileptr->cf_filename, cep->ce_varlinenum, cep->ce_vardata); errors++; continue; } has_match_type = 1; } else { config_error_unknown(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, "spamfilter", cep->ce_varname); errors++; continue; } } if (match && match_type) { aMatch *m; char *err; m = unreal_create_match(match_type, match, &err); if (!m) { config_error("%s:%i: spamfilter::match contains an invalid regex: %s", ce->ce_fileptr->cf_filename, ce->ce_varlinenum, err); errors++; } else { unreal_delete_match(m); } } if (!has_match) { config_error_missing(ce->ce_fileptr->cf_filename, ce->ce_varlinenum, "spamfilter::match"); errors++; } if (!has_target) { config_error_missing(ce->ce_fileptr->cf_filename, ce->ce_varlinenum, "spamfilter::target"); errors++; } if (!has_action) { config_error_missing(ce->ce_fileptr->cf_filename, ce->ce_varlinenum, "spamfilter::action"); errors++; } if (match && reason && (strlen(match) + strlen(reason) > 505)) { config_error("%s:%i: spamfilter block problem: match + reason field are together over 505 bytes, " "please choose a shorter regex or reason", ce->ce_fileptr->cf_filename, ce->ce_varlinenum); errors++; } if (!has_match_type) { config_error_missing(ce->ce_fileptr->cf_filename, ce->ce_varlinenum, "spamfilter::match-type"); errors++; } if (!has_match_type && !has_match && has_action && has_target) { need_34_upgrade = 1; } return errors; } int _conf_help(ConfigFile *conf, ConfigEntry *ce) { ConfigEntry *cep; ConfigItem_help *ca; aMotdLine *last = NULL, *temp; ca = MyMallocEx(sizeof(ConfigItem_help)); if (!ce->ce_vardata) ca->command = NULL; else ca->command = strdup(ce->ce_vardata); for (cep = ce->ce_entries; cep; cep = cep->ce_next) { temp = MyMallocEx(sizeof(aMotdLine)); temp->line = strdup(cep->ce_varname); temp->next = NULL; if (!last) ca->text = temp; else last->next = temp; last = temp; } AddListItem(ca, conf_help); return 1; } int _test_help(ConfigFile *conf, ConfigEntry *ce) { int errors = 0; ConfigEntry *cep; if (!ce->ce_entries) { config_error("%s:%i: empty help block", ce->ce_fileptr->cf_filename, ce->ce_varlinenum); return 1; } for (cep = ce->ce_entries; cep; cep = cep->ce_next) { if (!cep->ce_varname) { config_error_blank(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, "help"); errors++; continue; } } return errors; } int _conf_log(ConfigFile *conf, ConfigEntry *ce) { ConfigEntry *cep, *cepp; ConfigItem_log *ca; NameValue *ofp = NULL; ca = MyMallocEx(sizeof(ConfigItem_log)); ca->logfd = -1; safestrdup(ca->file, ce->ce_vardata); for (cep = ce->ce_entries; cep; cep = cep->ce_next) { if (!strcmp(cep->ce_varname, "maxsize")) { ca->maxsize = config_checkval(cep->ce_vardata,CFG_SIZE); } else if (!strcmp(cep->ce_varname, "flags")) { for (cepp = cep->ce_entries; cepp; cepp = cepp->ce_next) { if ((ofp = config_binary_flags_search(_LogFlags, cepp->ce_varname, ARRAY_SIZEOF(_LogFlags)))) ca->flags |= ofp->flag; } } } AddListItem(ca, conf_log); return 1; } int _test_log(ConfigFile *conf, ConfigEntry *ce) { int fd, errors = 0; ConfigEntry *cep, *cepp; char has_flags = 0, has_maxsize = 0; if (!ce->ce_vardata) { config_error("%s:%i: log block without filename", ce->ce_fileptr->cf_filename, ce->ce_varlinenum); return 1; } if (!ce->ce_entries) { config_error("%s:%i: empty log block", ce->ce_fileptr->cf_filename, ce->ce_varlinenum); return 1; } convert_to_absolute_path(&ce->ce_vardata, LOGDIR); for (cep = ce->ce_entries; cep; cep = cep->ce_next) { if (!cep->ce_varname) { config_error_blank(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, "log"); errors++; continue; } if (!strcmp(cep->ce_varname, "flags")) { if (has_flags) { config_warn_duplicate(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, "log::flags"); continue; } has_flags = 1; if (!cep->ce_entries) { config_error_empty(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, "log", cep->ce_varname); errors++; continue; } for (cepp = cep->ce_entries; cepp; cepp = cepp->ce_next) { if (!cepp->ce_varname) { config_error_blank(cepp->ce_fileptr->cf_filename, cepp->ce_varlinenum, "log::flags"); errors++; continue; } if (!config_binary_flags_search(_LogFlags, cepp->ce_varname, ARRAY_SIZEOF(_LogFlags))) { config_error_unknownflag(cepp->ce_fileptr->cf_filename, cepp->ce_varlinenum, "log", cepp->ce_varname); errors++; } } } else if (!strcmp(cep->ce_varname, "maxsize")) { if (has_maxsize) { config_warn_duplicate(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, "log::maxsize"); continue; } has_maxsize = 1; if (!cep->ce_vardata) { config_error_empty(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, "log", cep->ce_varname); errors++; } } else { config_error_unknown(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, "log", cep->ce_varname); errors++; continue; } } if (!has_flags) { config_error_missing(ce->ce_fileptr->cf_filename, ce->ce_varlinenum, "log::flags"); errors++; } if ((fd = fd_fileopen(ce->ce_vardata, O_WRONLY|O_CREAT)) == -1) { config_error("%s:%i: Couldn't open logfile (%s) for writing: %s", ce->ce_fileptr->cf_filename, ce->ce_varlinenum, ce->ce_vardata, strerror(errno)); errors++; } else fd_close(fd); return errors; } int _conf_link(ConfigFile *conf, ConfigEntry *ce) { ConfigEntry *cep, *cepp, *ceppp; ConfigItem_link *link = NULL; NameValue *ofp; link = (ConfigItem_link *) MyMallocEx(sizeof(ConfigItem_link)); link->servername = strdup(ce->ce_vardata); for (cep = ce->ce_entries; cep; cep = cep->ce_next) { if (!strcmp(cep->ce_varname, "incoming")) { for (cepp = cep->ce_entries; cepp; cepp = cepp->ce_next) { if (!strcmp(cepp->ce_varname, "mask")) { unreal_add_masks(&link->incoming.mask, cepp); } } } else if (!strcmp(cep->ce_varname, "outgoing")) { for (cepp = cep->ce_entries; cepp; cepp = cepp->ce_next) { if (!strcmp(cepp->ce_varname, "bind-ip")) safestrdup(link->outgoing.bind_ip, cepp->ce_vardata); else if (!strcmp(cepp->ce_varname, "hostname")) safestrdup(link->outgoing.hostname, cepp->ce_vardata); else if (!strcmp(cepp->ce_varname, "port")) link->outgoing.port = atoi(cepp->ce_vardata); else if (!strcmp(cepp->ce_varname, "options")) { /* TODO: options still need to be split */ link->outgoing.options = 0; for (ceppp = cepp->ce_entries; ceppp; ceppp = ceppp->ce_next) { if ((ofp = config_binary_flags_search(_LinkFlags, ceppp->ce_varname, ARRAY_SIZEOF(_LinkFlags)))) link->outgoing.options |= ofp->flag; } } } } else if (!strcmp(cep->ce_varname, "password")) link->auth = Auth_ConvertConf2AuthStruct(cep); else if (!strcmp(cep->ce_varname, "hub")) safestrdup(link->hub, cep->ce_vardata); else if (!strcmp(cep->ce_varname, "leaf")) safestrdup(link->leaf, cep->ce_vardata); else if (!strcmp(cep->ce_varname, "leaf-depth") || !strcmp(cep->ce_varname, "leafdepth")) link->leaf_depth = atoi(cep->ce_vardata); else if (!strcmp(cep->ce_varname, "class")) { link->class = Find_class(cep->ce_vardata); if (!link->class || (link->class->flag.temporary == 1)) { config_status("%s:%i: illegal link::class, unknown class '%s' using default of class 'default'", cep->ce_fileptr->cf_filename, cep->ce_varlinenum, cep->ce_vardata); link->class = default_class; } link->class->xrefcount++; } else if (!strcmp(cep->ce_varname, "ciphers")) link->ciphers = strdup(cep->ce_vardata); else if (!strcmp(cep->ce_varname, "options")) { link->options = 0; for (cepp = cep->ce_entries; cepp; cepp = cepp->ce_next) { if ((ofp = config_binary_flags_search(_LinkFlags, cepp->ce_varname, ARRAY_SIZEOF(_LinkFlags)))) link->options |= ofp->flag; } } } /* The default is 'hub *', unless you specify leaf or hub manually. */ if (!link->hub && !link->leaf) link->hub = strdup("*"); AddListItem(link, conf_link); return 0; } /** Helper function for erroring on duplicate items. * TODO: make even more friendy for dev's? */ int config_detect_duplicate(int *var, ConfigEntry *ce, int *errors) { if (*var) { config_error("%s:%d: Duplicate %s directive", ce->ce_fileptr->cf_filename, ce->ce_varlinenum, ce->ce_varname); (*errors)++; return 1; } else { *var = 1; } return 0; } int _test_link(ConfigFile *conf, ConfigEntry *ce) { ConfigEntry *cep, *cepp, *ceppp; NameValue *ofp; int errors = 0; int has_incoming = 0, has_incoming_mask = 0, has_outgoing = 0; int has_outgoing_bind_ip = 0, has_outgoing_hostname = 0, has_outgoing_port = 0; int has_outgoing_options = 0, has_hub = 0, has_leaf = 0, has_leaf_depth = 0; int has_password = 0, has_class = 0, has_ciphers = 0, has_options = 0; if (!ce->ce_vardata) { config_error("%s:%i: link without servername. Expected: link servername { ... }", ce->ce_fileptr->cf_filename, ce->ce_varlinenum); return 1; } if (!strchr(ce->ce_vardata, '.')) { config_error("%s:%i: link: bogus server name. Expected: link servername { ... }", ce->ce_fileptr->cf_filename, ce->ce_varlinenum); return 1; } for (cep = ce->ce_entries; cep; cep = cep->ce_next) { if (!strcmp(cep->ce_varname, "incoming")) { config_detect_duplicate(&has_incoming, cep, &errors); for (cepp = cep->ce_entries; cepp; cepp = cepp->ce_next) { if (!strcmp(cepp->ce_varname, "mask")) { if (cepp->ce_vardata || cepp->ce_entries) has_incoming_mask = 1; } } } else if (!strcmp(cep->ce_varname, "outgoing")) { config_detect_duplicate(&has_outgoing, cep, &errors); for (cepp = cep->ce_entries; cepp; cepp = cepp->ce_next) { if (!strcmp(cepp->ce_varname, "bind-ip")) { config_detect_duplicate(&has_outgoing_bind_ip, cepp, &errors); // todo: ipv4 vs ipv6 } else if (!strcmp(cepp->ce_varname, "hostname")) { config_detect_duplicate(&has_outgoing_hostname, cepp, &errors); if (strchr(cepp->ce_vardata, '*') || strchr(cepp->ce_vardata, '?')) { config_error("%s:%i: hostname in link::outgoing(!) cannot contain wildcards", cepp->ce_fileptr->cf_filename, cepp->ce_varlinenum); errors++; } } else if (!strcmp(cepp->ce_varname, "port")) { config_detect_duplicate(&has_outgoing_port, cepp, &errors); } else if (!strcmp(cepp->ce_varname, "options")) { config_detect_duplicate(&has_outgoing_options, cepp, &errors); for (ceppp = cepp->ce_entries; ceppp; ceppp = ceppp->ce_next) { if (!strcmp(ceppp->ce_varname, "autoconnect")) ; else if (!strcmp(ceppp->ce_varname, "ssl")) ; else if (!strcmp(ceppp->ce_varname, "insecure")) ; else { config_error_unknownopt(ceppp->ce_fileptr->cf_filename, ceppp->ce_varlinenum, "link::outgoing", ceppp->ce_varname); errors++; } // TODO: validate more options (?) and use list rather than code here... } } } } else if (!strcmp(cep->ce_varname, "password")) { config_detect_duplicate(&has_password, cep, &errors); if (Auth_CheckError(cep) < 0) { errors++; } else { anAuthStruct *auth = Auth_ConvertConf2AuthStruct(cep); /* hm. would be nicer if handled @auth-system I think. ah well.. */ if ((auth->type != AUTHTYPE_PLAINTEXT) && (auth->type != AUTHTYPE_SSL_CLIENTCERT) && (auth->type != AUTHTYPE_SSL_CLIENTCERTFP)) { config_error("%s:%i: password in link block should be plaintext OR should be the " "SSL fingerprint of the remote link (=better)", /* TODO: mention some faq or wiki item for more information */ cep->ce_fileptr->cf_filename, cep->ce_varlinenum); errors++; } Auth_DeleteAuthStruct(auth); } } else if (!strcmp(cep->ce_varname, "hub")) { config_detect_duplicate(&has_hub, cep, &errors); } else if (!strcmp(cep->ce_varname, "leaf")) { config_detect_duplicate(&has_leaf, cep, &errors); } else if (!strcmp(cep->ce_varname, "leaf-depth") || !strcmp(cep->ce_varname, "leafdepth")) { config_detect_duplicate(&has_leaf_depth, cep, &errors); } else if (!strcmp(cep->ce_varname, "class")) { config_detect_duplicate(&has_class, cep, &errors); } else if (!strcmp(cep->ce_varname, "ciphers")) { config_detect_duplicate(&has_ciphers, cep, &errors); } else if (!strcmp(cep->ce_varname, "options")) { config_detect_duplicate(&has_options, cep, &errors); for (cepp = cep->ce_entries; cepp; cepp = cepp->ce_next) { if (!strcmp(cep->ce_varname, "quarantine")) ; else { config_error("%s:%d: link::options only has one possible option ('quarantine', rarely used). " "Option '%s' is unrecognized. " "Perhaps you meant to set an outgoing option in link::outgoing::options instead?", cepp->ce_fileptr->cf_filename, cepp->ce_varlinenum, cepp->ce_varname); errors++; } } } } if (!has_incoming && !has_outgoing) { config_error("%s:%d: link block needs at least an incoming or outgoing section.", ce->ce_fileptr->cf_filename, ce->ce_varlinenum); errors++; need_34_upgrade = 1; } if (has_incoming) { /* If we have an incoming sub-block then we need at least 'mask' and 'password' */ if (!has_incoming_mask) { config_error_missing(ce->ce_fileptr->cf_filename, ce->ce_varlinenum, "link::incoming::mask"); errors++; } } if (has_outgoing) { /* If we have an outgoing sub-block then we need at least a hostname and port */ if (!has_outgoing_hostname) { config_error_missing(ce->ce_fileptr->cf_filename, ce->ce_varlinenum, "link::outgoing::hostname"); errors++; } if (!has_outgoing_port) { config_error_missing(ce->ce_fileptr->cf_filename, ce->ce_varlinenum, "link::outgoing::port"); errors++; } } /* The only other generic options that are required are 'class' and 'password' */ if (!has_password) { config_error_missing(ce->ce_fileptr->cf_filename, ce->ce_varlinenum, "link::password"); errors++; } if (!has_class) { config_error_missing(ce->ce_fileptr->cf_filename, ce->ce_varlinenum, "link::class"); errors++; } return errors; } int _conf_ban(ConfigFile *conf, ConfigEntry *ce) { ConfigEntry *cep; ConfigItem_ban *ca; Hook *h; ca = MyMallocEx(sizeof(ConfigItem_ban)); if (!strcmp(ce->ce_vardata, "nick")) { aTKline *nl = MyMallocEx(sizeof(aTKline)); nl->type = TKL_NICK; for (cep = ce->ce_entries; cep; cep = cep->ce_next) { if (!strcmp(cep->ce_varname, "mask")) nl->hostmask = strdup(cep->ce_vardata); else if (!strcmp(cep->ce_varname, "reason")) nl->reason = strdup(cep->ce_vardata); } strcpy(nl->usermask, "*"); AddListItem(nl, tklines[tkl_hash('q')]); free(ca); return 0; } else if (!strcmp(ce->ce_vardata, "ip")) ca->flag.type = CONF_BAN_IP; else if (!strcmp(ce->ce_vardata, "server")) ca->flag.type = CONF_BAN_SERVER; else if (!strcmp(ce->ce_vardata, "user")) ca->flag.type = CONF_BAN_USER; else if (!strcmp(ce->ce_vardata, "realname")) ca->flag.type = CONF_BAN_REALNAME; else if (!strcmp(ce->ce_vardata, "version")) { ca->flag.type = CONF_BAN_VERSION; tempiConf.use_ban_version = 1; } else { int value; free(ca); /* ca isn't used, modules have their own list. */ for (h = Hooks[HOOKTYPE_CONFIGRUN]; h; h = h->next) { value = (*(h->func.intfunc))(conf,ce,CONFIG_BAN); if (value == 1) break; } return 0; } for (cep = ce->ce_entries; cep; cep = cep->ce_next) { if (!strcmp(cep->ce_varname, "mask")) { ca->mask = strdup(cep->ce_vardata); } else if (!strcmp(cep->ce_varname, "reason")) ca->reason = strdup(cep->ce_vardata); else if (!strcmp(cep->ce_varname, "action")) ca ->action = banact_stringtoval(cep->ce_vardata); } AddListItem(ca, conf_ban); return 0; } int _test_ban(ConfigFile *conf, ConfigEntry *ce) { ConfigEntry *cep; int errors = 0; Hook *h; char type = 0; char has_mask = 0, has_action = 0, has_reason = 0; if (!ce->ce_vardata) { config_error("%s:%i: ban without type", ce->ce_fileptr->cf_filename, ce->ce_varlinenum); return 1; } if (!strcmp(ce->ce_vardata, "nick")) {} else if (!strcmp(ce->ce_vardata, "ip")) {} else if (!strcmp(ce->ce_vardata, "server")) {} else if (!strcmp(ce->ce_vardata, "user")) {} else if (!strcmp(ce->ce_vardata, "realname")) {} else if (!strcmp(ce->ce_vardata, "version")) type = 'v'; else { int used = 0; for (h = Hooks[HOOKTYPE_CONFIGTEST]; h; h = h->next) { int value, errs = 0; if (h->owner && !(h->owner->flags & MODFLAG_TESTING) && !(h->owner->options & MOD_OPT_PERM)) continue; value = (*(h->func.intfunc))(conf,ce,CONFIG_BAN, &errs); if (value == 2) used = 1; if (value == 1) { used = 1; break; } if (value == -1) { used = 1; errors += errs; break; } if (value == -2) { used = 1; errors += errs; } } if (!used) { config_error("%s:%i: unknown ban type %s", ce->ce_fileptr->cf_filename, ce->ce_varlinenum, ce->ce_vardata); return 1; } return errors; } for (cep = ce->ce_entries; cep; cep = cep->ce_next) { if (config_is_blankorempty(cep, "ban")) { errors++; continue; } if (!strcmp(cep->ce_varname, "mask")) { if (has_mask) { config_warn_duplicate(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, "ban::mask"); continue; } has_mask = 1; } else if (!strcmp(cep->ce_varname, "reason")) { if (has_reason) { config_warn_duplicate(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, "ban::reason"); continue; } has_reason = 1; } else if (!strcmp(cep->ce_varname, "action")) { if (has_action) { config_warn_duplicate(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, "ban::action"); } has_action = 1; if (!banact_stringtoval(cep->ce_vardata)) { config_error("%s:%i: ban::action has unknown action type '%s'", cep->ce_fileptr->cf_filename, cep->ce_varlinenum, cep->ce_vardata); errors++; } } } if (!has_mask) { config_error_missing(ce->ce_fileptr->cf_filename, ce->ce_varlinenum, "ban::mask"); errors++; } if (!has_reason) { config_error_missing(ce->ce_fileptr->cf_filename, ce->ce_varlinenum, "ban::reason"); errors++; } if (has_action && type != 'v') { config_error("%s:%d: ban::action specified even though type is not 'version'", ce->ce_fileptr->cf_filename, ce->ce_varlinenum); errors++; } return errors; } int _conf_set(ConfigFile *conf, ConfigEntry *ce) { ConfigEntry *cep, *cepp, *ceppp; NameValue *ofl = NULL; Hook *h; for (cep = ce->ce_entries; cep; cep = cep->ce_next) { if (!strcmp(cep->ce_varname, "kline-address")) { safestrdup(tempiConf.kline_address, cep->ce_vardata); } if (!strcmp(cep->ce_varname, "gline-address")) { safestrdup(tempiConf.gline_address, cep->ce_vardata); } else if (!strcmp(cep->ce_varname, "modes-on-connect")) { tempiConf.conn_modes = (long) set_usermode(cep->ce_vardata); } else if (!strcmp(cep->ce_varname, "modes-on-oper")) { tempiConf.oper_modes = (long) set_usermode(cep->ce_vardata); } else if (!strcmp(cep->ce_varname, "modes-on-join")) { set_channelmodes(cep->ce_vardata, &tempiConf.modes_on_join, 0); } else if (!strcmp(cep->ce_varname, "snomask-on-oper")) { safestrdup(tempiConf.oper_snomask, cep->ce_vardata); } else if (!strcmp(cep->ce_varname, "snomask-on-connect")) { safestrdup(tempiConf.user_snomask, cep->ce_vardata); } else if (!strcmp(cep->ce_varname, "level-on-join")) { tempiConf.level_on_join = channellevel_to_int(cep->ce_vardata); } else if (!strcmp(cep->ce_varname, "static-quit")) { safestrdup(tempiConf.static_quit, cep->ce_vardata); } else if (!strcmp(cep->ce_varname, "static-part")) { safestrdup(tempiConf.static_part, cep->ce_vardata); } else if (!strcmp(cep->ce_varname, "who-limit")) { tempiConf.who_limit = atol(cep->ce_vardata); } else if (!strcmp(cep->ce_varname, "maxbans")) { tempiConf.maxbans = atol(cep->ce_vardata); } else if (!strcmp(cep->ce_varname, "maxbanlength")) { tempiConf.maxbanlength = atol(cep->ce_vardata); } else if (!strcmp(cep->ce_varname, "silence-limit")) { tempiConf.silence_limit = atol(cep->ce_vardata); if (loop.ircd_booted) IsupportSetValue(IsupportFind("SILENCE"), cep->ce_vardata); } else if (!strcmp(cep->ce_varname, "auto-join")) { safestrdup(tempiConf.auto_join_chans, cep->ce_vardata); } else if (!strcmp(cep->ce_varname, "oper-auto-join")) { safestrdup(tempiConf.oper_auto_join_chans, cep->ce_vardata); } else if (!strcmp(cep->ce_varname, "check-target-nick-bans")) { tempiConf.check_target_nick_bans = config_checkval(cep->ce_vardata, CFG_YESNO); } else if (!strcmp(cep->ce_varname, "ping-cookie")) { tempiConf.ping_cookie = config_checkval(cep->ce_vardata, CFG_YESNO); } else if (!strcmp(cep->ce_varname, "watch-away-notification")) { tempiConf.watch_away_notification = config_checkval(cep->ce_vardata, CFG_YESNO); } else if (!strcmp(cep->ce_varname, "uhnames")) { tempiConf.uhnames = config_checkval(cep->ce_vardata, CFG_YESNO); } else if (!strcmp(cep->ce_varname, "allow-userhost-change")) { if (!stricmp(cep->ce_vardata, "always")) tempiConf.userhost_allowed = UHALLOW_ALWAYS; else if (!stricmp(cep->ce_vardata, "never")) tempiConf.userhost_allowed = UHALLOW_NEVER; else if (!stricmp(cep->ce_vardata, "not-on-channels")) tempiConf.userhost_allowed = UHALLOW_NOCHANS; else tempiConf.userhost_allowed = UHALLOW_REJOIN; } else if (!strcmp(cep->ce_varname, "allowed-nickchars")) { for (cepp = cep->ce_entries; cepp; cepp=cepp->ce_next) charsys_add_language(cepp->ce_varname); } else if (!strcmp(cep->ce_varname, "channel-command-prefix")) { safestrdup(tempiConf.channel_command_prefix, cep->ce_vardata); } else if (!strcmp(cep->ce_varname, "restrict-usermodes")) { int i; char *p = MyMalloc(strlen(cep->ce_vardata) + 1), *x = p; /* The data should be something like 'Gw' or something, * but just in case users use '+Gw' then ignore the + (and -). */ for (i=0; i < strlen(cep->ce_vardata); i++) if ((cep->ce_vardata[i] != '+') && (cep->ce_vardata[i] != '-')) *x++ = cep->ce_vardata[i]; *x = '\0'; tempiConf.restrict_usermodes = p; } else if (!strcmp(cep->ce_varname, "restrict-channelmodes")) { int i; char *p = MyMalloc(strlen(cep->ce_vardata) + 1), *x = p; /* The data should be something like 'GL' or something, * but just in case users use '+GL' then ignore the + (and -). */ for (i=0; i < strlen(cep->ce_vardata); i++) if ((cep->ce_vardata[i] != '+') && (cep->ce_vardata[i] != '-')) *x++ = cep->ce_vardata[i]; *x = '\0'; tempiConf.restrict_channelmodes = p; } else if (!strcmp(cep->ce_varname, "restrict-extendedbans")) { safestrdup(tempiConf.restrict_extendedbans, cep->ce_vardata); } else if (!strcmp(cep->ce_varname, "new-linking-protocol")) { tempiConf.new_linking_protocol = atoi(cep->ce_vardata); } else if (!strcmp(cep->ce_varname, "anti-spam-quit-message-time")) { tempiConf.anti_spam_quit_message_time = config_checkval(cep->ce_vardata,CFG_TIME); } else if (!strcmp(cep->ce_varname, "oper-only-stats")) { if (!cep->ce_entries) { safestrdup(tempiConf.oper_only_stats, cep->ce_vardata); } else { for (cepp = cep->ce_entries; cepp; cepp = cepp->ce_next) { OperStat *os = MyMallocEx(sizeof(OperStat)); safestrdup(os->flag, cepp->ce_varname); AddListItem(os, tempiConf.oper_only_stats_ext); } } } else if (!strcmp(cep->ce_varname, "maxchannelsperuser")) { tempiConf.maxchannelsperuser = atoi(cep->ce_vardata); if (loop.ircd_booted) { char tmpbuf[512]; IsupportSetValue(IsupportFind("MAXCHANNELS"), cep->ce_vardata); ircsnprintf(tmpbuf, sizeof(tmpbuf), "#:%s", cep->ce_vardata); IsupportSetValue(IsupportFind("CHANLIMIT"), tmpbuf); } } else if (!strcmp(cep->ce_varname, "maxdccallow")) { tempiConf.maxdccallow = atoi(cep->ce_vardata); } else if (!strcmp(cep->ce_varname, "network-name")) { char *tmp; safestrdup(tempiConf.network.x_ircnetwork, cep->ce_vardata); for (tmp = cep->ce_vardata; *cep->ce_vardata; cep->ce_vardata++) { if (*cep->ce_vardata == ' ') *cep->ce_vardata='-'; } safestrdup(tempiConf.network.x_ircnet005, tmp); if (loop.ircd_booted) IsupportSetValue(IsupportFind("NETWORK"), tmp); cep->ce_vardata = tmp; } else if (!strcmp(cep->ce_varname, "default-server")) { safestrdup(tempiConf.network.x_defserv, cep->ce_vardata); } else if (!strcmp(cep->ce_varname, "services-server")) { safestrdup(tempiConf.network.x_services_name, cep->ce_vardata); } else if (!strcmp(cep->ce_varname, "sasl-server")) { safestrdup(tempiConf.network.x_sasl_server, cep->ce_vardata); } else if (!strcmp(cep->ce_varname, "stats-server")) { safestrdup(tempiConf.network.x_stats_server, cep->ce_vardata); } else if (!strcmp(cep->ce_varname, "help-channel")) { safestrdup(tempiConf.network.x_helpchan, cep->ce_vardata); } else if (!strcmp(cep->ce_varname, "hiddenhost-prefix")) { safestrdup(tempiConf.network.x_hidden_host, cep->ce_vardata); } else if (!strcmp(cep->ce_varname, "prefix-quit")) { if (*cep->ce_vardata == '0') { safestrdup(tempiConf.network.x_prefix_quit, ""); } else safestrdup(tempiConf.network.x_prefix_quit, cep->ce_vardata); } else if (!strcmp(cep->ce_varname, "link")) { for (cepp = cep->ce_entries; cepp; cepp = cepp->ce_next) { if (!strcmp(cepp->ce_varname, "bind-ip")) { safestrdup(tempiConf.link_bindip, cepp->ce_vardata); } } } else if (!strcmp(cep->ce_varname, "dns")) { for (cepp = cep->ce_entries; cepp; cepp = cepp->ce_next) { if (!strcmp(cepp->ce_varname, "bind-ip")) { safestrdup(tempiConf.dns_bindip, cepp->ce_vardata); } } } else if (!strcmp(cep->ce_varname, "anti-flood")) { for (cepp = cep->ce_entries; cepp; cepp = cepp->ce_next) { if (!strcmp(cepp->ce_varname, "unknown-flood-bantime")) tempiConf.unknown_flood_bantime = config_checkval(cepp->ce_vardata,CFG_TIME); else if (!strcmp(cepp->ce_varname, "unknown-flood-amount")) tempiConf.unknown_flood_amount = atol(cepp->ce_vardata); #ifdef NO_FLOOD_AWAY else if (!strcmp(cepp->ce_varname, "away-count")) tempiConf.away_count = atol(cepp->ce_vardata); else if (!strcmp(cepp->ce_varname, "away-period")) tempiConf.away_period = config_checkval(cepp->ce_vardata, CFG_TIME); else if (!strcmp(cepp->ce_varname, "away-flood")) { int cnt, period; config_parse_flood(cepp->ce_vardata, &cnt, &period); tempiConf.away_count = cnt; tempiConf.away_period = period; } #endif else if (!strcmp(cepp->ce_varname, "nick-flood")) { int cnt, period; config_parse_flood(cepp->ce_vardata, &cnt, &period); tempiConf.nick_count = cnt; tempiConf.nick_period = period; } else if (!strcmp(cepp->ce_varname, "connect-flood")) { int cnt, period; config_parse_flood(cepp->ce_vardata, &cnt, &period); tempiConf.throttle_count = cnt; tempiConf.throttle_period = period; } else { for (h = Hooks[HOOKTYPE_CONFIGRUN]; h; h = h->next) { int value = (*(h->func.intfunc))(conf,cepp,CONFIG_SET_ANTI_FLOOD); if (value == 1) break; } } } } else if (!strcmp(cep->ce_varname, "options")) { for (cepp = cep->ce_entries; cepp; cepp = cepp->ce_next) { if (!strcmp(cepp->ce_varname, "hide-ulines")) { tempiConf.hide_ulines = 1; } else if (!strcmp(cepp->ce_varname, "flat-map")) { tempiConf.flat_map = 1; } else if (!strcmp(cepp->ce_varname, "show-opermotd")) { tempiConf.som = 1; } else if (!strcmp(cepp->ce_varname, "identd-check")) { tempiConf.ident_check = 1; } else if (!strcmp(cepp->ce_varname, "fail-oper-warn")) { tempiConf.fail_oper_warn = 1; } else if (!strcmp(cepp->ce_varname, "show-connect-info")) { tempiConf.show_connect_info = 1; } else if (!strcmp(cepp->ce_varname, "dont-resolve")) { tempiConf.dont_resolve = 1; } else if (!strcmp(cepp->ce_varname, "mkpasswd-for-everyone")) { tempiConf.mkpasswd_for_everyone = 1; } else if (!strcmp(cepp->ce_varname, "allow-insane-bans")) { tempiConf.allow_insane_bans = 1; } else if (!strcmp(cepp->ce_varname, "allow-part-if-shunned")) { tempiConf.allow_part_if_shunned = 1; } else if (!strcmp(cepp->ce_varname, "disable-cap")) { tempiConf.disable_cap = 1; } else if (!strcmp(cepp->ce_varname, "disable-ipv6")) { /* other code handles this */ } } } else if (!strcmp(cep->ce_varname, "cloak-keys")) { for (h = Hooks[HOOKTYPE_CONFIGRUN]; h; h = h->next) { int value; value = (*(h->func.intfunc))(conf, cep, CONFIG_CLOAKKEYS); if (value == 1) break; } } else if (!strcmp(cep->ce_varname, "ident")) { for (cepp = cep->ce_entries; cepp; cepp = cepp->ce_next) { if (!strcmp(cepp->ce_varname, "connect-timeout")) tempiConf.ident_connect_timeout = config_checkval(cepp->ce_vardata,CFG_TIME); if (!strcmp(cepp->ce_varname, "read-timeout")) tempiConf.ident_read_timeout = config_checkval(cepp->ce_vardata,CFG_TIME); } } else if (!strcmp(cep->ce_varname, "timesync") || !strcmp(cep->ce_varname, "timesynch")) { for (cepp = cep->ce_entries; cepp; cepp = cepp->ce_next) { if (!strcmp(cepp->ce_varname, "enabled")) tempiConf.timesynch_enabled = config_checkval(cepp->ce_vardata,CFG_YESNO); else if (!strcmp(cepp->ce_varname, "timeout")) tempiConf.timesynch_timeout = config_checkval(cepp->ce_vardata,CFG_TIME); else if (!strcmp(cepp->ce_varname, "server")) safestrdup(tempiConf.timesynch_server, cepp->ce_vardata); } } else if (!strcmp(cep->ce_varname, "spamfilter")) { for (cepp = cep->ce_entries; cepp; cepp = cepp->ce_next) { if (!strcmp(cepp->ce_varname, "ban-time")) tempiConf.spamfilter_ban_time = config_checkval(cepp->ce_vardata,CFG_TIME); else if (!strcmp(cepp->ce_varname, "ban-reason")) safestrdup(tempiConf.spamfilter_ban_reason, cepp->ce_vardata); else if (!strcmp(cepp->ce_varname, "virus-help-channel")) safestrdup(tempiConf.spamfilter_virus_help_channel, cepp->ce_vardata); else if (!strcmp(cepp->ce_varname, "virus-help-channel-deny")) tempiConf.spamfilter_vchan_deny = config_checkval(cepp->ce_vardata,CFG_YESNO); else if (!strcmp(cepp->ce_varname, "except")) { char *name, *p; SpamExcept *e; safestrdup(tempiConf.spamexcept_line, cepp->ce_vardata); for (name = strtoken(&p, cepp->ce_vardata, ","); name; name = strtoken(&p, NULL, ",")) { if (*name == ' ') name++; if (*name) { e = MyMallocEx(sizeof(SpamExcept) + strlen(name)); strcpy(e->name, name); AddListItem(e, tempiConf.spamexcept); } } } else if (!strcmp(cepp->ce_varname, "detect-slow-warn")) { tempiConf.spamfilter_detectslow_warn = atol(cepp->ce_vardata); } else if (!strcmp(cepp->ce_varname, "detect-slow-fatal")) { tempiConf.spamfilter_detectslow_fatal = atol(cepp->ce_vardata); } else if (!strcmp(cepp->ce_varname, "stop-on-first-match")) { tempiConf.spamfilter_stop_on_first_match = config_checkval(cepp->ce_vardata, CFG_YESNO); } } } else if (!strcmp(cep->ce_varname, "default-bantime")) { tempiConf.default_bantime = config_checkval(cep->ce_vardata,CFG_TIME); } else if (!strcmp(cep->ce_varname, "ban-version-tkl-time")) { tempiConf.ban_version_tkl_time = config_checkval(cep->ce_vardata,CFG_TIME); } else if (!strcmp(cep->ce_varname, "modef-default-unsettime")) { int v = atoi(cep->ce_vardata); tempiConf.modef_default_unsettime = (unsigned char)v; } else if (!strcmp(cep->ce_varname, "modef-max-unsettime")) { int v = atoi(cep->ce_vardata); tempiConf.modef_max_unsettime = (unsigned char)v; } else if (!strcmp(cep->ce_varname, "nick-length")) { int v = atoi(cep->ce_vardata); tempiConf.nicklen = v; if (loop.ircd_booted) IsupportSetValue(IsupportFind("NICKLEN"), cep->ce_vardata); } else if (!strcmp(cep->ce_varname, "ssl")) { for (cepp = cep->ce_entries; cepp; cepp = cepp->ce_next) { if (!strcmp(cepp->ce_varname, "egd")) { tempiConf.use_egd = 1; if (cepp->ce_vardata) tempiConf.egd_path = strdup(cepp->ce_vardata); } else if (!strcmp(cepp->ce_varname, "server-cipher-list")) { safestrdup(tempiConf.x_server_cipher_list, cepp->ce_vardata); } else if (!strcmp(cepp->ce_varname, "dh")) { safestrdup(tempiConf.x_dh_pem, cepp->ce_vardata); } else if (!strcmp(cepp->ce_varname, "certificate")) { convert_to_absolute_path(&cepp->ce_vardata, CONFDIR); safestrdup(tempiConf.x_server_cert_pem, cepp->ce_vardata); } else if (!strcmp(cepp->ce_varname, "key")) { convert_to_absolute_path(&cepp->ce_vardata, CONFDIR); safestrdup(tempiConf.x_server_key_pem, cepp->ce_vardata); } else if (!strcmp(cepp->ce_varname, "trusted-ca-file")) { convert_to_absolute_path(&cepp->ce_vardata, CONFDIR); safestrdup(tempiConf.trusted_ca_file, cepp->ce_vardata); } else if (!strcmp(cepp->ce_varname, "renegotiate-bytes")) { tempiConf.ssl_renegotiate_bytes = config_checkval(cepp->ce_vardata, CFG_SIZE); } else if (!strcmp(cepp->ce_varname, "renegotiate-timeout")) { tempiConf.ssl_renegotiate_timeout = config_checkval(cepp->ce_vardata, CFG_TIME); } else if (!strcmp(cepp->ce_varname, "options")) { tempiConf.ssl_options = 0; for (ceppp = cepp->ce_entries; ceppp; ceppp = ceppp->ce_next) { ofl = config_binary_flags_search(_SSLFlags, ceppp->ce_varname, ARRAY_SIZEOF(_SSLFlags)); if (ofl) /* this should always be true */ tempiConf.ssl_options |= ofl->flag; } if (tempiConf.ssl_options & SSLFLAG_DONOTACCEPTSELFSIGNED) if (!(tempiConf.ssl_options & SSLFLAG_VERIFYCERT)) tempiConf.ssl_options |= SSLFLAG_VERIFYCERT; } } } else if (!strcmp(cep->ce_varname, "default-ipv6-clone-mask")) { tempiConf.default_ipv6_clone_mask = atoi(cep->ce_vardata); } else { int value; for (h = Hooks[HOOKTYPE_CONFIGRUN]; h; h = h->next) { value = (*(h->func.intfunc))(conf,cep,CONFIG_SET); if (value == 1) break; } } } return 0; } int _test_set(ConfigFile *conf, ConfigEntry *ce) { ConfigEntry *cep, *cepp, *ceppp; long templong; int tempi; int errors = 0; Hook *h; #define CheckNull(x) if ((!(x)->ce_vardata) || (!(*((x)->ce_vardata)))) { config_error("%s:%i: missing parameter", (x)->ce_fileptr->cf_filename, (x)->ce_varlinenum); errors++; continue; } #define CheckNullAllowEmpty(x) if ((!(x)->ce_vardata)) { config_error("%s:%i: missing parameter", (x)->ce_fileptr->cf_filename, (x)->ce_varlinenum); errors++; continue; } #define CheckDuplicate(cep, name, display) if (settings.has_##name) { config_warn_duplicate((cep)->ce_fileptr->cf_filename, cep->ce_varlinenum, "set::" display); continue; } else settings.has_##name = 1 for (cep = ce->ce_entries; cep; cep = cep->ce_next) { if (!cep->ce_varname) { config_error_blank(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, "set"); errors++; continue; } if (!strcmp(cep->ce_varname, "kline-address")) { CheckNull(cep); CheckDuplicate(cep, kline_address, "kline-address"); if (!strchr(cep->ce_vardata, '@') && !strchr(cep->ce_vardata, ':')) { config_error("%s:%i: set::kline-address must be an e-mail or an URL", cep->ce_fileptr->cf_filename, cep->ce_varlinenum); errors++; continue; } else if (!match("*@unrealircd.com", cep->ce_vardata) || !match("*@unrealircd.org",cep->ce_vardata) || !match("unreal-*@lists.sourceforge.net",cep->ce_vardata)) { config_error("%s:%i: set::kline-address may not be an UnrealIRCd Team address", cep->ce_fileptr->cf_filename, cep->ce_varlinenum); errors++; continue; } } else if (!strcmp(cep->ce_varname, "gline-address")) { CheckNull(cep); CheckDuplicate(cep, gline_address, "gline-address"); if (!strchr(cep->ce_vardata, '@') && !strchr(cep->ce_vardata, ':')) { config_error("%s:%i: set::gline-address must be an e-mail or an URL", cep->ce_fileptr->cf_filename, cep->ce_varlinenum); errors++; continue; } else if (!match("*@unrealircd.com", cep->ce_vardata) || !match("*@unrealircd.org",cep->ce_vardata) || !match("unreal-*@lists.sourceforge.net",cep->ce_vardata)) { config_error("%s:%i: set::gline-address may not be an UnrealIRCd Team address", cep->ce_fileptr->cf_filename, cep->ce_varlinenum); errors++; continue; } } else if (!strcmp(cep->ce_varname, "modes-on-connect")) { char *p; CheckNull(cep); CheckDuplicate(cep, modes_on_connect, "modes-on-connect"); for (p = cep->ce_vardata; *p; p++) if (strchr("oOaANCrzSgHhqtW", *p)) { config_error("%s:%i: set::modes-on-connect may not include mode '%c'", cep->ce_fileptr->cf_filename, cep->ce_varlinenum, *p); errors++; } templong = (long) set_usermode(cep->ce_vardata); } else if (!strcmp(cep->ce_varname, "modes-on-join")) { char *c; struct ChMode temp; bzero(&temp, sizeof(temp)); CheckNull(cep); CheckDuplicate(cep, modes_on_join, "modes-on-join"); for (c = cep->ce_vardata; *c; c++) { if (*c == ' ') break; /* don't check the parameter ;p */ switch (*c) { case 'q': case 'a': case 'o': case 'h': case 'v': case 'b': case 'e': case 'I': case 'O': case 'A': case 'z': case 'l': case 'k': case 'L': config_error("%s:%i: set::modes-on-join contains +%c", cep->ce_fileptr->cf_filename, cep->ce_varlinenum, *c); errors++; break; } } set_channelmodes(cep->ce_vardata, &temp, 1); if (temp.mode & MODE_SECRET && temp.mode & MODE_PRIVATE) { config_error("%s:%i: set::modes-on-join has +s and +p", cep->ce_fileptr->cf_filename, cep->ce_varlinenum); errors++; } } else if (!strcmp(cep->ce_varname, "modes-on-oper")) { char *p; CheckNull(cep); CheckDuplicate(cep, modes_on_oper, "modes-on-oper"); for (p = cep->ce_vardata; *p; p++) if (strchr("oOaANCrzS", *p)) { config_error("%s:%i: set::modes-on-oper may not include mode '%c'", cep->ce_fileptr->cf_filename, cep->ce_varlinenum, *p); errors++; } templong = (long) set_usermode(cep->ce_vardata); } else if (!strcmp(cep->ce_varname, "snomask-on-oper")) { CheckNull(cep); CheckDuplicate(cep, snomask_on_oper, "snomask-on-oper"); } else if (!strcmp(cep->ce_varname, "snomask-on-connect")) { CheckNull(cep); CheckDuplicate(cep, snomask_on_connect, "snomask-on-connect"); } else if (!strcmp(cep->ce_varname, "level-on-join")) { char *p; CheckNull(cep); CheckDuplicate(cep, level_on_join, "level-on-join"); if (!channellevel_to_int(cep->ce_vardata)) { config_error("%s:%i: set::level-on-join: unknown value '%s', should be one of: none, voice, halfop, op, protect, owner", cep->ce_fileptr->cf_filename, cep->ce_varlinenum, cep->ce_vardata); errors++; } } else if (!strcmp(cep->ce_varname, "static-quit")) { CheckNull(cep); CheckDuplicate(cep, static_quit, "static-quit"); } else if (!strcmp(cep->ce_varname, "static-part")) { CheckNull(cep); CheckDuplicate(cep, static_part, "static-part"); } else if (!strcmp(cep->ce_varname, "who-limit")) { CheckNull(cep); CheckDuplicate(cep, who_limit, "who-limit"); } else if (!strcmp(cep->ce_varname, "maxbans")) { CheckNull(cep); CheckDuplicate(cep, maxbans, "maxbans"); } else if (!strcmp(cep->ce_varname, "maxbanlength")) { CheckNull(cep); CheckDuplicate(cep, maxbanlength, "maxbanlength"); } else if (!strcmp(cep->ce_varname, "silence-limit")) { CheckNull(cep); CheckDuplicate(cep, silence_limit, "silence-limit"); } else if (!strcmp(cep->ce_varname, "auto-join")) { CheckNull(cep); CheckDuplicate(cep, auto_join, "auto-join"); } else if (!strcmp(cep->ce_varname, "oper-auto-join")) { CheckNull(cep); CheckDuplicate(cep, oper_auto_join, "oper-auto-join"); } else if (!strcmp(cep->ce_varname, "check-target-nick-bans")) { CheckNull(cep); CheckDuplicate(cep, check_target_nick_bans, "check-target-nick-bans"); } else if (!strcmp(cep->ce_varname, "pingpong-warning")) { config_error("%s:%i: set::pingpong-warning no longer exists (the warning is always off)", cep->ce_fileptr->cf_filename, cep->ce_varlinenum); need_34_upgrade = 1; errors++; } else if (!strcmp(cep->ce_varname, "ping-cookie")) { CheckNull(cep); CheckDuplicate(cep, ping_cookie, "ping-cookie"); } else if (!strcmp(cep->ce_varname, "watch-away-notification")) { CheckNull(cep); CheckDuplicate(cep, watch_away_notification, "watch-away-notification"); } else if (!strcmp(cep->ce_varname, "uhnames")) { CheckNull(cep); CheckDuplicate(cep, uhnames, "uhnames"); } else if (!strcmp(cep->ce_varname, "channel-command-prefix")) { CheckNullAllowEmpty(cep); CheckDuplicate(cep, channel_command_prefix, "channel-command-prefix"); } else if (!strcmp(cep->ce_varname, "allow-userhost-change")) { CheckNull(cep); CheckDuplicate(cep, allow_userhost_change, "allow-userhost-change"); if (stricmp(cep->ce_vardata, "always") && stricmp(cep->ce_vardata, "never") && stricmp(cep->ce_vardata, "not-on-channels") && stricmp(cep->ce_vardata, "force-rejoin")) { config_error("%s:%i: set::allow-userhost-change is invalid", cep->ce_fileptr->cf_filename, cep->ce_varlinenum); errors++; continue; } } else if (!strcmp(cep->ce_varname, "allowed-nickchars")) { if (cep->ce_vardata) { config_error("%s:%i: set::allowed-nickchars: please use 'allowed-nickchars { name; };' " "and not 'allowed-nickchars name;'", cep->ce_fileptr->cf_filename, cep->ce_varlinenum); errors++; continue; } for (cepp = cep->ce_entries; cepp; cepp=cepp->ce_next) { if (!charsys_test_language(cepp->ce_varname)) { config_error("%s:%i: set::allowed-nickchars: Unknown (sub)language '%s'", cep->ce_fileptr->cf_filename, cep->ce_varlinenum, cepp->ce_varname); errors++; } } } else if (!strcmp(cep->ce_varname, "anti-spam-quit-message-time")) { CheckNull(cep); CheckDuplicate(cep, anti_spam_quit_message_time, "anti-spam-quit-message-time"); } else if (!strcmp(cep->ce_varname, "oper-only-stats")) { CheckDuplicate(cep, oper_only_stats, "oper-only-stats"); if (!cep->ce_entries) { CheckNull(cep); } else { for (cepp = cep->ce_entries; cepp; cepp = cepp->ce_next) { if (!cepp->ce_varname) config_error("%s:%i: blank set::oper-only-stats item", cepp->ce_fileptr->cf_filename, cepp->ce_varlinenum); } } } else if (!strcmp(cep->ce_varname, "maxchannelsperuser")) { CheckNull(cep); CheckDuplicate(cep, maxchannelsperuser, "maxchannelsperuser"); tempi = atoi(cep->ce_vardata); if (tempi < 1) { config_error("%s:%i: set::maxchannelsperuser must be > 0", cep->ce_fileptr->cf_filename, cep->ce_varlinenum); errors++; continue; } } else if (!strcmp(cep->ce_varname, "maxdccallow")) { CheckNull(cep); CheckDuplicate(cep, maxdccallow, "maxdccallow"); } else if (!strcmp(cep->ce_varname, "network-name")) { char *p; CheckNull(cep); CheckDuplicate(cep, network_name, "network-name"); for (p = cep->ce_vardata; *p; p++) if ((*p < ' ') || (*p > '~')) { config_error("%s:%i: set::network-name can only contain ASCII characters 33-126. Invalid character = '%c'", cep->ce_fileptr->cf_filename, cep->ce_varlinenum, *p); errors++; break; } } else if (!strcmp(cep->ce_varname, "default-server")) { CheckNull(cep); CheckDuplicate(cep, default_server, "default-server"); } else if (!strcmp(cep->ce_varname, "services-server")) { CheckNull(cep); CheckDuplicate(cep, services_server, "services-server"); } else if (!strcmp(cep->ce_varname, "sasl-server")) { CheckNull(cep); CheckDuplicate(cep, sasl_server, "sasl-server"); } else if (!strcmp(cep->ce_varname, "stats-server")) { CheckNull(cep); CheckDuplicate(cep, stats_server, "stats-server"); } else if (!strcmp(cep->ce_varname, "help-channel")) { CheckNull(cep); CheckDuplicate(cep, help_channel, "help-channel"); } else if (!strcmp(cep->ce_varname, "hiddenhost-prefix")) { CheckNull(cep); CheckDuplicate(cep, hiddenhost_prefix, "hiddenhost-prefix"); if (strchr(cep->ce_vardata, ' ') || (*cep->ce_vardata == ':')) { config_error("%s:%i: set::hiddenhost-prefix must not contain spaces or be prefixed with ':'", cep->ce_fileptr->cf_filename, cep->ce_varlinenum); errors++; continue; } } else if (!strcmp(cep->ce_varname, "prefix-quit")) { CheckNull(cep); CheckDuplicate(cep, prefix_quit, "prefix-quit"); } else if (!strcmp(cep->ce_varname, "restrict-usermodes")) { CheckNull(cep); CheckDuplicate(cep, restrict_usermodes, "restrict-usermodes"); if (cep->ce_varname) { int warn = 0; char *p; for (p = cep->ce_vardata; *p; p++) if ((*p == '+') || (*p == '-')) warn = 1; if (warn) { config_status("%s:%i: warning: set::restrict-usermodes: should only contain modechars, no + or -.\n", cep->ce_fileptr->cf_filename, cep->ce_varlinenum); } } } else if (!strcmp(cep->ce_varname, "restrict-channelmodes")) { CheckNull(cep); CheckDuplicate(cep, restrict_channelmodes, "restrict-channelmodes"); if (cep->ce_varname) { int warn = 0; char *p; for (p = cep->ce_vardata; *p; p++) if ((*p == '+') || (*p == '-')) warn = 1; if (warn) { config_status("%s:%i: warning: set::restrict-channelmodes: should only contain modechars, no + or -.\n", cep->ce_fileptr->cf_filename, cep->ce_varlinenum); } } } else if (!strcmp(cep->ce_varname, "restrict-extendedbans")) { CheckDuplicate(cep, restrict_extendedbans, "restrict-extendedbans"); CheckNull(cep); } else if (!strcmp(cep->ce_varname, "new-linking-protocol")) { CheckDuplicate(cep, new_linking_protocol, "new-linking-protocol"); CheckNull(cep); } else if (!strcmp(cep->ce_varname, "link")) { for (cepp = cep->ce_entries; cepp; cepp = cepp->ce_next) { CheckNull(cepp); if (!strcmp(cepp->ce_varname, "bind-ip")) { CheckDuplicate(cepp, link_bind_ip, "link::bind-ip"); if (strcmp(cepp->ce_vardata, "*")) { if (!is_valid_ip(cepp->ce_vardata)) { config_error("%s:%i: set::link::bind-ip (%s) is not a valid IP", cepp->ce_fileptr->cf_filename, cepp->ce_varlinenum, cepp->ce_vardata); errors++; continue; } } } } } else if (!strcmp(cep->ce_varname, "dns")) { for (cepp = cep->ce_entries; cepp; cepp = cepp->ce_next) { CheckNull(cepp); if (!strcmp(cepp->ce_varname, "nameserver") || !strcmp(cepp->ce_varname, "timeout") || !strcmp(cepp->ce_varname, "retries")) { config_error("%s:%i: set::dns::%s no longer exist in UnrealIRCd 3.4.x. " "Please remove it from your configuration file.", cepp->ce_fileptr->cf_filename, cepp->ce_varlinenum, cepp->ce_varname); errors++; } else if (!strcmp(cepp->ce_varname, "bind-ip")) { CheckDuplicate(cepp, dns_bind_ip, "dns::bind-ip"); if (strcmp(cepp->ce_vardata, "*")) { if (!is_valid_ip(cepp->ce_vardata)) { config_error("%s:%i: set::dns::bind-ip (%s) is not a valid IP", cepp->ce_fileptr->cf_filename, cepp->ce_varlinenum, cepp->ce_vardata); errors++; continue; } } } else { config_error_unknownopt(cepp->ce_fileptr->cf_filename, cepp->ce_varlinenum, "set::dns", cepp->ce_varname); errors++; } } } else if (!strcmp(cep->ce_varname, "throttle")) { config_error("%s:%i: set::throttle has been renamed. you now use " "set::anti-flood::connect-flood :. " "Or just remove the throttle block and you get the default " "of 3 per 60 seconds.", cep->ce_fileptr->cf_filename, cep->ce_varlinenum); errors++; need_34_upgrade = 1; continue; } else if (!strcmp(cep->ce_varname, "anti-flood")) { for (cepp = cep->ce_entries; cepp; cepp = cepp->ce_next) { CheckNull(cepp); if (!strcmp(cepp->ce_varname, "unknown-flood-bantime")) { CheckDuplicate(cepp, anti_flood_unknown_flood_bantime, "anti-flood::unknown-flood-bantime"); } else if (!strcmp(cepp->ce_varname, "unknown-flood-amount")) { CheckDuplicate(cepp, anti_flood_unknown_flood_amount, "anti-flood::unknown-flood-amount"); } #ifdef NO_FLOOD_AWAY else if (!strcmp(cepp->ce_varname, "away-count")) { int temp = atol(cepp->ce_vardata); CheckDuplicate(cepp, anti_flood_away_count, "anti-flood::away-count"); if (temp < 1 || temp > 255) { config_error("%s:%i: set::anti-flood::away-count must be between 1 and 255", cepp->ce_fileptr->cf_filename, cepp->ce_varlinenum); errors++; } } else if (!strcmp(cepp->ce_varname, "away-period")) { int temp = config_checkval(cepp->ce_vardata, CFG_TIME); CheckDuplicate(cepp, anti_flood_away_period, "anti-flood::away-period"); if (temp < 10) { config_error("%s:%i: set::anti-flood::away-period must be greater than 9", cepp->ce_fileptr->cf_filename, cepp->ce_varlinenum); errors++; } } else if (!strcmp(cepp->ce_varname, "away-flood")) { int cnt, period; if (settings.has_anti_flood_away_period) { config_warn("%s:%d: set::anti-flood::away-flood overrides set::anti-flood::away-period", cepp->ce_fileptr->cf_filename, cepp->ce_varlinenum); continue; } if (settings.has_anti_flood_away_count) { config_warn("%s:%d: set::anti-flood::away-flood overrides set::anti-flood::away-count", cepp->ce_fileptr->cf_filename, cepp->ce_varlinenum); continue; } settings.has_anti_flood_away_period = 1; settings.has_anti_flood_away_count = 1; if (!config_parse_flood(cepp->ce_vardata, &cnt, &period) || (cnt < 1) || (cnt > 255) || (period < 10)) { config_error("%s:%i: set::anti-flood::away-flood error. Syntax is ':' (eg 5:60), " "count should be 1-255, period should be greater than 9", cepp->ce_fileptr->cf_filename, cepp->ce_varlinenum); errors++; } } #endif else if (!strcmp(cepp->ce_varname, "nick-flood")) { int cnt, period; CheckDuplicate(cepp, anti_flood_nick_flood, "anti-flood::nick-flood"); if (!config_parse_flood(cepp->ce_vardata, &cnt, &period) || (cnt < 1) || (cnt > 255) || (period < 5)) { config_error("%s:%i: set::anti-flood::away-flood error. Syntax is ':' (eg 5:60), " "count should be 1-255, period should be greater than 4", cepp->ce_fileptr->cf_filename, cepp->ce_varlinenum); errors++; } } else if (!strcmp(cepp->ce_varname, "connect-flood")) { int cnt, period; CheckDuplicate(cepp, anti_flood_connect_flood, "anti-flood::connect-flood"); if (!config_parse_flood(cepp->ce_vardata, &cnt, &period) || (cnt < 1) || (cnt > 255) || (period < 1) || (period > 3600)) { config_error("%s:%i: set::anti-flood::connect-flood: Syntax is ':' (eg 5:60), " "count should be 1-255, period should be 1-3600", cepp->ce_fileptr->cf_filename, cepp->ce_varlinenum); errors++; } } else { /* hmm.. I don't like this method. but I just quickly copied it from CONFIG_ALLOW for now... */ int used = 0; Hook *h; for (h = Hooks[HOOKTYPE_CONFIGTEST]; h; h = h->next) { int value, errs = 0; if (h->owner && !(h->owner->flags & MODFLAG_TESTING) && !(h->owner->options & MOD_OPT_PERM)) continue; value = (*(h->func.intfunc))(conf,cepp,CONFIG_SET_ANTI_FLOOD,&errs); if (value == 2) used = 1; if (value == 1) { used = 1; break; } if (value == -1) { used = 1; errors += errs; break; } if (value == -2) { used = 1; errors += errs; } } if (!used) { config_error_unknownopt(cepp->ce_fileptr->cf_filename, cepp->ce_varlinenum, "set::anti-flood", cepp->ce_varname); errors++; } continue; } } } else if (!strcmp(cep->ce_varname, "options")) { for (cepp = cep->ce_entries; cepp; cepp = cepp->ce_next) { if (!strcmp(cepp->ce_varname, "hide-ulines")) { CheckDuplicate(cepp, options_hide_ulines, "options::hide-ulines"); } else if (!strcmp(cepp->ce_varname, "flat-map")) { CheckDuplicate(cepp, options_flat_map, "options::flat-map"); } else if (!strcmp(cepp->ce_varname, "show-opermotd")) { CheckDuplicate(cepp, options_show_opermotd, "options::show-opermotd"); } else if (!strcmp(cepp->ce_varname, "identd-check")) { CheckDuplicate(cepp, options_identd_check, "options::identd-check"); } else if (!strcmp(cepp->ce_varname, "fail-oper-warn")) { CheckDuplicate(cepp, options_fail_oper_warn, "options::fail-oper-warn"); } else if (!strcmp(cepp->ce_varname, "show-connect-info")) { CheckDuplicate(cepp, options_show_connect_info, "options::show-connect-info"); } else if (!strcmp(cepp->ce_varname, "dont-resolve")) { CheckDuplicate(cepp, options_dont_resolve, "options::dont-resolve"); } else if (!strcmp(cepp->ce_varname, "mkpasswd-for-everyone")) { CheckDuplicate(cepp, options_mkpasswd_for_everyone, "options::mkpasswd-for-everyone"); } else if (!strcmp(cepp->ce_varname, "allow-insane-bans")) { CheckDuplicate(cepp, options_allow_insane_bans, "options::allow-insane-bans"); } else if (!strcmp(cepp->ce_varname, "allow-part-if-shunned")) { CheckDuplicate(cepp, options_allow_part_if_shunned, "options::allow-part-if-shunned"); } else if (!strcmp(cepp->ce_varname, "disable-cap")) { CheckDuplicate(cepp, options_disable_cap, "options::disable-cap"); } else if (!strcmp(cepp->ce_varname, "disable-ipv6")) { CheckDuplicate(cepp, options_disable_ipv6, "options::disable-ipv6"); DISABLE_IPV6 = 1; /* ugly ugly. needs to be done here because at conf runtime is too late. */ } else { config_error_unknownopt(cepp->ce_fileptr->cf_filename, cepp->ce_varlinenum, "set::options", cepp->ce_varname); errors++; continue; } } } else if (!strcmp(cep->ce_varname, "hosts")) { config_error("%s:%i: set::hosts has been removed in UnrealIRCd 3.4.x. You can use oper::vhost now.", cep->ce_fileptr->cf_filename, cep->ce_varlinenum); errors++; need_34_upgrade = 1; } else if (!strcmp(cep->ce_varname, "cloak-keys")) { CheckDuplicate(cep, cloak_keys, "cloak-keys"); for (h = Hooks[HOOKTYPE_CONFIGTEST]; h; h = h->next) { int value, errs = 0; if (h->owner && !(h->owner->flags & MODFLAG_TESTING) && !(h->owner->options & MOD_OPT_PERM)) continue; value = (*(h->func.intfunc))(conf, cep, CONFIG_CLOAKKEYS, &errs); if (value == 1) break; if (value == -1) { errors += errs; break; } if (value == -2) errors += errs; } } else if (!strcmp(cep->ce_varname, "scan")) { config_status("%s:%i: set::scan: WARNING: scanner support has been removed, " "use BOPM instead: http://www.blitzed.org/bopm/ (*NIX) / http://vulnscan.org/winbopm/ (Windows)", cep->ce_fileptr->cf_filename, cep->ce_varlinenum); } else if (!strcmp(cep->ce_varname, "ident")) { for (cepp = cep->ce_entries; cepp; cepp = cepp->ce_next) { int is_ok = 0; CheckNull(cepp); if (!strcmp(cepp->ce_varname, "connect-timeout")) { is_ok = 1; CheckDuplicate(cepp, ident_connect_timeout, "ident::connect-timeout"); } else if (!strcmp(cepp->ce_varname, "read-timeout")) { is_ok = 1; CheckDuplicate(cepp, ident_read_timeout, "ident::read-timeout"); } if (is_ok) { int v = config_checkval(cepp->ce_vardata,CFG_TIME); if ((v > 60) || (v < 1)) { config_error("%s:%i: set::ident::%s value out of range (%d), should be between 1 and 60.", cepp->ce_fileptr->cf_filename, cepp->ce_varlinenum, cepp->ce_varname, v); errors++; continue; } } else { config_error_unknown(cepp->ce_fileptr->cf_filename, cepp->ce_varlinenum, "set::ident", cepp->ce_varname); errors++; continue; } } } else if (!strcmp(cep->ce_varname, "timesync") || !strcmp(cep->ce_varname, "timesynch")) { for (cepp = cep->ce_entries; cepp; cepp = cepp->ce_next) { CheckNull(cepp); if (!strcmp(cepp->ce_varname, "enabled")) { } else if (!strcmp(cepp->ce_varname, "timeout")) { int v = config_checkval(cepp->ce_vardata,CFG_TIME); if ((v > 5) || (v < 1)) { config_error("%s:%i: set::timesync::%s value out of range (%d), should be between 1 and 5 (higher=unreliable).", cepp->ce_fileptr->cf_filename, cepp->ce_varlinenum, cepp->ce_varname, v); errors++; continue; } } else if (!strcmp(cepp->ce_varname, "server")) { } else { config_error_unknown(cepp->ce_fileptr->cf_filename, cepp->ce_varlinenum, "set::timesync", cepp->ce_varname); errors++; continue; } } } else if (!strcmp(cep->ce_varname, "spamfilter")) { for (cepp = cep->ce_entries; cepp; cepp = cepp->ce_next) { CheckNull(cepp); if (!strcmp(cepp->ce_varname, "ban-time")) { long x; CheckDuplicate(cepp, spamfilter_ban_time, "spamfilter::ban-time"); x = config_checkval(cepp->ce_vardata,CFG_TIME); if ((x < 0) > (x > 2000000000)) { config_error("%s:%i: set::spamfilter:ban-time: value '%ld' out of range", cep->ce_fileptr->cf_filename, cep->ce_varlinenum, x); errors++; continue; } } else if (!strcmp(cepp->ce_varname, "ban-reason")) { CheckDuplicate(cepp, spamfilter_ban_reason, "spamfilter::ban-reason"); } else if (!strcmp(cepp->ce_varname, "virus-help-channel")) { CheckDuplicate(cepp, spamfilter_virus_help_channel, "spamfilter::virus-help-channel"); if ((cepp->ce_vardata[0] != '#') || (strlen(cepp->ce_vardata) > CHANNELLEN)) { config_error("%s:%i: set::spamfilter:virus-help-channel: " "specified channelname is too long or contains invalid characters (%s)", cep->ce_fileptr->cf_filename, cep->ce_varlinenum, cepp->ce_vardata); errors++; continue; } } else if (!strcmp(cepp->ce_varname, "virus-help-channel-deny")) { CheckDuplicate(cepp, spamfilter_virus_help_channel_deny, "spamfilter::virus-help-channel-deny"); } else if (!strcmp(cepp->ce_varname, "except")) { CheckDuplicate(cepp, spamfilter_except, "spamfilter::except"); } else #ifdef SPAMFILTER_DETECTSLOW if (!strcmp(cepp->ce_varname, "detect-slow-warn")) { } else if (!strcmp(cepp->ce_varname, "detect-slow-fatal")) { } else #endif if (!strcmp(cepp->ce_varname, "stop-on-first-match")) { } else { config_error_unknown(cepp->ce_fileptr->cf_filename, cepp->ce_varlinenum, "set::spamfilter", cepp->ce_varname); errors++; continue; } } } /* TODO: FIX THIS */ else if (!strcmp(cep->ce_varname, "default-bantime")) { long x; CheckDuplicate(cep, default_bantime, "default-bantime"); CheckNull(cep); x = config_checkval(cep->ce_vardata,CFG_TIME); if ((x < 0) > (x > 2000000000)) { config_error("%s:%i: set::default-bantime: value '%ld' out of range", cep->ce_fileptr->cf_filename, cep->ce_varlinenum, x); errors++; } } else if (!strcmp(cep->ce_varname, "ban-version-tkl-time")) { long x; CheckDuplicate(cep, ban_version_tkl_time, "ban-version-tkl-time"); CheckNull(cep); x = config_checkval(cep->ce_vardata,CFG_TIME); if ((x < 0) > (x > 2000000000)) { config_error("%s:%i: set::ban-version-tkl-time: value '%ld' out of range", cep->ce_fileptr->cf_filename, cep->ce_varlinenum, x); errors++; } } else if (!strcmp(cep->ce_varname, "modef-default-unsettime")) { int v; CheckDuplicate(cep, modef_default_unsettime, "modef-default-unsettime"); CheckNull(cep); v = atoi(cep->ce_vardata); if ((v <= 0) || (v > 255)) { config_error("%s:%i: set::modef-default-unsettime: value '%d' out of range (should be 1-255)", cep->ce_fileptr->cf_filename, cep->ce_varlinenum, v); errors++; } } else if (!strcmp(cep->ce_varname, "modef-max-unsettime")) { int v; CheckDuplicate(cep, modef_max_unsettime, "modef-max-unsettime"); CheckNull(cep); v = atoi(cep->ce_vardata); if ((v <= 0) || (v > 255)) { config_error("%s:%i: set::modef-max-unsettime: value '%d' out of range (should be 1-255)", cep->ce_fileptr->cf_filename, cep->ce_varlinenum, v); errors++; } } else if (!strcmp(cep->ce_varname, "nick-length")) { int v; CheckDuplicate(cep, nicklen, "nick-length"); CheckNull(cep); v = atoi(cep->ce_vardata); if ((v <= 0) || (v > NICKLEN)) { config_error("%s:%i: set::nick-length: value '%d' out of range (should be 1-%d)", cep->ce_fileptr->cf_filename, cep->ce_varlinenum, v, NICKLEN); errors++; } } else if (!strcmp(cep->ce_varname, "ssl")) { for (cepp = cep->ce_entries; cepp; cepp = cepp->ce_next) { if (!strcmp(cepp->ce_varname, "egd")) { CheckDuplicate(cep, ssl_egd, "ssl::egd"); } else if (!strcmp(cepp->ce_varname, "renegotiate-timeout")) { CheckDuplicate(cep, renegotiate_timeout, "ssl::renegotiate-timeout"); } else if (!strcmp(cepp->ce_varname, "renegotiate-bytes")) { CheckDuplicate(cep, renegotiate_bytes, "ssl::renegotiate-bytes"); } else if (!strcmp(cepp->ce_varname, "server-cipher-list")) { CheckNull(cepp); CheckDuplicate(cep, ssl_server_cipher_list, "ssl::server-cipher-list"); } else if (!strcmp(cepp->ce_varname, "certificate")) { CheckNull(cepp); CheckDuplicate(cep, ssl_certificate, "ssl::certificate"); } else if (!strcmp(cepp->ce_varname, "dh")) { CheckNull(cepp); CheckDuplicate(cep, ssl_dh, "ssl::dh"); } else if (!strcmp(cepp->ce_varname, "key")) { CheckNull(cepp); CheckDuplicate(cep, ssl_key, "ssl::key"); } else if (!strcmp(cepp->ce_varname, "trusted-ca-file")) { CheckNull(cepp); CheckDuplicate(cep, ssl_trusted_ca_file, "ssl::trusted-ca-file"); } else if (!strcmp(cepp->ce_varname, "options")) { CheckDuplicate(cep, ssl_options, "ssl::options"); for (ceppp = cepp->ce_entries; ceppp; ceppp = ceppp->ce_next) if (!config_binary_flags_search(_SSLFlags, ceppp->ce_varname, ARRAY_SIZEOF(_SSLFlags))) { config_error("%s:%i: unknown SSL flag '%s'", ceppp->ce_fileptr->cf_filename, ceppp->ce_varlinenum, ceppp->ce_varname); errors ++; } } else { config_error("%s:%i: unknown directive set::ssl::%s", cepp->ce_fileptr->cf_filename, cepp->ce_varlinenum, cepp->ce_varname); errors++; } } } else if (!strcmp(cep->ce_varname, "default-ipv6-clone-mask")) { /* keep this in sync with _test_allow() */ int ipv6mask; ipv6mask = atoi(cep->ce_vardata); if (ipv6mask == 0) { config_error("%s:%d: set::default-ipv6-clone-mask given a value of zero. This cannnot be correct, as it would treat all IPv6 hosts as one host.", cep->ce_fileptr->cf_filename, cep->ce_varlinenum); errors++; } if (ipv6mask > 128) { config_error("%s:%d: set::default-ipv6-clone-mask was set to %d. The maximum value is 128.", cep->ce_fileptr->cf_filename, cep->ce_varlinenum, ipv6mask); errors++; } if (ipv6mask <= 32) { config_warn("%s:%d: set::default-ipv6-clone-mask was given a very small value.", cep->ce_fileptr->cf_filename, cep->ce_varlinenum); } } else { int used = 0; for (h = Hooks[HOOKTYPE_CONFIGTEST]; h; h = h->next) { int value, errs = 0; if (h->owner && !(h->owner->flags & MODFLAG_TESTING) && !(h->owner->options & MOD_OPT_PERM)) continue; value = (*(h->func.intfunc))(conf,cep,CONFIG_SET, &errs); if (value == 2) used = 1; if (value == 1) { used = 1; break; } if (value == -1) { used = 1; errors += errs; break; } if (value == -2) { used = 1; errors += errs; } } if (!used) { config_error("%s:%i: unknown directive set::%s", cep->ce_fileptr->cf_filename, cep->ce_varlinenum, cep->ce_varname); errors++; } } } return errors; } int _conf_loadmodule(ConfigFile *conf, ConfigEntry *ce) { char *ret; if (!ce->ce_vardata) { config_status("%s:%i: loadmodule without filename", ce->ce_fileptr->cf_filename, ce->ce_varlinenum); return -1; } if (strstr(ce->ce_vardata, "commands.so") || strstr(ce->ce_vardata, "commands.dll")) { config_error("%s:%i: You are trying to load the 'commands' module, this is no longer supported. " "Fix this by editing your configuration file: remove the loadmodule line for commands and add the following line instead: " "include \"modules.default.conf\";", ce->ce_fileptr->cf_filename, ce->ce_varlinenum); need_34_upgrade = 1; return -1; } if (strstr(ce->ce_vardata, "modules/cloak") && !strcmp(conf->cf_filename, "modules.conf")) { config_error("You seem to have an include for 'modules.conf'."); config_error("If you have this because you are upgrading from 3.4-alpha3 to a"); config_error("later 3.4.x version then please change the include \"modules.conf\";"); config_error("into an include \"modules.default.conf\"; (probably in your"); config_error("conf/unrealircd.conf). Yeah, we changed the file name."); // TODO ^: silly win32 wrapping prevents this from being displayed otherwise. PLZ FIX! ! /* let it continue to load anyway? */ } if ((ret = Module_Create(ce->ce_vardata))) { config_status("%s:%i: loadmodule %s: failed to load: %s", ce->ce_fileptr->cf_filename, ce->ce_varlinenum, ce->ce_vardata, ret); return -1; } return 1; } int _test_loadmodule(ConfigFile *conf, ConfigEntry *ce) { return 0; } /* * Actually use configuration */ void run_configuration(void) { ConfigItem_listen *listenptr; int failed = 0, ports_bound = 0; char boundmsg[512]; *boundmsg = '\0'; for (listenptr = conf_listen; listenptr; listenptr = (ConfigItem_listen *) listenptr->next) { if (!(listenptr->options & LISTENER_BOUND)) { if (add_listener2(listenptr) == -1) { ircd_log(LOG_ERROR, "Failed to bind to %s:%i", listenptr->ip, listenptr->port); failed = 1; } else { snprintf(boundmsg+strlen(boundmsg), sizeof(boundmsg)-strlen(boundmsg), "%s:%d, ", listenptr->ip, listenptr->port); } } /* NOTE: do not merge this with code above (nor in an else block), * as add_listener2() affects this flag. */ if (listenptr->options & LISTENER_BOUND) ports_bound++; } if (ports_bound == 0) { ircd_log(LOG_ERROR, "IRCd could not listen on any ports. If you see 'Address already in use' errors " "above then most likely the IRCd is already running (or something else is using the " "specified ports). If you are sure the IRCd is not running then verify your " "listen blocks, maybe you have to bind to a specific IP rather than \"*\"."); exit(-1); } if (failed && !loop.ircd_booted) { ircd_log(LOG_ERROR, "Could not listen on all specified addresses/ports. See errors above. " "Please fix your listen { } blocks and/or make sure no other programs " "are listening on the same port."); exit(-1); } if (strlen(boundmsg) > 2) boundmsg[strlen(boundmsg)-2] = '\0'; ircd_log(LOG_ERROR, "UnrealIRCd is now listening on the following addresses/ports: %s", boundmsg); } int _conf_offchans(ConfigFile *conf, ConfigEntry *ce) { ConfigEntry *cep, *cepp; for (cep = ce->ce_entries; cep; cep = cep->ce_next) { ConfigItem_offchans *of = MyMallocEx(sizeof(ConfigItem_offchans)); strlcpy(of->chname, cep->ce_varname, CHANNELLEN+1); for (cepp = cep->ce_entries; cepp; cepp = cepp->ce_next) { if (!strcmp(cepp->ce_varname, "topic")) of->topic = strdup(cepp->ce_vardata); } AddListItem(of, conf_offchans); } return 0; } int _test_offchans(ConfigFile *conf, ConfigEntry *ce) { int errors = 0; ConfigEntry *cep, *cep2; char checkchan[CHANNELLEN + 1]; if (!ce->ce_entries) { config_error("%s:%i: empty official-channels block", ce->ce_fileptr->cf_filename, ce->ce_varlinenum); return 1; } for (cep = ce->ce_entries; cep; cep = cep->ce_next) { if (strlen(cep->ce_varname) > CHANNELLEN) { config_error("%s:%i: official-channels: '%s' name too long (max %d characters).", cep->ce_fileptr->cf_filename, cep->ce_varlinenum, cep->ce_varname, CHANNELLEN); errors++; continue; } strcpy(checkchan, cep->ce_varname); /* safe */ clean_channelname(checkchan); if (strcmp(checkchan, cep->ce_varname) || (*cep->ce_varname != '#')) { config_error("%s:%i: official-channels: '%s' is not a valid channel name.", cep->ce_fileptr->cf_filename, cep->ce_varlinenum, cep->ce_varname); errors++; continue; } for (cep2 = cep->ce_entries; cep2; cep2 = cep2->ce_next) { if (!cep2->ce_vardata) { config_error_empty(cep2->ce_fileptr->cf_filename, cep2->ce_varlinenum, "official-channels", cep2->ce_varname); errors++; continue; } if (!strcmp(cep2->ce_varname, "topic")) { if (strlen(cep2->ce_vardata) > TOPICLEN) { config_error("%s:%i: official-channels::%s: topic too long (max %d characters).", cep2->ce_fileptr->cf_filename, cep2->ce_varlinenum, cep->ce_varname, TOPICLEN); errors++; continue; } } else { config_error_unknown(cep2->ce_fileptr->cf_filename, cep2->ce_varlinenum, "official-channels", cep2->ce_varname); errors++; continue; } } } return errors; } int _conf_alias(ConfigFile *conf, ConfigEntry *ce) { ConfigItem_alias *alias = NULL; ConfigItem_alias_format *format; ConfigEntry *cep, *cepp; aCommand *cmptr; if ((cmptr = find_Command(ce->ce_vardata, 0, M_ALIAS))) del_Command(ce->ce_vardata, cmptr->func); if (find_Command_simple(ce->ce_vardata)) { config_warn("%s:%i: Alias '%s' would conflict with command (or server token) '%s', alias not added.", ce->ce_fileptr->cf_filename, ce->ce_varlinenum, ce->ce_vardata, ce->ce_vardata); return 0; } if ((alias = Find_alias(ce->ce_vardata))) DelListItem(alias, conf_alias); alias = MyMallocEx(sizeof(ConfigItem_alias)); safestrdup(alias->alias, ce->ce_vardata); for (cep = ce->ce_entries; cep; cep = cep->ce_next) { if (!strcmp(cep->ce_varname, "format")) { format = MyMallocEx(sizeof(ConfigItem_alias_format)); safestrdup(format->format, cep->ce_vardata); format->expr = unreal_create_match(MATCH_PCRE_REGEX, cep->ce_vardata, NULL); if (!format->expr) abort(); /* Impossible due to _test_alias earlier */ for (cepp = cep->ce_entries; cepp; cepp = cepp->ce_next) { if (!strcmp(cepp->ce_varname, "nick") || !strcmp(cepp->ce_varname, "target") || !strcmp(cepp->ce_varname, "command")) { safestrdup(format->nick, cepp->ce_vardata); } else if (!strcmp(cepp->ce_varname, "parameters")) { safestrdup(format->parameters, cepp->ce_vardata); } else if (!strcmp(cepp->ce_varname, "type")) { if (!strcmp(cepp->ce_vardata, "services")) format->type = ALIAS_SERVICES; else if (!strcmp(cepp->ce_vardata, "stats")) format->type = ALIAS_STATS; else if (!strcmp(cepp->ce_vardata, "normal")) format->type = ALIAS_NORMAL; else if (!strcmp(cepp->ce_vardata, "channel")) format->type = ALIAS_CHANNEL; else if (!strcmp(cepp->ce_vardata, "real")) format->type = ALIAS_REAL; } } AddListItem(format, alias->format); } else if (!strcmp(cep->ce_varname, "nick") || !strcmp(cep->ce_varname, "target")) { safestrdup(alias->nick, cep->ce_vardata); } else if (!strcmp(cep->ce_varname, "type")) { if (!strcmp(cep->ce_vardata, "services")) alias->type = ALIAS_SERVICES; else if (!strcmp(cep->ce_vardata, "stats")) alias->type = ALIAS_STATS; else if (!strcmp(cep->ce_vardata, "normal")) alias->type = ALIAS_NORMAL; else if (!strcmp(cep->ce_vardata, "channel")) alias->type = ALIAS_CHANNEL; else if (!strcmp(cep->ce_vardata, "command")) alias->type = ALIAS_COMMAND; } else if (!strcmp(cep->ce_varname, "spamfilter")) alias->spamfilter = config_checkval(cep->ce_vardata, CFG_YESNO); } if (BadPtr(alias->nick) && alias->type != ALIAS_COMMAND) { safestrdup(alias->nick, alias->alias); } CommandAdd(NULL, alias->alias, m_alias, 1, M_USER|M_ALIAS); AddListItem(alias, conf_alias); return 0; } int _test_alias(ConfigFile *conf, ConfigEntry *ce) { int errors = 0; ConfigEntry *cep, *cepp; char has_type = 0, has_target = 0, has_format = 0; char type = 0; if (!ce->ce_entries) { config_error("%s:%i: empty alias block", ce->ce_fileptr->cf_filename, ce->ce_varlinenum); return 1; } if (!ce->ce_vardata) { config_error("%s:%i: alias without name", ce->ce_fileptr->cf_filename, ce->ce_varlinenum); errors++; } else if (!find_Command(ce->ce_vardata, 0, M_ALIAS) && find_Command(ce->ce_vardata, 0, 0)) { config_status("%s:%i: %s is an existing command, can not add alias", ce->ce_fileptr->cf_filename, ce->ce_varlinenum, ce->ce_vardata); errors++; } for (cep = ce->ce_entries; cep; cep = cep->ce_next) { if (config_is_blankorempty(cep, "alias")) { errors++; continue; } if (!strcmp(cep->ce_varname, "format")) { char *err = NULL; aMatch *expr; char has_type = 0, has_target = 0, has_parameters = 0; has_format = 1; expr = unreal_create_match(MATCH_PCRE_REGEX, cep->ce_vardata, &err); if (!expr) { config_error("%s:%i: alias::format contains an invalid regex: %s", cep->ce_fileptr->cf_filename, cep->ce_varlinenum, err); config_error("Upgrading from 3.2.x to 3.4.x? Note that regex changed from POSIX Regex " "to PCRE Regex!"); /* TODO: refer to some url ? */ } else { unreal_delete_match(expr); } for (cepp = cep->ce_entries; cepp; cepp = cepp->ce_next) { if (config_is_blankorempty(cepp, "alias::format")) { errors++; continue; } if (!strcmp(cepp->ce_varname, "nick") || !strcmp(cepp->ce_varname, "command") || !strcmp(cepp->ce_varname, "target")) { if (has_target) { config_warn_duplicate(cepp->ce_fileptr->cf_filename, cepp->ce_varlinenum, "alias::format::target"); continue; } has_target = 1; } else if (!strcmp(cepp->ce_varname, "type")) { if (has_type) { config_warn_duplicate(cepp->ce_fileptr->cf_filename, cepp->ce_varlinenum, "alias::format::type"); continue; } has_type = 1; if (!strcmp(cepp->ce_vardata, "services")) ; else if (!strcmp(cepp->ce_vardata, "stats")) ; else if (!strcmp(cepp->ce_vardata, "normal")) ; else if (!strcmp(cepp->ce_vardata, "channel")) ; else if (!strcmp(cepp->ce_vardata, "real")) ; else { config_error("%s:%i: unknown alias type", cepp->ce_fileptr->cf_filename, cepp->ce_varlinenum); errors++; } } else if (!strcmp(cepp->ce_varname, "parameters")) { if (has_parameters) { config_warn_duplicate(cepp->ce_fileptr->cf_filename, cepp->ce_varlinenum, "alias::format::parameters"); continue; } has_parameters = 1; } else { config_error_unknown(cepp->ce_fileptr->cf_filename, cepp->ce_varlinenum, "alias::format", cepp->ce_varname); errors++; } } if (!has_target) { config_error_missing(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, "alias::format::target"); errors++; } if (!has_type) { config_error_missing(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, "alias::format::type"); errors++; } if (!has_parameters) { config_error_missing(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, "alias::format::parameters"); errors++; } } else if (!strcmp(cep->ce_varname, "nick") || !strcmp(cep->ce_varname, "target")) { if (has_target) { config_warn_duplicate(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, "alias::target"); continue; } has_target = 1; } else if (!strcmp(cep->ce_varname, "type")) { if (has_type) { config_warn_duplicate(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, "alias::type"); continue; } has_type = 1; if (!strcmp(cep->ce_vardata, "services")) ; else if (!strcmp(cep->ce_vardata, "stats")) ; else if (!strcmp(cep->ce_vardata, "normal")) ; else if (!strcmp(cep->ce_vardata, "channel")) ; else if (!strcmp(cep->ce_vardata, "command")) type = 'c'; else { config_error("%s:%i: unknown alias type", cep->ce_fileptr->cf_filename, cep->ce_varlinenum); errors++; } } else if (!strcmp(cep->ce_varname, "spamfilter")) ; else { config_error_unknown(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, "alias", cep->ce_varname); errors++; } } if (!has_type) { config_error_missing(ce->ce_fileptr->cf_filename, ce->ce_varlinenum, "alias::type"); errors++; } if (!has_format && type == 'c') { config_error("%s:%d: alias::type is 'command' but no alias::format was specified", ce->ce_fileptr->cf_filename, ce->ce_varlinenum); errors++; } else if (has_format && type != 'c') { config_error("%s:%d: alias::format specified when type is not 'command'", ce->ce_fileptr->cf_filename, ce->ce_varlinenum); errors++; } return errors; } int _conf_deny(ConfigFile *conf, ConfigEntry *ce) { Hook *h; if (!strcmp(ce->ce_vardata, "dcc")) _conf_deny_dcc(conf, ce); else if (!strcmp(ce->ce_vardata, "channel")) _conf_deny_channel(conf, ce); else if (!strcmp(ce->ce_vardata, "link")) _conf_deny_link(conf, ce); else if (!strcmp(ce->ce_vardata, "version")) _conf_deny_version(conf, ce); else { int value; for (h = Hooks[HOOKTYPE_CONFIGRUN]; h; h = h->next) { value = (*(h->func.intfunc))(conf,ce,CONFIG_DENY); if (value == 1) break; } return 0; } return 0; } int _conf_deny_dcc(ConfigFile *conf, ConfigEntry *ce) { ConfigItem_deny_dcc *deny = NULL; ConfigEntry *cep; deny = MyMallocEx(sizeof(ConfigItem_deny_dcc)); for (cep = ce->ce_entries; cep; cep = cep->ce_next) { if (!strcmp(cep->ce_varname, "filename")) { safestrdup(deny->filename, cep->ce_vardata); } else if (!strcmp(cep->ce_varname, "reason")) { safestrdup(deny->reason, cep->ce_vardata); } else if (!strcmp(cep->ce_varname, "soft")) { int x = config_checkval(cep->ce_vardata,CFG_YESNO); if (x == 1) deny->flag.type = DCCDENY_SOFT; } } if (!deny->reason) { if (deny->flag.type == DCCDENY_HARD) safestrdup(deny->reason, "Possible infected virus file"); else safestrdup(deny->reason, "Possible executable content"); } AddListItem(deny, conf_deny_dcc); return 0; } int _conf_deny_channel(ConfigFile *conf, ConfigEntry *ce) { ConfigItem_deny_channel *deny = NULL; ConfigEntry *cep; deny = MyMallocEx(sizeof(ConfigItem_deny_channel)); for (cep = ce->ce_entries; cep; cep = cep->ce_next) { if (!strcmp(cep->ce_varname, "channel")) { safestrdup(deny->channel, cep->ce_vardata); } else if (!strcmp(cep->ce_varname, "redirect")) { safestrdup(deny->redirect, cep->ce_vardata); } else if (!strcmp(cep->ce_varname, "reason")) { safestrdup(deny->reason, cep->ce_vardata); } else if (!strcmp(cep->ce_varname, "warn")) { deny->warn = config_checkval(cep->ce_vardata,CFG_YESNO); } else if (!strcmp(cep->ce_varname, "class")) { safestrdup(deny->class, cep->ce_vardata); } } AddListItem(deny, conf_deny_channel); return 0; } int _conf_deny_link(ConfigFile *conf, ConfigEntry *ce) { ConfigItem_deny_link *deny = NULL; ConfigEntry *cep; deny = MyMallocEx(sizeof(ConfigItem_deny_link)); for (cep = ce->ce_entries; cep; cep = cep->ce_next) { if (!strcmp(cep->ce_varname, "mask")) { safestrdup(deny->mask, cep->ce_vardata); } else if (!strcmp(cep->ce_varname, "rule")) { deny->rule = (char *)crule_parse(cep->ce_vardata); safestrdup(deny->prettyrule, cep->ce_vardata); } else if (!strcmp(cep->ce_varname, "type")) { if (!strcmp(cep->ce_vardata, "all")) deny->flag.type = CRULE_ALL; else if (!strcmp(cep->ce_vardata, "auto")) deny->flag.type = CRULE_AUTO; } } AddListItem(deny, conf_deny_link); return 0; } int _conf_deny_version(ConfigFile *conf, ConfigEntry *ce) { ConfigItem_deny_version *deny = NULL; ConfigEntry *cep; deny = MyMallocEx(sizeof(ConfigItem_deny_version)); for (cep = ce->ce_entries; cep; cep = cep->ce_next) { if (!strcmp(cep->ce_varname, "mask")) { safestrdup(deny->mask, cep->ce_vardata); } else if (!strcmp(cep->ce_varname, "version")) { safestrdup(deny->version, cep->ce_vardata); } else if (!strcmp(cep->ce_varname, "flags")) { safestrdup(deny->flags, cep->ce_vardata); } } AddListItem(deny, conf_deny_version); return 0; } int _test_deny(ConfigFile *conf, ConfigEntry *ce) { ConfigEntry *cep; int errors = 0; Hook *h; if (!ce->ce_vardata) { config_error("%s:%i: deny without type", ce->ce_fileptr->cf_filename, ce->ce_varlinenum); return 1; } if (!strcmp(ce->ce_vardata, "dcc")) { char has_filename = 0, has_reason = 0, has_soft = 0; for (cep = ce->ce_entries; cep; cep = cep->ce_next) { if (config_is_blankorempty(cep, "deny dcc")) { errors++; continue; } if (!strcmp(cep->ce_varname, "filename")) { if (has_filename) { config_warn_duplicate(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, "deny dcc::filename"); continue; } has_filename = 1; } else if (!strcmp(cep->ce_varname, "reason")) { if (has_reason) { config_warn_duplicate(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, "deny dcc::reason"); continue; } has_reason = 1; } else if (!strcmp(cep->ce_varname, "soft")) { if (has_soft) { config_warn_duplicate(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, "deny dcc::soft"); continue; } has_soft = 1; } else { config_error_unknown(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, "deny dcc", cep->ce_varname); errors++; } } if (!has_filename) { config_error_missing(ce->ce_fileptr->cf_filename, ce->ce_varlinenum, "deny dcc::filename"); errors++; } if (!has_reason) { config_error_missing(ce->ce_fileptr->cf_filename, ce->ce_varlinenum, "deny dcc::reason"); errors++; } } else if (!strcmp(ce->ce_vardata, "channel")) { char has_channel = 0, has_warn = 0, has_reason = 0, has_redirect = 0, has_class = 0; for (cep = ce->ce_entries; cep; cep = cep->ce_next) { if (config_is_blankorempty(cep, "deny channel")) { errors++; continue; } if (!strcmp(cep->ce_varname, "channel")) { if (has_channel) { config_warn_duplicate(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, "deny channel::channel"); continue; } has_channel = 1; } else if (!strcmp(cep->ce_varname, "redirect")) { if (has_redirect) { config_warn_duplicate(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, "deny channel::redirect"); continue; } has_redirect = 1; } else if (!strcmp(cep->ce_varname, "reason")) { if (has_reason) { config_warn_duplicate(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, "deny channel::reason"); continue; } has_reason = 1; } else if (!strcmp(cep->ce_varname, "warn")) { if (has_warn) { config_warn_duplicate(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, "deny channel::warn"); continue; } has_warn = 1; } else if (!strcmp(cep->ce_varname, "class")) { if (has_class) { config_warn_duplicate(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, "deny channel::class"); continue; } has_class = 1; } else { config_error_unknown(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, "deny channel", cep->ce_varname); errors++; } } if (!has_channel) { config_error_missing(ce->ce_fileptr->cf_filename, ce->ce_varlinenum, "deny channel::channel"); errors++; } if (!has_reason) { config_error_missing(ce->ce_fileptr->cf_filename, ce->ce_varlinenum, "deny channel::reason"); errors++; } } else if (!strcmp(ce->ce_vardata, "link")) { char has_mask = 0, has_rule = 0, has_type = 0; for (cep = ce->ce_entries; cep; cep = cep->ce_next) { if (config_is_blankorempty(cep, "deny link")) { errors++; continue; } if (!strcmp(cep->ce_varname, "mask")) { if (has_mask) { config_warn_duplicate(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, "deny link::mask"); continue; } has_mask = 1; } else if (!strcmp(cep->ce_varname, "rule")) { int val = 0; if (has_rule) { config_warn_duplicate(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, "deny link::rule"); continue; } has_rule = 1; if ((val = crule_test(cep->ce_vardata))) { config_error("%s:%i: deny link::rule contains an invalid expression: %s", cep->ce_fileptr->cf_filename, cep->ce_varlinenum, crule_errstring(val)); errors++; } } else if (!strcmp(cep->ce_varname, "type")) { if (has_type) { config_warn_duplicate(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, "deny link::type"); continue; } has_type = 1; if (!strcmp(cep->ce_vardata, "auto")) ; else if (!strcmp(cep->ce_vardata, "all")) ; else { config_status("%s:%i: unknown deny link type", cep->ce_fileptr->cf_filename, cep->ce_varlinenum); errors++; } } else { config_error_unknown(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, "deny link", cep->ce_varname); errors++; } } if (!has_mask) { config_error_missing(ce->ce_fileptr->cf_filename, ce->ce_varlinenum, "deny link::mask"); errors++; } if (!has_rule) { config_error_missing(ce->ce_fileptr->cf_filename, ce->ce_varlinenum, "deny link::rule"); errors++; } if (!has_type) { config_error_missing(ce->ce_fileptr->cf_filename, ce->ce_varlinenum, "deny link::type"); errors++; } } else if (!strcmp(ce->ce_vardata, "version")) { char has_mask = 0, has_version = 0, has_flags = 0; for (cep = ce->ce_entries; cep; cep = cep->ce_next) { if (config_is_blankorempty(cep, "deny version")) { errors++; continue; } if (!strcmp(cep->ce_varname, "mask")) { if (has_mask) { config_warn_duplicate(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, "deny version::mask"); continue; } has_mask = 1; } else if (!strcmp(cep->ce_varname, "version")) { if (has_version) { config_warn_duplicate(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, "deny version::version"); continue; } has_version = 1; } else if (!strcmp(cep->ce_varname, "flags")) { if (has_flags) { config_warn_duplicate(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, "deny version::flags"); continue; } has_flags = 1; } else { config_error_unknown(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, "deny version", cep->ce_varname); errors++; } } if (!has_mask) { config_error_missing(ce->ce_fileptr->cf_filename, ce->ce_varlinenum, "deny version::mask"); errors++; } if (!has_version) { config_error_missing(ce->ce_fileptr->cf_filename, ce->ce_varlinenum, "deny version::version"); errors++; } if (!has_flags) { config_error_missing(ce->ce_fileptr->cf_filename, ce->ce_varlinenum, "deny version::flags"); errors++; } } else { int used = 0; for (h = Hooks[HOOKTYPE_CONFIGTEST]; h; h = h->next) { int value, errs = 0; if (h->owner && !(h->owner->flags & MODFLAG_TESTING) && !(h->owner->options & MOD_OPT_PERM)) continue; value = (*(h->func.intfunc))(conf,ce,CONFIG_DENY, &errs); if (value == 2) used = 1; if (value == 1) { used = 1; break; } if (value == -1) { used = 1; errors += errs; break; } if (value == -2) { used = 1; errors += errs; } } if (!used) { config_error("%s:%i: unknown deny type %s", ce->ce_fileptr->cf_filename, ce->ce_varlinenum, ce->ce_vardata); return 1; } return errors; } return errors; } #ifdef USE_LIBCURL static void conf_download_complete(const char *url, const char *file, const char *errorbuf, int cached, void *inc_key) { ConfigItem_include *inc; if (!loop.ircd_rehashing) return; /* use inc_key to find the correct include block. This should be cheaper than using the full URL. */ for (inc = conf_include; inc; inc = (ConfigItem_include *)inc->next) { if ( inc_key != (void *)inc ) continue; if (!(inc->flag.type & INCLUDE_REMOTE)) continue; if (inc->flag.type & INCLUDE_NOTLOADED) continue; if (stricmp(url, inc->url)) continue; inc->flag.type &= ~INCLUDE_DLQUEUED; break; } if (!inc) { ircd_log(LOG_ERROR, "Downloaded remote include which matches no include statement."); return; } if (!file && !cached) update_remote_include(inc, file, 0, errorbuf); /* DOWNLOAD FAILED */ else { char *urlfile = url_getfilename(url); char *file_basename = unreal_getfilename(urlfile); char *tmp = unreal_mktemp(TMPDIR, file_basename ? file_basename : "download.conf"); free(urlfile); if (cached) { unreal_copyfileex(inc->file, tmp, 1); #ifdef REMOTEINC_SPECIALCACHE unreal_copyfileex(inc->file, unreal_mkcache(url), 0); #endif update_remote_include(inc, tmp, 0, NULL); } else { /* copy/hardlink file to another file because our caller will remove(file). */ unreal_copyfileex(file, tmp, 1); update_remote_include(inc, tmp, 0, NULL); #ifdef REMOTEINC_SPECIALCACHE unreal_copyfileex(file, unreal_mkcache(url), 0); #endif } } for (inc = conf_include; inc; inc = (ConfigItem_include *)inc->next) { if (inc->flag.type & INCLUDE_DLQUEUED) return; } rehash_internal(loop.rehash_save_cptr, loop.rehash_save_sptr, loop.rehash_save_sig); } #endif int rehash(aClient *cptr, aClient *sptr, int sig) { #ifdef USE_LIBCURL ConfigItem_include *inc; char found_remote = 0; if (loop.ircd_rehashing) { if (!sig) sendto_one(sptr, ":%s NOTICE %s :A rehash is already in progress", me.name, sptr->name); return 0; } loop.ircd_rehashing = 1; loop.rehash_save_cptr = cptr; loop.rehash_save_sptr = sptr; loop.rehash_save_sig = sig; for (inc = conf_include; inc; inc = (ConfigItem_include *)inc->next) { time_t modtime; if (!(inc->flag.type & INCLUDE_REMOTE)) continue; if (inc->flag.type & INCLUDE_NOTLOADED) continue; found_remote = 1; modtime = unreal_getfilemodtime(inc->file); inc->flag.type |= INCLUDE_DLQUEUED; /* use (void *)inc as the key for finding which include block conf_download_complete() should use. */ download_file_async(inc->url, modtime, conf_download_complete, (void *)inc); } if (!found_remote) return rehash_internal(cptr, sptr, sig); return 0; #else loop.ircd_rehashing = 1; return rehash_internal(cptr, sptr, sig); #endif } int rehash_internal(aClient *cptr, aClient *sptr, int sig) { if (sig == 1) { sendto_ops("Got signal SIGHUP, reloading %s file", configfile); #ifdef ULTRIX if (fork() > 0) exit(0); write_pidfile(); #endif } loop.ircd_rehashing = 1; /* double checking.. */ if (init_conf(configfile, 1) == 0) run_configuration(); if (sig == 1) reread_motdsandrules(); unload_all_unused_snomasks(); unload_all_unused_umodes(); unload_all_unused_extcmodes(); extcmodes_check_for_changes(); loop.ircd_rehashing = 0; remote_rehash_client = NULL; return 1; } void link_cleanup(ConfigItem_link *link_ptr) { safefree(link_ptr->servername); unreal_delete_masks(link_ptr->incoming.mask); Auth_DeleteAuthStruct(link_ptr->auth); safefree(link_ptr->outgoing.bind_ip); safefree(link_ptr->outgoing.hostname); safefree(link_ptr->hub); safefree(link_ptr->leaf); safefree(link_ptr->ciphers); } void delete_linkblock(ConfigItem_link *link_ptr) { Debug((DEBUG_ERROR, "delete_linkblock: deleting %s, refcount=%d", link_ptr->servername, link_ptr->refcount)); if (link_ptr->class) { link_ptr->class->xrefcount--; /* Perhaps the class is temporary too and we need to free it... */ if (link_ptr->class->flag.temporary && !link_ptr->class->clients && !link_ptr->class->xrefcount) { delete_classblock(link_ptr->class); link_ptr->class = NULL; } } link_cleanup(link_ptr); DelListItem(link_ptr, conf_link); MyFree(link_ptr); } void delete_classblock(ConfigItem_class *class_ptr) { Debug((DEBUG_ERROR, "delete_classblock: deleting %s, clients=%d, xrefcount=%d", class_ptr->name, class_ptr->clients, class_ptr->xrefcount)); safefree(class_ptr->name); DelListItem(class_ptr, conf_class); MyFree(class_ptr); } void listen_cleanup() { int i = 0; ConfigItem_listen *listen_ptr; ListStruct *next; for (listen_ptr = conf_listen; listen_ptr; listen_ptr = (ConfigItem_listen *)next) { next = (ListStruct *)listen_ptr->next; if (listen_ptr->flag.temporary && !listen_ptr->clients) { safefree(listen_ptr->ip); DelListItem(listen_ptr, conf_listen); MyFree(listen_ptr); i++; } } if (i) close_listeners(); } #ifdef USE_LIBCURL char *find_remote_include(char *url, char **errorbuf) { ConfigItem_include *inc; for (inc = conf_include; inc; inc = (ConfigItem_include *)inc->next) { if (!(inc->flag.type & INCLUDE_NOTLOADED)) continue; if (!(inc->flag.type & INCLUDE_REMOTE)) continue; if (!stricmp(url, inc->url)) { *errorbuf = inc->errorbuf; return inc->file; } } return NULL; } char *find_loaded_remote_include(char *url) { ConfigItem_include *inc; for (inc = conf_include; inc; inc = (ConfigItem_include *)inc->next) { if ((inc->flag.type & INCLUDE_NOTLOADED)) continue; if (!(inc->flag.type & INCLUDE_REMOTE)) continue; if (!stricmp(url, inc->url)) return inc->file; } return NULL; } /** * Non-asynchronous remote inclusion to give a user better feedback * when first starting his IRCd. * * The asynchronous friend is rehash() which merely queues remote * includes for download using download_file_async(). */ int remote_include(ConfigEntry *ce) { char *errorbuf = NULL; char *url = ce->ce_vardata; char *file = find_remote_include(url, &errorbuf); int ret; if (!loop.ircd_rehashing || (loop.ircd_rehashing && !file && !errorbuf)) { char *error; if (config_verbose > 0) config_status("Downloading %s", url); file = download_file(url, &error); if (!file) { #ifdef REMOTEINC_SPECIALCACHE if (has_cached_version(url)) { config_warn("%s:%i: include: error downloading '%s': %s -- using cached version instead.", ce->ce_fileptr->cf_filename, ce->ce_varlinenum, url, error); file = strdup(unreal_mkcache(url)); /* Let it pass to load_conf()... */ } else { #endif config_error("%s:%i: include: error downloading '%s': %s", ce->ce_fileptr->cf_filename, ce->ce_varlinenum, url, error); return -1; #ifdef REMOTEINC_SPECIALCACHE } #endif } else { #ifdef REMOTEINC_SPECIALCACHE unreal_copyfileex(file, unreal_mkcache(url), 0); #endif } add_remote_include(file, url, 0, NULL, ce->ce_fileptr->cf_filename, ce->ce_varlinenum); ret = load_conf(file, url); free(file); return ret; } else { if (errorbuf) { #ifdef REMOTEINC_SPECIALCACHE if (has_cached_version(url)) { config_warn("%s:%i: include: error downloading '%s': %s -- using cached version instead.", ce->ce_fileptr->cf_filename, ce->ce_varlinenum, url, errorbuf); /* Let it pass to load_conf()... */ file = strdup(unreal_mkcache(url)); } else { #endif config_error("%s:%i: include: error downloading '%s': %s", ce->ce_fileptr->cf_filename, ce->ce_varlinenum, url, errorbuf); return -1; #ifdef REMOTEINC_SPECIALCACHE } #endif } if (config_verbose > 0) config_status("Loading %s from download", url); add_remote_include(file, url, 0, NULL, ce->ce_fileptr->cf_filename, ce->ce_varlinenum); ret = load_conf(file, url); return ret; } return 0; } #endif /** * Add an item to the conf_include list for the specified file. * * Checks for whether or not we're performing recursive includes * belong in conf_load() because that function is able to return an * error code. Any checks in here will end up being ignored by callers * and thus will gain us nothing. * * @param file path to the include file. */ void add_include(const char *file, const char *included_from, int included_from_line) { ConfigItem_include *inc; inc = MyMallocEx(sizeof(ConfigItem_include)); inc->file = strdup(file); inc->flag.type = INCLUDE_NOTLOADED; inc->included_from = strdup(included_from); inc->included_from_line = included_from_line; AddListItem(inc, conf_include); } #ifdef USE_LIBCURL /** * Adds a remote include entry to the config_include list. * * This is to be called whenever the included_from and * included_from_line parameters are known. This means that during a * rehash when downloads are done asynchronously, you call this with * the inclued_from and included_from_line information. After the * download is complete and you know there it is stored in the FS, * call update_remote_include(). */ void add_remote_include(const char *file, const char *url, int flags, const char *errorbuf, const char *included_from, int included_from_line) { ConfigItem_include *inc; /* we rely on MyMallocEx() zeroing the ConfigItem_include */ inc = MyMallocEx(sizeof(ConfigItem_include)); if (included_from) { inc->included_from = strdup(included_from); inc->included_from_line = included_from_line; } inc->url = strdup(url); update_remote_include(inc, file, INCLUDE_NOTLOADED|INCLUDE_REMOTE|flags, errorbuf); AddListItem(inc, conf_include); } /** * Update certain information in a remote include's config_include list entry. * * @param file the place on disk where the downloaded remote include * may be found * @param flags additional flags to set on the config_include entry * @param errorbuf non-NULL if there were errors encountered in * downloading. The error will be stored into the config_include * entry. */ void update_remote_include(ConfigItem_include *inc, const char *file, int flags, const char *errorbuf) { /* * file may be NULL when errorbuf is non-NULL and vice-versa. */ if (file) inc->file = strdup(file); inc->flag.type |= flags; if (errorbuf) inc->errorbuf = strdup(errorbuf); } #endif /** * Clean up conf_include after a rehash fails because of a * configuration file error. * * Duplicates some in unload_loaded_include(). */ void unload_notloaded_includes(void) { ConfigItem_include *inc, *next; for (inc = conf_include; inc; inc = next) { next = (ConfigItem_include *)inc->next; if ((inc->flag.type & INCLUDE_NOTLOADED) || !(inc->flag.type & INCLUDE_USED)) { #ifdef USE_LIBCURL if (inc->flag.type & INCLUDE_REMOTE) { remove(inc->file); free(inc->url); if (inc->errorbuf) free(inc->errorbuf); } #endif free(inc->file); free(inc->included_from); DelListItem(inc, conf_include); free(inc); } } } /** * Clean up conf_include after a successful rehash to make way for * load_includes(). */ void unload_loaded_includes(void) { ConfigItem_include *inc, *next; for (inc = conf_include; inc; inc = next) { next = (ConfigItem_include *)inc->next; if (!(inc->flag.type & INCLUDE_NOTLOADED) || !(inc->flag.type & INCLUDE_USED)) { #ifdef USE_LIBCURL if (inc->flag.type & INCLUDE_REMOTE) { remove(inc->file); free(inc->url); if (inc->errorbuf) free(inc->errorbuf); } #endif free(inc->file); free(inc->included_from); DelListItem(inc, conf_include); free(inc); } } } /** * Mark loaded includes as loaded by removing the INCLUDE_NOTLOADED * flag. Meant to be called only after calling * unload_loaded_includes(). */ void load_includes(void) { ConfigItem_include *inc; /* Doing this for all the includes should actually be faster * than only doing it for includes that are not-loaded */ for (inc = conf_include; inc; inc = (ConfigItem_include *)inc->next) inc->flag.type &= ~INCLUDE_NOTLOADED; } /** Check if an important SSL option is used in config. Only checks link and listen at this time. * This will generate a warning on boot & REHASH. * If booting, the IRCd will stop. */ int ssl_used_in_config_but_unavail(void) { int errors = 0; ConfigItem_link *link; ConfigItem_listen *listener; if (ctx_server && ctx_client) return 0; /* everything is functional */ for (listener = conf_listen; listener; listener = (ConfigItem_listen *)listener->next) if (listener->options & LISTENER_SSL) { config_error("Listen block %s:%d is configured to use SSL, however SSL is unavailable due to an earlier error (certificate/key not loaded?)", listener->ip, listener->port); errors++; } for (link = conf_link; link; link = (ConfigItem_link *)link->next) if (link->options & CONNECT_SSL) { config_error("Link block %s is configured to use SSL, however SSL is unavailable due to an earlier error (certificate/key not loaded?)", link->servername); errors++; } return (errors ? 1 : 0); } /** Check if the user attempts to unload (eg: by commenting out) a module * that is currently loaded and is tagged as MOD_OPT_PERM_RELOADABLE * (in other words: a module that allows re-loading but not un-loading) */ int reloadable_perm_module_unloaded(void) { Module *m, *m2; extern Module *Modules; int ret = 0; for (m = Modules; m; m = m->next) { if ((m->options & MOD_OPT_PERM_RELOADABLE) && (m->flags & MODFLAG_LOADED)) { /* For each module w/MOD_OPT_PERM_RELOADABLE that is currently fully loaded... */ int found = 0; for (m2 = Modules; m2; m2 = m2->next) { if ((m != m2) && !strcmp(m->header->name, m2->header->name)) found = 1; } if (!found) { config_error("Attempt to unload module '%s' is not permitted. Module is permanent and reloadable only.", m->header->name); ret = 1; /* we don't return straight away so the user gets to see all errors and not just one */ } } } return ret; }