/* * 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_operflag OperFlag; struct _conf_operflag { 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_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); static int _conf_cgiirc (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_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); static int _test_cgiirc (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 }, { "cgiirc", _conf_cgiirc, _test_cgiirc }, { "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 }, { "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 }, }; static int _OldOperFlags[] = { OFLAG_LOCAL, 'o', OFLAG_GLOBAL, 'O', OFLAG_REHASH, 'r', OFLAG_DIE, 'D', OFLAG_RESTART, 'R', OFLAG_HELPOP, 'h', OFLAG_GLOBOP, 'g', OFLAG_WALLOP, 'w', OFLAG_LOCOP, 'l', OFLAG_LROUTE, 'c', OFLAG_GROUTE, 'L', OFLAG_LKILL, 'k', OFLAG_GKILL, 'K', OFLAG_KLINE, 'b', OFLAG_UNKLINE, 'B', OFLAG_LNOTICE, 'n', OFLAG_GNOTICE, 'G', OFLAG_ADMIN_, 'A', OFLAG_SADMIN_, 'a', OFLAG_NADMIN, 'N', OFLAG_COADMIN, 'C', OFLAG_ZLINE, 'z', OFLAG_WHOIS, 'W', OFLAG_HIDE, 'H', OFLAG_TKL, 't', OFLAG_GZL, 'Z', OFLAG_OVERRIDE, 'v', OFLAG_UMODEQ, 'q', OFLAG_DCCDENY, 'd', OFLAG_ADDLINE, 'X', OFLAG_TSCTL, 'T', 0, 0 }; /* This MUST be alphabetized */ static OperFlag _OperFlags[] = { { OFLAG_ADMIN_, "admin"}, { OFLAG_ADDLINE, "can_addline"}, { OFLAG_DCCDENY, "can_dccdeny"}, { OFLAG_DIE, "can_die" }, { OFLAG_TKL, "can_gkline"}, { OFLAG_GKILL, "can_globalkill" }, { OFLAG_GNOTICE, "can_globalnotice" }, { OFLAG_GROUTE, "can_globalroute" }, { OFLAG_GLOBOP, "can_globops" }, { OFLAG_GZL, "can_gzline"}, { OFLAG_KLINE, "can_kline" }, { OFLAG_LKILL, "can_localkill" }, { OFLAG_LNOTICE, "can_localnotice" }, { OFLAG_LROUTE, "can_localroute" }, { OFLAG_OVERRIDE, "can_override" }, { OFLAG_REHASH, "can_rehash" }, { OFLAG_RESTART, "can_restart" }, { OFLAG_UMODEQ, "can_setq" }, { OFLAG_TSCTL, "can_tsctl" }, { OFLAG_UNKLINE, "can_unkline" }, { OFLAG_WALLOP, "can_wallops" }, { OFLAG_ZLINE, "can_zline"}, { OFLAG_COADMIN_, "coadmin"}, { OFLAG_HIDE, "get_host"}, { OFLAG_WHOIS, "get_umodew"}, { OFLAG_GLOBAL, "global" }, { OFLAG_HELPOP, "helpop" }, { OFLAG_LOCAL, "local" }, { OFLAG_LOCOP, "locop"}, { OFLAG_NADMIN, "netadmin"}, { OFLAG_SADMIN_, "services-admin"}, }; /* This MUST be alphabetized */ static OperFlag _ListenerFlags[] = { { LISTENER_CLIENTSONLY, "clientsonly"}, { LISTENER_DEFER_ACCEPT, "defer-accept"}, { LISTENER_SERVERSONLY, "serversonly"}, { LISTENER_SSL, "ssl"}, { LISTENER_NORMAL, "standard"}, }; /* This MUST be alphabetized */ static OperFlag _LinkFlags[] = { { CONNECT_AUTO, "autoconnect" }, { CONNECT_NODNSCACHE, "nodnscache" }, { CONNECT_NOHOSTCHECK, "nohostcheck" }, { CONNECT_QUARANTINE, "quarantine"}, { CONNECT_SSL, "ssl" }, }; /* This MUST be alphabetized */ static OperFlag _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 OperFlag 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" } }; #ifdef USE_SSL /* This MUST be alphabetized */ static OperFlag _SSLFlags[] = { { SSLFLAG_FAILIFNOCERT, "fail-if-no-clientcert" }, { SSLFLAG_DONOTACCEPTSELFSIGNED, "no-self-signed" }, { SSLFLAG_NOSTARTTLS, "no-starttls" }, { SSLFLAG_VERIFYCERT, "verify-certificate" }, }; #endif struct { unsigned conf_me : 1; unsigned conf_admin : 1; unsigned conf_listen : 1; } requiredstuff; struct SetCheck settings; /* * Utilities */ void ipport_seperate(char *string, char **ip, char **port); 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); void delete_cgiircblock(ConfigItem_cgiirc *e); /* * 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_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_cgiirc *conf_cgiirc = 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; aConfiguration iConf; MODVAR aConfiguration tempiConf; MODVAR ConfigFile *conf = NULL; MODVAR int config_error_flag = 0; int config_verbose = 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); /* Pick out the ip address and the port number from a string. * The string syntax is: ip:port. ip must be enclosed in brackets ([]) if its an ipv6 * address because they contain colon (:) separators. The ip part is optional. If the string * contains a single number its assumed to be a port number. * * Returns with ip pointing to the ip address (if one was specified), a "*" (if only a port * was specified), or an empty string if there was an error. port is returned pointing to the * port number if one was specified, otherwise it points to a empty string. */ void ipport_seperate(char *string, char **ip, char **port) { char *f; /* assume failure */ *ip = *port = ""; /* sanity check */ if (string && strlen(string) > 0) { /* handle ipv6 type of ip address */ if (*string == '[') { if ((f = strrchr(string, ']'))) { *ip = string + 1; /* skip [ */ *f = '\0'; /* terminate the ip string */ /* next char must be a : if a port was specified */ if (*++f == ':') { *port = ++f; } } } /* handle ipv4 and port */ else if ((f = strchr(string, ':'))) { /* we found a colon... we may have ip:port or just :port */ if (f == string) { /* we have just :port */ *ip = "*"; } else { /* we have ip:port */ *ip = string; *f = '\0'; } *port = ++f; } /* no ip was specified, just a port number */ else if (!strcmp(string, my_itoa(atoi(string)))) { *ip = "*"; *port = string; } } } 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; } int iplist_onlist(IPList *iplist, char *ip) { IPList *e; for (e = iplist; e; e = e->next) if (!match(e->mask, ip)) return 1; return 0; } 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 = MyMalloc(strlen(params)+1); strcpy(parambuf, 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, " "); 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); } } /* 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; 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_status("%s:%i: No name for section start\n", filename, linenumber); continue; } else if (curce->ce_entries) { config_status("%s:%i: Ignoring extra section start\n", filename, linenumber); 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\n", filename, linenumber); config_entry_free(curce); config_free(curcf); return NULL; } else if (!cursection) { config_status("%s:%i: Ignoring extra close brace\n", filename, linenumber); 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 this line does not end\n", filename, commentstart); config_entry_free(curce); config_free(curcf); return NULL; } } break; case '\"': if (curce && curce->ce_varlinenum != linenumber && cursection) { config_warn("%s:%i: Missing semicolon at end of line\n", filename, curce->ce_varlinenum); *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) == '\"') { char *tptr = ptr; while((*tptr = *(tptr+1))) tptr++; } } else if ((*ptr == '\"') || (*ptr == '\n')) break; } if (!*ptr || (*ptr == '\n')) { config_error("%s:%i: Unterminated quote found\n", filename, linenumber); config_entry_free(curce); config_free(curcf); return NULL; } if (curce) { if (curce->ce_vardata) { config_status("%s:%i: Ignoring extra data\n", filename, linenumber); } else { curce->ce_vardata = MyMalloc(ptr-start+1); strncpy(curce->ce_vardata, start, ptr-start); curce->ce_vardata[ptr-start] = '\0'; } } else { curce = MyMalloc(sizeof(ConfigEntry)); memset(curce, 0, sizeof(ConfigEntry)); curce->ce_varname = MyMalloc((ptr-start)+1); strncpy(curce->ce_varname, start, ptr-start); curce->ce_varname[ptr-start] = '\0'; 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: Unexpected EOF for variable starting at %i\n", filename, curce->ce_varlinenum); else if (cursection) config_error("%s: Unexpected EOF for section starting at %i\n", filename, cursection->ce_sectlinenum); else config_error("%s: Unexpected EOF.\n", filename); config_entry_free(curce); config_free(curcf); return NULL; } if (curce) { if (curce->ce_vardata) { config_status("%s:%i: Ignoring extra data\n", filename, linenumber); } else { curce->ce_vardata = MyMalloc(ptr-start+1); strncpy(curce->ce_vardata, start, ptr-start); curce->ce_vardata[ptr-start] = '\0'; } } else { curce = MyMalloc(sizeof(ConfigEntry)); memset(curce, 0, sizeof(ConfigEntry)); curce->ce_varname = MyMalloc((ptr-start)+1); strncpy(curce->ce_varname, start, ptr-start); curce->ce_varname[ptr-start] = '\0'; 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: Unexpected EOF for variable starting on line %i\n", filename, curce->ce_varlinenum); config_entry_free(curce); config_free(curcf); return NULL; } else if (cursection) { config_error("%s: Unexpected EOF for section starting on line %i\n", filename, cursection->ce_sectlinenum); 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'; if (!loop.ircd_booted) #ifndef _WIN32 fprintf(stderr, "[error] %s\n", buffer); #else win_log("[error] %s", buffer); #endif ircd_log(LOG_ERROR, "config error: %s", buffer); sendto_realops("error: %s", 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); } /* Like above */ 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'; if (!loop.ircd_booted) #ifndef _WIN32 fprintf(stderr, "* %s\n", buffer); #else win_log("* %s", buffer); #endif ircd_log(LOG_ERROR, "%s", buffer); sendto_realops("%s", 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'; if (!loop.ircd_booted) #ifndef _WIN32 fprintf(stderr, "[warning] %s\n", buffer); #else win_log("[warning] %s", buffer); #endif ircd_log(LOG_ERROR, "[warning] %s", buffer); sendto_realops("[warning] %s", 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'; if (!loop.ircd_booted) #ifndef _WIN32 fprintf(stderr, "* %s\n", buffer); #else 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) { ircfree(i->name_server); ircfree(i->kline_address); ircfree(i->gline_address); ircfree(i->auto_join_chans); ircfree(i->oper_auto_join_chans); ircfree(i->oper_only_stats); ircfree(i->channel_command_prefix); ircfree(i->oper_snomask); ircfree(i->user_snomask); ircfree(i->egd_path); ircfree(i->static_quit); #ifdef USE_SSL ircfree(i->x_server_cert_pem); ircfree(i->x_server_key_pem); ircfree(i->x_server_cipher_list); ircfree(i->trusted_ca_file); #endif ircfree(i->restrict_usermodes); ircfree(i->restrict_channelmodes); ircfree(i->restrict_extendedbans); ircfree(i->network.x_ircnetwork); ircfree(i->network.x_ircnet005); ircfree(i->network.x_defserv); ircfree(i->network.x_services_name); ircfree(i->network.x_oper_host); ircfree(i->network.x_admin_host); ircfree(i->network.x_locop_host); ircfree(i->network.x_sadmin_host); ircfree(i->network.x_netadmin_host); ircfree(i->network.x_coadmin_host); ircfree(i->network.x_hidden_host); ircfree(i->network.x_prefix_quit); ircfree(i->network.x_helpchan); ircfree(i->network.x_stats_server); ircfree(i->spamfilter_ban_reason); ircfree(i->spamfilter_virus_help_channel); ircfree(i->spamexcept_line); } int config_test(); void config_setdefaultsettings(aConfiguration *i) { 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 = 10; 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->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->name_server = strdup("127.0.0.1"); /* default, especially needed for w2003+ in some rare cases */ i->level_on_join = CHFL_CHANOP; i->watch_away_notification = 1; i->new_linking_protocol = 1; i->uhnames = 1; i->ping_cookie = 1; #ifdef INET6 i->default_ipv6_clone_mask = 64; #endif /* INET6 */ i->nicklen = NICKLEN; } /* 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)); } 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()) { 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; 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); ircfree(old_pid_file); write_pidfile(); } } 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(0); 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 counter; if (config_verbose > 0) config_status("Loading config file %s ..", filename); /* * 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->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; } 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); } /* 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; if (config_verbose > 1) config_status("Loading modules in %s", filename); for (ce = cfptr->cf_entries; ce; ce = ce->ce_next) if (!strcmp(ce->ce_varname, "loadmodule")) { ret = _conf_loadmodule(cfptr, ce); if (ret < 0) return ret; } 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 (ret < 0) return ret; } my_inc->flag.type |= INCLUDE_USED; return 1; } else { config_error("Could not load config file %s", filename); 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_cgiirc *cgiirc_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; /* clean out stuff that we don't use */ for (admin_ptr = conf_admin; admin_ptr; admin_ptr = (ConfigItem_admin *)next) { next = (ListStruct *)admin_ptr->next; ircfree(admin_ptr->line); DelListItem(admin_ptr, conf_admin); MyFree(admin_ptr); } /* wipe the fckers out ..*/ for (oper_ptr = conf_oper; oper_ptr; oper_ptr = (ConfigItem_oper *)next) { ConfigItem_oper_from *oper_from; next = (ListStruct *)oper_ptr->next; ircfree(oper_ptr->name); ircfree(oper_ptr->swhois); ircfree(oper_ptr->snomask); Auth_DeleteAuthStruct(oper_ptr->auth); for (oper_from = (ConfigItem_oper_from *) oper_ptr->from; oper_from; oper_from = (ConfigItem_oper_from *) next2) { next2 = (ListStruct *)oper_from->next; ircfree(oper_from->name); if (oper_from->netmask) { MyFree(oper_from->netmask); } DelListItem(oper_from, oper_ptr->from); MyFree(oper_from); } DelListItem(oper_ptr, conf_oper); 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 */ ircfree(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; ircfree(allow_ptr->ip); ircfree(allow_ptr->hostname); if (allow_ptr->netmask) MyFree(allow_ptr->netmask); 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; ircfree(except_ptr->mask); if (except_ptr->netmask) MyFree(except_ptr->netmask); 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) { ircfree(ban_ptr->mask); ircfree(ban_ptr->reason); if (ban_ptr->netmask) MyFree(ban_ptr->netmask); 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; ircfree(tld_ptr->motd_file); ircfree(tld_ptr->rules_file); ircfree(tld_ptr->smotd_file); ircfree(tld_ptr->opermotd_file); ircfree(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_oper_from *vhost_from; next = (ListStruct *)vhost_ptr->next; ircfree(vhost_ptr->login); Auth_DeleteAuthStruct(vhost_ptr->auth); ircfree(vhost_ptr->virthost); ircfree(vhost_ptr->virtuser); for (vhost_from = (ConfigItem_oper_from *) vhost_ptr->from; vhost_from; vhost_from = (ConfigItem_oper_from *) next2) { next2 = (ListStruct *)vhost_from->next; ircfree(vhost_from->name); DelListItem(vhost_from, vhost_ptr->from); MyFree(vhost_from); } 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) { ircfree(deny_dcc_ptr->filename); ircfree(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; ircfree(deny_link_ptr->prettyrule); ircfree(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; ircfree(deny_version_ptr->mask); ircfree(deny_version_ptr->version); ircfree(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; ircfree(deny_channel_ptr->redirect); ircfree(deny_channel_ptr->channel); ircfree(deny_channel_ptr->reason); ircfree(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; ircfree(allow_channel_ptr->channel); ircfree(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) { ircfree(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; ircfree(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); ircfree(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; ircfree(alias_ptr->nick); del_Command(alias_ptr->alias, cmptr->func); ircfree(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; ircfree(fmt->format); ircfree(fmt->nick); ircfree(fmt->parameters); regfree(&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; ircfree(help_ptr->command); while (help_ptr->text) { text = help_ptr->text->next; ircfree(help_ptr->text->line); ircfree(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; ircfree(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; ircfree(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]); } for (cgiirc_ptr = conf_cgiirc; cgiirc_ptr; cgiirc_ptr = (ConfigItem_cgiirc *) next) { next = (ListStruct *)cgiirc_ptr->next; delete_cgiircblock(cgiirc_ptr); } /* reset conf_files -- should this be in its own function? no, because it's only used here */ ircfree(conf_files->motd_file); ircfree(conf_files->smotd_file); ircfree(conf_files->opermotd_file); ircfree(conf_files->svsmotd_file); ircfree(conf_files->botmotd_file); ircfree(conf_files->rules_file); ircfree(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. */ ircfree(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 0 if (!settings.has_dns_nameserver) Error("set::dns::nameserver is missing"); if (!settings.has_dns_timeout) Error("set::dns::timeout is missing"); if (!settings.has_dns_retries) Error("set::dns::retries is missing"); #endif 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_hosts_global) Error("set::hosts::global is missing"); if (!settings.has_hosts_admin) Error("set::hosts::admin is missing"); if (!settings.has_hosts_servicesadmin) Error("set::hosts::servicesadmin is missing"); if (!settings.has_hosts_netadmin) Error("set::hosts::netadmin is missing"); if (!settings.has_hosts_coadmin) Error("set::hosts::coadmin 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; #ifdef INET6 ConfigItem_allow *allow; #endif /* INET6 */ 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; } } } } #ifdef INET6 /* * 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; #endif /* INET6 */ 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); } OperFlag *config_binary_flags_search(OperFlag *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; 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); } 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; } 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) { ConfigItem_listen *p; if (!ipmask) return NULL; for (p = conf_listen; p; p = (ConfigItem_listen *) p->next) { 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, char *host, short type) { ConfigItem_except *excepts; if (!host) return NULL; for(excepts = conf_except; excepts; excepts =(ConfigItem_except *) excepts->next) { if (excepts->flag.type == type) { if (match_ip(sptr->ip, host, excepts->mask, excepts->netmask)) return excepts; } } return NULL; } ConfigItem_tld *Find_tld(aClient *cptr, char *uhost) { ConfigItem_tld *tld; if (!uhost || !cptr) return NULL; for(tld = conf_tld; tld; tld = (ConfigItem_tld *) tld->next) { if (!match(tld->mask, uhost)) { 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 *username, char *hostname, char *ip, char *servername) { ConfigItem_link *link; if (!username || !hostname || !servername || !ip) return NULL; for(link = conf_link; link; link = (ConfigItem_link *) link->next) { if (!match(link->servername, servername) && !match(link->username, username) && (!match(link->hostname, hostname) || !match(link->hostname, ip))) return link; } return NULL; } /* ugly ugly ugly */ int match_ip46(char *a, char *b) { #ifdef INET6 if (!strncmp(a, "::ffff:", 7) && !strcmp(a+7, b)) return 0; // match #endif return 1; //nomatch } ConfigItem_cgiirc *Find_cgiirc(char *username, char *hostname, char *ip, CGIIRCType type) { ConfigItem_cgiirc *e; if (!username || !hostname || !ip) return NULL; for (e = conf_cgiirc; e; e = (ConfigItem_cgiirc *)e->next) { if ((e->type == type) && (!e->username || !match(e->username, username)) && (!match(e->hostname, hostname) || !match(e->hostname, ip) || !match_ip46(e->hostname, ip))) return e; } 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_ip(sptr->ip, host, ban->mask, ban->netmask)) { /* Person got a exception */ if ((type == CONF_BAN_USER || type == CONF_BAN_IP) && Find_except(sptr, host, 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_ip(sptr->ip, host, ban->mask, ban->netmask)) { /* Person got a exception */ if (Find_except(sptr, host, 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]; #ifdef INET6 short is_ipv4; #endif /* INET6 */ for (aconf = conf_allow; aconf; aconf = (ConfigItem_allow *) aconf->next) { if (!aconf->hostname || !aconf->ip) goto attach; if (aconf->auth && !cptr->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_ip(cptr->ip, uhost, aconf->ip, aconf->netmask)) 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) strcpy(uhost, username); else strcpy(uhost, cptr->username); strcat(uhost, "@localhost"); } 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)); get_sockhost(cptr, uhost); #ifdef INET6 is_ipv4 = IN6_IS_ADDR_V4MAPPED(&cptr->ip); #endif /* INET6 */ /* FIXME */ if (aconf->maxperip) { aClient *acptr, *acptr2; ii = 1; list_for_each_entry_safe(acptr, acptr2, &lclient_list, lclient_node) { if ( #ifndef INET6 acptr->ip.S_ADDR == cptr->ip.S_ADDR) #else /* * match IPv4 exactly and the ipv6 * based on ipv6_clone_mask. */ (is_ipv4 ? !bcmp(acptr->ip.S_ADDR, cptr->ip.S_ADDR, sizeof(cptr->ip.S_ADDR)) : match_ipv6(&acptr->ip, &cptr->ip, aconf->ipv6_clone_mask))) #endif { ii++; if (ii > aconf->maxperip) { exit_client(cptr, cptr, &me, "Too many connections from your IP"); return -5; /* Already got one with that ip# */ } } } } if ((i = Auth_Check(cptr, aconf->auth, cptr->passwd)) == -1) { exit_client(cptr, cptr, &me, "Password mismatch"); return -5; } if ((i == 2) && (cptr->passwd)) { MyFree(cptr->passwd); cptr->passwd = NULL; } if (!((aconf->class->clients + 1) > aconf->class->maxclients)) { cptr->class = aconf->class; cptr->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->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->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; } /* * 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; } #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 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; ircstrdup(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")) { ircstrdup(conf_me->name, cep->ce_vardata); } else if (!strcmp(cep->ce_varname, "info")) { ircstrdup(conf_me->info, cep->ce_vardata); } else if (!strcmp(cep->ce_varname, "sid")) { ircstrdup(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")) ircstrdup(conf_files->motd_file, cep->ce_vardata); else if (!strcmp(cep->ce_varname, "shortmotd")) ircstrdup(conf_files->smotd_file, cep->ce_vardata); else if (!strcmp(cep->ce_varname, "opermotd")) ircstrdup(conf_files->opermotd_file, cep->ce_vardata); else if (!strcmp(cep->ce_varname, "svsmotd")) ircstrdup(conf_files->svsmotd_file, cep->ce_vardata); else if (!strcmp(cep->ce_varname, "botmotd")) ircstrdup(conf_files->botmotd_file, cep->ce_vardata); else if (!strcmp(cep->ce_varname, "rules")) ircstrdup(conf_files->rules_file, cep->ce_vardata); else if (!strcmp(cep->ce_varname, "tunefile")) ircstrdup(conf_files->tune_file, cep->ce_vardata); else if (!strcmp(cep->ce_varname, "pidfile")) ircstrdup(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 oper {} block parser */ int _conf_oper(ConfigFile *conf, ConfigEntry *ce) { ConfigEntry *cep; ConfigEntry *cepp; ConfigItem_oper *oper = NULL; ConfigItem_oper_from *from; OperFlag *ofp = NULL; struct irc_netmask tmp; 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, "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, "flags")) { if (!cep->ce_entries) { char *m = "*"; int *i, flag; for (m = (*cep->ce_vardata) ? cep->ce_vardata : m; *m; m++) { for (i = _OldOperFlags; (flag = *i); i += 2) if (*m == (char)(*(i + 1))) { oper->oflags |= flag; break; } } } else { for (cepp = cep->ce_entries; cepp; cepp = cepp->ce_next) { if ((ofp = config_binary_flags_search(_OperFlags, cepp->ce_varname, ARRAY_SIZEOF(_OperFlags)))) oper->oflags |= ofp->flag; } } } else if (!strcmp(cep->ce_varname, "swhois")) { ircstrdup(oper->swhois, cep->ce_vardata); } else if (!strcmp(cep->ce_varname, "snomask")) { ircstrdup(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, "from")) { for (cepp = cep->ce_entries; cepp; cepp = cepp->ce_next) { if (!strcmp(cepp->ce_varname, "userhost")) { from = MyMallocEx(sizeof(ConfigItem_oper_from)); ircstrdup(from->name, cepp->ce_vardata); tmp.type = parse_netmask(from->name, &tmp); if (tmp.type != HM_HOST) { from->netmask = MyMallocEx(sizeof(struct irc_netmask)); bcopy(&tmp, from->netmask, sizeof(struct irc_netmask)); } AddListItem(from, oper->from); } } } } AddListItem(oper, conf_oper); return 1; } int _test_oper(ConfigFile *conf, ConfigEntry *ce) { char has_class = 0, has_password = 0, has_flags = 0, has_swhois = 0, has_snomask = 0; char has_modes = 0, has_require_modes = 0, has_from = 0, has_maxlogins = 0; int oper_flags = 0; ConfigEntry *cep; ConfigEntry *cepp; OperFlag *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; } 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++; continue; } /* Regular variables */ if (!cep->ce_entries) { if (config_is_blankorempty(cep, "oper")) { errors++; continue; } /* oper::class */ 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")) { if (has_swhois) { config_warn_duplicate(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, "oper::swhois"); continue; } has_swhois = 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")) { if (has_flags) { config_warn_duplicate(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, "oper::flags"); continue; } has_flags = 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")) { if (has_flags) { config_warn_duplicate(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, "oper::flags"); continue; } has_flags = 1; for (cepp = cep->ce_entries; cepp; cepp = cepp->ce_next) { if (!cepp->ce_varname) { config_error_empty(cepp->ce_fileptr->cf_filename, cepp->ce_varlinenum, "oper::flags", cep->ce_varname); errors++; continue; } if (!(ofp = config_binary_flags_search(_OperFlags, cepp->ce_varname, ARRAY_SIZEOF(_OperFlags)))) { config_error_unknownflag(cepp->ce_fileptr->cf_filename, cepp->ce_varlinenum, "oper", cepp->ce_varname); errors++; } else oper_flags |= ofp->flag; } continue; } /* oper::from {} */ else if (!strcmp(cep->ce_varname, "from")) { if (has_from) { config_warn_duplicate(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, "oper::from"); continue; } has_from = 1; for (cepp = cep->ce_entries; cepp; cepp = cepp->ce_next) { if (config_is_blankorempty(cepp, "oper::from")) { errors++; continue; } /* Unknown Entry */ if (strcmp(cepp->ce_varname, "userhost")) { config_error_unknown(cepp->ce_fileptr->cf_filename, cepp->ce_varlinenum, "oper::from", cepp->ce_varname); errors++; continue; } } continue; } 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_from) { config_error_missing(ce->ce_fileptr->cf_filename, ce->ce_varlinenum, "oper::from"); errors++; } if (!has_flags) { config_error_missing(ce->ce_fileptr->cf_filename, ce->ce_varlinenum, "oper::flags"); errors++; } else { /* Check oper flags -- warning needed only (autoconvert) */ if (!(oper_flags & (OFLAG_GROUTE|OFLAG_GKILL|OFLAG_GNOTICE)) && (oper_flags & (OFLAG_GZL|OFLAG_TKL|OFLAG_OVERRIDE))) { config_warn("%s:%i: oper::oflags: can_gzline/can_gkline/can_override (global privileges) " "are incompatible with local oper -- user will be globop", ce->ce_fileptr->cf_filename, ce->ce_varlinenum); } } if (!has_class) { config_error_missing(ce->ce_fileptr->cf_filename, ce->ce_varlinenum, "oper::class"); 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)); ircstrdup(class->name, ce->ce_vardata); isnew = 1; } else { isnew = 0; class->flag.temporary = 0; class->options = 0; /* RESET OPTIONS */ } ircstrdup(class->name, ce->ce_vardata); class->connfreq = 60; /* default */ for (cep = ce->ce_entries; cep; cep = cep->ce_next) { if (!strcmp(cep->ce_varname, "pingfreq")) class->pingfreq = atol(cep->ce_vardata); else if (!strcmp(cep->ce_varname, "connfreq")) class->connfreq = atol(cep->ce_vardata); else if (!strcmp(cep->ce_varname, "maxclients")) class->maxclients = atol(cep->ce_vardata); else if (!strcmp(cep->ce_varname, "sendq")) class->sendq = atol(cep->ce_vardata); else if (!strcmp(cep->ce_varname, "recvq")) class->recvq = atol(cep->ce_vardata); 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"); errors++; } 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 = atol(cep->ce_vardata); 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 = atol(cep->ce_vardata); if ((l < 10) || (l > 604800)) { config_error("%s:%i: class::connfreq with illegal value (must be >10 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 = atol(cep->ce_vardata); 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 = atol(cep->ce_vardata); 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)); ircstrdup(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; OperFlag *ofp; char copy[256]; char *ip; char *port; int start, end, iport, isnew; int tmpflags =0; strcpy(copy, ce->ce_vardata); /* Seriously cheap hack to make listen work -Stskeeps */ ipport_seperate(copy, &ip, &port); if (!ip || !*ip) { return -1; } if (strchr(ip, '*') && strcmp(ip, "*")) { return -1; } if (!port || !*port) { return -1; } port_range(port, &start, &end); if ((start < 0) || (start > 65535) || (end < 0) || (end > 65535)) { return -1; } end++; for (cep = ce->ce_entries; cep; cep = cep->ce_next) { if (!strcmp(cep->ce_varname, "options")) { for (cepp = cep->ce_entries; cepp; cepp = cepp->ce_next) { if ((ofp = config_binary_flags_search(_ListenerFlags, cepp->ce_varname, ARRAY_SIZEOF(_ListenerFlags)))) tmpflags |= ofp->flag; } } } #ifndef USE_SSL tmpflags &= ~LISTENER_SSL; #endif for (iport = start; iport < end; iport++) { if (!(listen = Find_listen(ip, iport))) { listen = MyMallocEx(sizeof(ConfigItem_listen)); listen->ip = strdup(ip); listen->port = iport; listen->fd = -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; char copy[256]; char *ip; char *port; int start, end; int errors = 0; char has_options = 0; OperFlag *ofp; if (!ce->ce_vardata) { config_error("%s:%i: listen without ip:port", ce->ce_fileptr->cf_filename, ce->ce_varlinenum); return 1; } strcpy(copy, ce->ce_vardata); /* Seriously cheap hack to make listen work -Stskeeps */ ipport_seperate(copy, &ip, &port); if (!ip || !*ip) { config_error("%s:%i: listen: illegal ip:port mask", ce->ce_fileptr->cf_filename, ce->ce_varlinenum); return 1; } if (strchr(ip, '*') && strcmp(ip, "*")) { config_error("%s:%i: listen: illegal ip, (mask, and not '*')", ce->ce_fileptr->cf_filename, ce->ce_varlinenum); return 1; } if (!port || !*port) { config_error("%s:%i: listen: missing port in mask", ce->ce_fileptr->cf_filename, ce->ce_varlinenum); return 1; } #ifdef INET6 if ((strlen(ip) > 6) && !strchr(ip, ':') && isdigit(ip[strlen(ip)-1])) { char crap[32]; if (inet_pton(AF_INET, ip, crap) != 0) { char ipv6buf[128]; snprintf(ipv6buf, sizeof(ipv6buf), "[::ffff:%s]:%s", ip, port); ce->ce_vardata = strdup(ipv6buf); } else { /* Insert IPv6 validation here */ config_error("%s:%i: listen: '%s' looks like it might be IPv4, but is not a valid address.", ce->ce_fileptr->cf_filename, ce->ce_varlinenum, ip); return 1; } } #endif port_range(port, &start, &end); if (start == end) { if ((start < 0) || (start > 65535)) { config_error("%s:%i: listen: illegal port (must be 0..65535)", ce->ce_fileptr->cf_filename, ce->ce_varlinenum); return 1; } } else { if (end < start) { config_error("%s:%i: listen: illegal port range end value is less than starting value", ce->ce_fileptr->cf_filename, ce->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.", ce->ce_fileptr->cf_filename, ce->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", 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, "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) { 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, "class", cepp->ce_varname); errors++; continue; } #ifndef USE_SSL else if (ofp->flag & LISTENER_SSL) { config_warn("%s:%i: listen with SSL flag enabled on a non SSL compile", cep->ce_fileptr->cf_filename, cep->ce_varlinenum); } #endif } } else { config_error_unknown(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, "listen", cep->ce_varname); errors++; continue; } } requiredstuff.conf_listen = 1; return errors; } int _conf_allow(ConfigFile *conf, ConfigEntry *ce) { ConfigEntry *cep, *cepp; ConfigItem_allow *allow; Hook *h; struct irc_netmask tmp; 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); /* CIDR */ tmp.type = parse_netmask(allow->ip, &tmp); if (tmp.type != HM_HOST) { allow->netmask = MyMallocEx(sizeof(struct irc_netmask)); bcopy(&tmp, allow->netmask, sizeof(struct irc_netmask)); } } 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")) { #ifdef INET6 /* * 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); #endif /* INET6 */ } 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; } } } 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) { config_error_missing(ce->ce_fileptr->cf_filename, ce->ce_varlinenum, "allow::ip"); errors++; } if (!has_hostname) { config_error_missing(ce->ce_fileptr->cf_filename, ce->ce_varlinenum, "allow::hostname"); errors++; } if (!has_class) { config_error_missing(ce->ce_fileptr->cf_filename, ce->ce_varlinenum, "allow::class"); 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)); ircstrdup(allow->channel, cep->ce_vardata); if (class) ircstrdup(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")) ircstrdup(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; struct irc_netmask tmp; OperFlag *opf; ca = MyMallocEx(sizeof(ConfigItem_except)); ca->mask = strdup(mask); opf = config_binary_flags_search(ExceptTklFlags, type, ARRAY_SIZEOF(ExceptTklFlags)); ca->type = opf->flag; if (ca->type & TKL_KILL || ca->type & TKL_ZAP || ca->type & TKL_SHUN) { tmp.type = parse_netmask(ca->mask, &tmp); if (tmp.type != HM_HOST) { ca->netmask = MyMallocEx(sizeof(struct irc_netmask)); bcopy(&tmp, ca->netmask, sizeof(struct irc_netmask)); } } 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; struct irc_netmask tmp; 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); tmp.type = parse_netmask(ca->mask, &tmp); if (tmp.type != HM_HOST) { ca->netmask = MyMallocEx(sizeof(struct irc_netmask)); bcopy(&tmp, ca->netmask, sizeof(struct irc_netmask)); } 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); tmp.type = parse_netmask(ca->mask, &tmp); if (tmp.type != HM_HOST) { ca->netmask = MyMallocEx(sizeof(struct irc_netmask)); bcopy(&tmp, ca->netmask, sizeof(struct irc_netmask)); } 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_oper_from *from; 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, "from")) { for (cepp = cep->ce_entries; cepp; cepp = cepp->ce_next) { if (!strcmp(cepp->ce_varname, "userhost")) { from = MyMallocEx(sizeof(ConfigItem_oper_from)); ircstrdup(from->name, cepp->ce_vardata); AddListItem(from, vhost->from); } } } else if (!strcmp(cep->ce_varname, "swhois")) vhost->swhois = strdup(cep->ce_vardata); } 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_from = 0; char has_userhost = 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; if (has_from) { config_warn_duplicate(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, "vhost::from"); continue; } has_from = 1; for (cepp = cep->ce_entries; cepp; cepp = cepp->ce_next) { if (config_is_blankorempty(cepp, "vhost::from")) { errors++; continue; } if (!strcmp(cepp->ce_varname, "userhost")) has_userhost = 1; else { config_error_unknown(cepp->ce_fileptr->cf_filename, cepp->ce_varlinenum, "vhost::from", cepp->ce_varname); errors++; continue; } } } else if (!strcmp(cep->ce_varname, "swhois")) { if (has_swhois) { config_warn_duplicate(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, "vhost::swhois"); continue; } has_swhois = 1; } 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_from) { config_error_missing(ce->ce_fileptr->cf_filename, ce->ce_varlinenum, "vhost::from"); errors++; } if (!has_userhost) { config_error_missing(ce->ce_fileptr->cf_filename, ce->ce_varlinenum, "vhost::userhost"); errors++; } 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; for (cep = ce->ce_entries; cep; cep = cep->ce_next) { if (!strcmp(cep->ce_varname, "regex")) { 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; } } 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 = unreal_buildspamfilter(word); 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 *regex = NULL, *reason = NULL; char has_target = 0, has_regex = 0, has_action = 0, has_reason = 0, has_bantime = 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, "regex")) { char *errbuf; if (has_regex) { config_warn_duplicate(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, "spamfilter::regex"); continue; } has_regex = 1; if ((errbuf = unreal_checkregex(cep->ce_vardata,0,0))) { config_error("%s:%i: spamfilter::regex contains an invalid regex: %s", cep->ce_fileptr->cf_filename, cep->ce_varlinenum, errbuf); errors++; continue; } regex = 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 { config_error_unknown(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, "spamfilter", cep->ce_varname); errors++; continue; } } if (!has_regex) { config_error_missing(ce->ce_fileptr->cf_filename, ce->ce_varlinenum, "spamfilter::regex"); 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 (regex && reason && (strlen(regex) + strlen(reason) > 505)) { config_error("%s:%i: spamfilter block problem: regex + reason field are together over 505 bytes, " "please choose a shorter regex or reason", ce->ce_fileptr->cf_filename, ce->ce_varlinenum); errors++; } 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 = MyMalloc(sizeof(aMotdLine)); temp->line = strdup(cep->ce_varname); temp->next = NULL; if (!ca->text) 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; OperFlag *ofp = NULL; ca = MyMallocEx(sizeof(ConfigItem_log)); ca->logfd = -1; ircstrdup(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; } 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; ConfigEntry *cepp; ConfigItem_link *link = NULL; OperFlag *ofp; link = (ConfigItem_link *) MyMallocEx(sizeof(ConfigItem_link)); link->servername = strdup(ce->ce_vardata); /* ugly, but it works. if it fails, we know _test_link failed miserably */ for (cep = ce->ce_entries; cep; cep = cep->ce_next) { if (!strcmp(cep->ce_varname, "username")) link->username = strdup(cep->ce_vardata); else if (!strcmp(cep->ce_varname, "hostname")) link->hostname = strdup(cep->ce_vardata); else if (!strcmp(cep->ce_varname, "bind-ip")) link->bindip = strdup(cep->ce_vardata); else if (!strcmp(cep->ce_varname, "port")) link->port = atol(cep->ce_vardata); else if (!strcmp(cep->ce_varname, "password-receive")) link->recvauth = Auth_ConvertConf2AuthStruct(cep); else if (!strcmp(cep->ce_varname, "password-connect")) link->connpwd = strdup(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, "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; } } else if (!strcmp(cep->ce_varname, "hub")) link->hubmask = strdup(cep->ce_vardata); else if (!strcmp(cep->ce_varname, "leaf")) link->leafmask = strdup(cep->ce_vardata); else if (!strcmp(cep->ce_varname, "leafdepth")) link->leafdepth = atol(cep->ce_vardata); #ifdef USE_SSL else if (!strcmp(cep->ce_varname, "ciphers")) link->ciphers = strdup(cep->ce_vardata); #endif } AddListItem(link, conf_link); return 0; } int _test_link(ConfigFile *conf, ConfigEntry *ce) { ConfigEntry *cep, *cepp; OperFlag *ofp; int errors = 0; char has_username = 0, has_hostname = 0, has_bindip = 0, has_port = 0; char has_passwordreceive = 0, has_passwordconnect = 0, has_class = 0; char has_hub = 0, has_leaf = 0, has_leafdepth = 0, has_ciphers = 0; char has_options = 0; char has_autoconnect = 0; char has_hostname_wildcards = 0; if (!ce->ce_vardata) { config_error("%s:%i: link without servername", ce->ce_fileptr->cf_filename, ce->ce_varlinenum); return 1; } if (!strchr(ce->ce_vardata, '.')) { config_error("%s:%i: link: bogus server name", 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, "link"); errors++; continue; } if (!strcmp(cep->ce_varname, "options")) { if (has_options) { config_warn_duplicate(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, "link::options"); continue; } has_options = 1; 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, "link::options"); errors++; continue; } if (!stricmp(cepp->ce_varname, "zip")) { config_warn("%s:%i: link %s with deprecated zip option, compression is included with SSL linking", cep->ce_fileptr->cf_filename, cep->ce_varlinenum, ce->ce_vardata); continue; } if (!(ofp = config_binary_flags_search(_LinkFlags, cepp->ce_varname, ARRAY_SIZEOF(_LinkFlags)))) { config_error_unknownopt(cepp->ce_fileptr->cf_filename, cepp->ce_varlinenum, "link", cepp->ce_varname); errors++; continue; } #ifndef USE_SSL if (ofp->flag == CONNECT_SSL) { config_error("%s:%i: link %s with SSL option enabled on a non-SSL compile", cep->ce_fileptr->cf_filename, cep->ce_varlinenum, ce->ce_vardata); errors++; } #endif if (ofp->flag == CONNECT_AUTO) { has_autoconnect = 1; } } continue; } if (!cep->ce_vardata) { config_error_empty(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, "link", cep->ce_varname); errors++; continue; } if (!strcmp(cep->ce_varname, "username")) { if (has_username) { config_warn_duplicate(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, "link::username"); continue; } has_username = 1; } else if (!strcmp(cep->ce_varname, "hostname")) { if (has_hostname) { config_warn_duplicate(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, "link::hostname"); continue; } has_hostname = 1; #ifdef INET6 /* I'm nice... I'll help those poor ipv6 users. -- Syzop */ /* [ not null && len>6 && has not a : in it && last character is a digit ] */ if (cep->ce_vardata && (strlen(cep->ce_vardata) > 6) && !strchr(cep->ce_vardata, ':') && isdigit(cep->ce_vardata[strlen(cep->ce_vardata)-1])) { char crap[32]; if (inet_pton(AF_INET, cep->ce_vardata, crap) != 0) { char ipv6buf[48]; snprintf(ipv6buf, sizeof(ipv6buf), "::ffff:%s", cep->ce_vardata); MyFree(cep->ce_vardata); cep->ce_vardata = strdup(ipv6buf); } else { /* Insert IPv6 validation here */ config_error( "%s:%i: listen: '%s' looks like " "it might be IPv4, but is not a valid address.", ce->ce_fileptr->cf_filename, ce->ce_varlinenum, cep->ce_vardata); errors++; } } #endif if (strchr(cep->ce_vardata, '*') != NULL || strchr(cep->ce_vardata, '?')) { has_hostname_wildcards = 1; } } else if (!strcmp(cep->ce_varname, "bind-ip")) { if (has_bindip) { config_warn_duplicate(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, "link::bind-ip"); continue; } has_bindip = 1; #ifdef INET6 /* I'm nice... I'll help those poor ipv6 users. -- Syzop */ /* [ not null && len>6 && has not a : in it && last character is a digit ] */ if (cep->ce_vardata && (strlen(cep->ce_vardata) > 6) && !strchr(cep->ce_vardata, ':') && isdigit(cep->ce_vardata[strlen(cep->ce_vardata)-1])) { char crap[32]; if (inet_pton(AF_INET, cep->ce_vardata, crap) != 0) { char ipv6buf[48]; snprintf(ipv6buf, sizeof(ipv6buf), "::ffff:%s", cep->ce_vardata); MyFree(cep->ce_vardata); cep->ce_vardata = strdup(ipv6buf); } else { /* Insert IPv6 validation here */ config_error( "%s:%i: bind-ip: '%s' looks like " "it might be IPv4, but is not a valid address.", ce->ce_fileptr->cf_filename, ce->ce_varlinenum, cep->ce_vardata); errors++; } } #endif } else if (!strcmp(cep->ce_varname, "port")) { if (has_port) { config_warn_duplicate(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, "link::port"); continue; } has_port = 1; } else if (!strcmp(cep->ce_varname, "password-receive")) { if (has_passwordreceive) { config_warn_duplicate(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, "link::password-receive"); continue; } has_passwordreceive = 1; if (Auth_CheckError(cep) < 0) errors++; } else if (!strcmp(cep->ce_varname, "password-connect")) { if (has_passwordconnect) { config_warn_duplicate(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, "link::password-connect"); continue; } has_passwordconnect = 1; if (cep->ce_entries) { config_error("%s:%i: link::password-connect cannot be encrypted", ce->ce_fileptr->cf_filename, ce->ce_varlinenum); errors++; } if (strlen(cep->ce_vardata) > PASSWDLEN) { config_error("%s:%i: link::password-connect cannot exceed %d characters in length", ce->ce_fileptr->cf_filename, ce->ce_varlinenum, PASSWDLEN); errors++; } } else if (!strcmp(cep->ce_varname, "class")) { if (has_class) { config_warn_duplicate(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, "link::class"); continue; } has_class = 1; } else if (!strcmp(cep->ce_varname, "hub")) { if (has_hub) { config_warn_duplicate(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, "link::hub"); continue; } has_hub = 1; } else if (!strcmp(cep->ce_varname, "leaf")) { if (has_leaf) { config_warn_duplicate(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, "link::leaf"); continue; } has_leaf = 1; } else if (!strcmp(cep->ce_varname, "leafdepth")) { if (has_leafdepth) { config_warn_duplicate(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, "link::leafdepth"); continue; } has_leafdepth = 1; } else if (!strcmp(cep->ce_varname, "ciphers")) { if (has_ciphers) { config_warn_duplicate(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, "link::ciphers"); continue; } has_ciphers = 1; } else { config_error_unknown(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, "link", cep->ce_varname); errors++; } } if (!has_username) { config_error_missing(ce->ce_fileptr->cf_filename, ce->ce_varlinenum, "link::username"); errors++; } if (!has_hostname) { config_error_missing(ce->ce_fileptr->cf_filename, ce->ce_varlinenum, "link::hostname"); errors++; } if (!has_port) { config_error_missing(ce->ce_fileptr->cf_filename, ce->ce_varlinenum, "link::port"); errors++; } if (!has_passwordreceive) { config_error_missing(ce->ce_fileptr->cf_filename, ce->ce_varlinenum, "link::password-receive"); errors++; } if (!has_passwordconnect) { config_error_missing(ce->ce_fileptr->cf_filename, ce->ce_varlinenum, "link::password-connect"); errors++; } if (!has_class) { config_error_missing(ce->ce_fileptr->cf_filename, ce->ce_varlinenum, "link::class"); errors++; } if (has_autoconnect && has_hostname_wildcards) { config_error("%s:%i: link block with autoconnect and wildcards (* and/or ? in hostname)", ce->ce_fileptr->cf_filename, ce->ce_varlinenum); errors++; } if (errors > 0) return errors; for (cep = ce->ce_entries; cep; cep = cep->ce_next) { if (!strcmp(cep->ce_varname, "options")) { continue; } } return errors; } int _conf_cgiirc(ConfigFile *conf, ConfigEntry *ce) { ConfigEntry *cep; ConfigEntry *cepp; ConfigItem_cgiirc *cgiirc = NULL; cgiirc = (ConfigItem_cgiirc *) MyMallocEx(sizeof(ConfigItem_cgiirc)); for (cep = ce->ce_entries; cep; cep = cep->ce_next) { if (!strcmp(cep->ce_varname, "username")) cgiirc->username = strdup(cep->ce_vardata); else if (!strcmp(cep->ce_varname, "hostname")) cgiirc->hostname = strdup(cep->ce_vardata); else if (!strcmp(cep->ce_varname, "password")) cgiirc->auth = Auth_ConvertConf2AuthStruct(cep); else if (!strcmp(cep->ce_varname, "type")) { if (!strcmp(cep->ce_vardata, "webirc")) cgiirc->type = CGIIRC_WEBIRC; else if (!strcmp(cep->ce_vardata, "old")) cgiirc->type = CGIIRC_PASS; else abort(); } } AddListItem(cgiirc, conf_cgiirc); return 0; } int _test_cgiirc(ConfigFile *conf, ConfigEntry *ce) { ConfigEntry *cep, *cepp; OperFlag *ofp; int errors = 0; char has_username = 0; /* dup checking only, not mandatory */ char has_type = 0; /* mandatory */ char has_hostname = 0; /* mandatory */ char has_password = 0; /* mandatory */ CGIIRCType type; 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, "cgiirc"); errors++; continue; } if (!cep->ce_vardata) { config_error_empty(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, "cgiirc", cep->ce_varname); errors++; continue; } if (!strcmp(cep->ce_varname, "username")) { if (has_username) { config_warn_duplicate(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, "cgiirc::username"); continue; } has_username = 1; } else if (!strcmp(cep->ce_varname, "hostname")) { if (has_hostname) { config_warn_duplicate(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, "cgiirc::hostname"); continue; } has_hostname = 1; #ifdef INET6 /* I'm nice... I'll help those poor ipv6 users. -- Syzop */ /* [ not null && len>6 && has not a : in it && last character is a digit ] */ if (cep->ce_vardata && (strlen(cep->ce_vardata) > 6) && !strchr(cep->ce_vardata, ':') && isdigit(cep->ce_vardata[strlen(cep->ce_vardata)-1])) { char crap[32]; if (inet_pton(AF_INET, cep->ce_vardata, crap) != 0) { char ipv6buf[48]; snprintf(ipv6buf, sizeof(ipv6buf), "::ffff:%s", cep->ce_vardata); MyFree(cep->ce_vardata); cep->ce_vardata = strdup(ipv6buf); } else { /* Insert IPv6 validation here */ config_error( "%s:%i: cgiirc::hostname: '%s' looks like " "it might be IPv4, but is not a valid address.", ce->ce_fileptr->cf_filename, ce->ce_varlinenum, cep->ce_vardata); errors++; } } #endif } else if (!strcmp(cep->ce_varname, "password")) { if (has_password) { config_warn_duplicate(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, "cgiirc::password"); continue; } has_password = 1; if (Auth_CheckError(cep) < 0) errors++; } else if (!strcmp(cep->ce_varname, "type")) { if (has_type) { config_warn_duplicate(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, "cgiirc::type"); } has_type = 1; if (!strcmp(cep->ce_vardata, "webirc")) type = CGIIRC_WEBIRC; else if (!strcmp(cep->ce_vardata, "old")) type = CGIIRC_PASS; else { config_error("%s:%i: unknown cgiirc::type '%s', should be either 'webirc' or 'old'", cep->ce_fileptr->cf_filename, cep->ce_varlinenum, cep->ce_vardata); errors++; } } else { config_error_unknown(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, "cgiirc", cep->ce_varname); errors++; } } if (!has_hostname) { config_error_missing(ce->ce_fileptr->cf_filename, ce->ce_varlinenum, "cgiirc::hostname"); errors++; } if (!has_type) { config_error_missing(ce->ce_fileptr->cf_filename, ce->ce_varlinenum, "cgiirc::type"); errors++; } else { if (!has_password && (type == CGIIRC_WEBIRC)) { config_error_missing(ce->ce_fileptr->cf_filename, ce->ce_varlinenum, "cgiirc::password"); errors++; } else if (has_password && (type == CGIIRC_PASS)) { config_error("%s:%i: cgiirc block has type set to 'old' but has a password set. " "Passwords are not used with type 'old'. Either remove the password or " "use the 'webirc' method instead.", ce->ce_fileptr->cf_filename, ce->ce_varlinenum); 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); if (ca->flag.type == CONF_BAN_IP || ca->flag.type == CONF_BAN_USER) { struct irc_netmask tmp; tmp.type = parse_netmask(ca->mask, &tmp); if (tmp.type != HM_HOST) { ca->netmask = MyMallocEx(sizeof(struct irc_netmask)); bcopy(&tmp, ca->netmask, sizeof(struct irc_netmask)); } } } 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; OperFlag *ofl = NULL; Hook *h; for (cep = ce->ce_entries; cep; cep = cep->ce_next) { if (!strcmp(cep->ce_varname, "kline-address")) { ircstrdup(tempiConf.kline_address, cep->ce_vardata); } if (!strcmp(cep->ce_varname, "gline-address")) { ircstrdup(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")) { ircstrdup(tempiConf.oper_snomask, cep->ce_vardata); } else if (!strcmp(cep->ce_varname, "snomask-on-connect")) { ircstrdup(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")) { ircstrdup(tempiConf.static_quit, cep->ce_vardata); } else if (!strcmp(cep->ce_varname, "static-part")) { ircstrdup(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")) { ircstrdup(tempiConf.auto_join_chans, cep->ce_vardata); } else if (!strcmp(cep->ce_varname, "oper-auto-join")) { ircstrdup(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, "pingpong-warning")) { tempiConf.pingpong_warning = 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")) { ircstrdup(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")) { ircstrdup(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) { ircstrdup(tempiConf.oper_only_stats, cep->ce_vardata); } else { for (cepp = cep->ce_entries; cepp; cepp = cepp->ce_next) { OperStat *os = MyMallocEx(sizeof(OperStat)); ircstrdup(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; ircstrdup(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='-'; } ircstrdup(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")) { ircstrdup(tempiConf.network.x_defserv, cep->ce_vardata); } else if (!strcmp(cep->ce_varname, "services-server")) { ircstrdup(tempiConf.network.x_services_name, cep->ce_vardata); } else if (!strcmp(cep->ce_varname, "sasl-server")) { ircstrdup(tempiConf.network.x_sasl_server, cep->ce_vardata); } else if (!strcmp(cep->ce_varname, "stats-server")) { ircstrdup(tempiConf.network.x_stats_server, cep->ce_vardata); } else if (!strcmp(cep->ce_varname, "help-channel")) { ircstrdup(tempiConf.network.x_helpchan, cep->ce_vardata); } else if (!strcmp(cep->ce_varname, "hiddenhost-prefix")) { ircstrdup(tempiConf.network.x_hidden_host, cep->ce_vardata); } else if (!strcmp(cep->ce_varname, "prefix-quit")) { if (*cep->ce_vardata == '0') { ircstrdup(tempiConf.network.x_prefix_quit, ""); } else ircstrdup(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")) { ircstrdup(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, "timeout")) { tempiConf.host_timeout = config_checkval(cepp->ce_vardata,CFG_TIME); } else if (!strcmp(cepp->ce_varname, "retries")) { tempiConf.host_retries = config_checkval(cepp->ce_vardata,CFG_TIME); } else if (!strcmp(cepp->ce_varname, "nameserver")) { ircstrdup(tempiConf.name_server, cepp->ce_vardata); } else if (!strcmp(cepp->ce_varname, "bind-ip")) { ircstrdup(tempiConf.dns_bindip, cepp->ce_vardata); } } } else if (!strcmp(cep->ce_varname, "throttle")) { for (cepp = cep->ce_entries; cepp; cepp = cepp->ce_next) { if (!strcmp(cepp->ce_varname, "period")) tempiConf.throttle_period = config_checkval(cepp->ce_vardata,CFG_TIME); else if (!strcmp(cepp->ce_varname, "connections")) tempiConf.throttle_count = atoi(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(cep->ce_varname, "options")) { for (cepp = cep->ce_entries; cepp; cepp = cepp->ce_next) { if (!strcmp(cepp->ce_varname, "webtv-support")) { tempiConf.webtv_support = 1; } else 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(cep->ce_varname, "hosts")) { for (cepp = cep->ce_entries; cepp; cepp = cepp->ce_next) { if (!strcmp(cepp->ce_varname, "local")) { ircstrdup(tempiConf.network.x_locop_host, cepp->ce_vardata); } else if (!strcmp(cepp->ce_varname, "global")) { ircstrdup(tempiConf.network.x_oper_host, cepp->ce_vardata); } else if (!strcmp(cepp->ce_varname, "coadmin")) { ircstrdup(tempiConf.network.x_coadmin_host, cepp->ce_vardata); } else if (!strcmp(cepp->ce_varname, "admin")) { ircstrdup(tempiConf.network.x_admin_host, cepp->ce_vardata); } else if (!strcmp(cepp->ce_varname, "servicesadmin")) { ircstrdup(tempiConf.network.x_sadmin_host, cepp->ce_vardata); } else if (!strcmp(cepp->ce_varname, "netadmin")) { ircstrdup(tempiConf.network.x_netadmin_host, cepp->ce_vardata); } else if (!strcmp(cepp->ce_varname, "host-on-oper-up")) { tempiConf.network.x_inah = config_checkval(cepp->ce_vardata,CFG_YESNO); } } } 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")) ircstrdup(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")) ircstrdup(tempiConf.spamfilter_ban_reason, cepp->ce_vardata); else if (!strcmp(cepp->ce_varname, "virus-help-channel")) ircstrdup(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; ircstrdup(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")) { #ifdef USE_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")) { ircstrdup(tempiConf.x_server_cipher_list, cepp->ce_vardata); } else if (!strcmp(cepp->ce_varname, "dh")) { ircstrdup(tempiConf.x_dh_pem, cepp->ce_vardata); } else if (!strcmp(cepp->ce_varname, "certificate")) { ircstrdup(tempiConf.x_server_cert_pem, cepp->ce_vardata); } else if (!strcmp(cepp->ce_varname, "key")) { ircstrdup(tempiConf.x_server_key_pem, cepp->ce_vardata); } else if (!strcmp(cepp->ce_varname, "trusted-ca-file")) { ircstrdup(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; } } #endif /* USE_SSL */ } else if (!strcmp(cep->ce_varname, "default-ipv6-clone-mask")) { #ifdef INET6 tempiConf.default_ipv6_clone_mask = atoi(cep->ce_vardata); #endif /* INET6 */ } 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")) { CheckNull(cep); CheckDuplicate(cep, pingpong_warning, "pingpong-warning"); } 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")) { struct in_addr in; CheckDuplicate(cepp, dns_bind_ip, "link::bind-ip"); if (strcmp(cepp->ce_vardata, "*")) { in.s_addr = inet_addr(cepp->ce_vardata); if (strcmp((char *)inet_ntoa(in), 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, "timeout")) { CheckDuplicate(cepp, dns_timeout, "dns::timeout"); } else if (!strcmp(cepp->ce_varname, "retries")) { CheckDuplicate(cepp, dns_retries, "dns::retries"); } else if (!strcmp(cepp->ce_varname, "nameserver")) { struct in_addr in; CheckDuplicate(cepp, dns_nameserver, "dns::nameserver"); in.s_addr = inet_addr(cepp->ce_vardata); if (strcmp((char *)inet_ntoa(in), cepp->ce_vardata)) { config_error("%s:%i: set::dns::nameserver (%s) is not a valid IP", cepp->ce_fileptr->cf_filename, cepp->ce_varlinenum, cepp->ce_vardata); errors++; continue; } } else if (!strcmp(cepp->ce_varname, "bind-ip")) { struct in_addr in; CheckDuplicate(cepp, dns_bind_ip, "dns::bind-ip"); if (strcmp(cepp->ce_vardata, "*")) { in.s_addr = inet_addr(cepp->ce_vardata); if (strcmp((char *)inet_ntoa(in), 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")) { for (cepp = cep->ce_entries; cepp; cepp = cepp->ce_next) { CheckNull(cepp); if (!strcmp(cepp->ce_varname, "period")) { int x = config_checkval(cepp->ce_vardata,CFG_TIME); CheckDuplicate(cepp, throttle_period, "throttle::period"); if (x > 86400*7) { config_error("%s:%i: insane set::throttle::period value", cepp->ce_fileptr->cf_filename, cepp->ce_varlinenum); errors++; continue; } } else if (!strcmp(cepp->ce_varname, "connections")) { int x = atoi(cepp->ce_vardata); CheckDuplicate(cepp, throttle_connections, "throttle::connections"); if ((x < 1) || (x > 127)) { config_error("%s:%i: set::throttle::connections out of range, should be 1-127", cepp->ce_fileptr->cf_filename, cepp->ce_varlinenum); errors++; continue; } } else { config_error_unknownopt(cepp->ce_fileptr->cf_filename, cepp->ce_varlinenum, "set::throttle", cepp->ce_varname); errors++; 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 { 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, "webtv-support")) { CheckDuplicate(cepp, options_webtv_support, "options::webtv-support"); } else 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 { 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")) { for (cepp = cep->ce_entries; cepp; cepp = cepp->ce_next) { char *c, *host; if (!cepp->ce_vardata) { config_error_empty(cepp->ce_fileptr->cf_filename, cepp->ce_varlinenum, "set::hosts", cepp->ce_varname); errors++; continue; } if (!strcmp(cepp->ce_varname, "local")) { CheckDuplicate(cepp, hosts_local, "hosts::local"); } else if (!strcmp(cepp->ce_varname, "global")) { CheckDuplicate(cepp, hosts_global, "hosts::global"); } else if (!strcmp(cepp->ce_varname, "coadmin")) { CheckDuplicate(cepp, hosts_coadmin, "hosts::coadmin"); } else if (!strcmp(cepp->ce_varname, "admin")) { CheckDuplicate(cepp, hosts_admin, "hosts::admin"); } else if (!strcmp(cepp->ce_varname, "servicesadmin")) { CheckDuplicate(cepp, hosts_servicesadmin, "hosts::servicesadmin"); } else if (!strcmp(cepp->ce_varname, "netadmin")) { CheckDuplicate(cepp, hosts_netadmin, "hosts::netadmin"); } else if (!strcmp(cepp->ce_varname, "host-on-oper-up")) { CheckDuplicate(cepp, hosts_host_on_oper_up, "hosts::host-on-oper-up"); } else { config_error_unknown(cepp->ce_fileptr->cf_filename, cepp->ce_varlinenum, "set::hosts", cepp->ce_varname); errors++; continue; } if ((c = strchr(cepp->ce_vardata, '@'))) { char *tmp; if (!(*(c+1)) || (c-cepp->ce_vardata) > USERLEN || c == cepp->ce_vardata) { config_error("%s:%i: illegal value for set::hosts::%s", cepp->ce_fileptr->cf_filename, cepp->ce_varlinenum, cepp->ce_varname); errors++; continue; } for (tmp = cepp->ce_vardata; tmp != c; tmp++) { if (*tmp == '~' && tmp == cepp->ce_vardata) continue; if (!isallowed(*tmp)) break; } if (tmp != c) { config_error("%s:%i: illegal value for set::hosts::%s", cepp->ce_fileptr->cf_filename, cepp->ce_varlinenum, cepp->ce_varname); errors++; continue; } host = c+1; } else host = cepp->ce_vardata; if (strlen(host) > HOSTLEN) { config_error("%s:%i: illegal value for set::hosts::%s", cepp->ce_fileptr->cf_filename, cepp->ce_varlinenum, cepp->ce_varname); errors++; continue; } for (; *host; host++) { if (!isallowed(*host) && *host != ':') { config_error("%s:%i: illegal value for set::hosts::%s", cepp->ce_fileptr->cf_filename, cepp->ce_varlinenum, cepp->ce_varname); errors++; continue; } } } } 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")) { #ifdef USE_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++; } } #endif /* USE_SSL */ } 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.conf\";", ce->ce_fileptr->cf_filename, ce->ce_varlinenum); return -1; } 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; 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); } else { } } } } 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)); ircstrdup(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)); ircstrdup(format->format, cep->ce_vardata); regcomp(&format->expr, cep->ce_vardata, REG_ICASE|REG_EXTENDED); 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")) { ircstrdup(format->nick, cepp->ce_vardata); } else if (!strcmp(cepp->ce_varname, "parameters")) { ircstrdup(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")) { ircstrdup(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) { ircstrdup(alias->nick, alias->alias); } add_CommandX(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")) { int errorcode, errorbufsize; char *errorbuf; regex_t expr; char has_type = 0, has_target = 0, has_parameters = 0; has_format = 1; errorcode = regcomp(&expr, cep->ce_vardata, REG_ICASE|REG_EXTENDED); if (errorcode > 0) { errorbufsize = regerror(errorcode, &expr, NULL, 0)+1; errorbuf = MyMalloc(errorbufsize); regerror(errorcode, &expr, errorbuf, errorbufsize); config_error("%s:%i: alias::format contains an invalid regex: %s", cep->ce_fileptr->cf_filename, cep->ce_varlinenum, errorbuf); errors++; free(errorbuf); } regfree(&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")) { ircstrdup(deny->filename, cep->ce_vardata); } else if (!strcmp(cep->ce_varname, "reason")) { ircstrdup(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) ircstrdup(deny->reason, "Possible infected virus file"); else ircstrdup(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")) { ircstrdup(deny->channel, cep->ce_vardata); } else if (!strcmp(cep->ce_varname, "redirect")) { ircstrdup(deny->redirect, cep->ce_vardata); } else if (!strcmp(cep->ce_varname, "reason")) { ircstrdup(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")) { ircstrdup(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")) { ircstrdup(deny->mask, cep->ce_vardata); } else if (!strcmp(cep->ce_varname, "rule")) { deny->rule = (char *)crule_parse(cep->ce_vardata); ircstrdup(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")) { ircstrdup(deny->mask, cep->ce_vardata); } else if (!strcmp(cep->ce_varname, "version")) { ircstrdup(deny->version, cep->ce_vardata); } else if (!strcmp(cep->ce_varname, "flags")) { ircstrdup(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("tmp", file_basename); 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(); loop.ircd_rehashing = 0; return 1; } void link_cleanup(ConfigItem_link *link_ptr) { ircfree(link_ptr->servername); ircfree(link_ptr->username); ircfree(link_ptr->bindip); ircfree(link_ptr->hostname); ircfree(link_ptr->hubmask); ircfree(link_ptr->leafmask); ircfree(link_ptr->connpwd); #ifdef USE_SSL ircfree(link_ptr->ciphers); #endif Auth_DeleteAuthStruct(link_ptr->recvauth); link_ptr->recvauth = NULL; } 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_cgiircblock(ConfigItem_cgiirc *e) { Debug((DEBUG_ERROR, "delete_cgiircblock: deleting %s", e->hostname)); if (e->auth) Auth_DeleteAuthStruct(e->auth); ircfree(e->hostname); ircfree(e->username); DelListItem(e, conf_cgiirc); MyFree(e); } 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)); ircfree(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) { ircfree(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 *file = find_remote_include(ce->ce_vardata, &errorbuf); int ret; if (!loop.ircd_rehashing || (loop.ircd_rehashing && !file && !errorbuf)) { char *error; if (config_verbose > 0) config_status("Downloading %s", ce->ce_vardata); file = download_file(ce->ce_vardata, &error); if (!file) { #ifdef REMOTEINC_SPECIALCACHE if (has_cached_version(ce->ce_vardata)) { config_warn("%s:%i: include: error downloading '%s': %s -- using cached version instead.", ce->ce_fileptr->cf_filename, ce->ce_varlinenum, ce->ce_vardata, error); file = strdup(unreal_mkcache(ce->ce_vardata)); /* 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, ce->ce_vardata, error); return -1; #ifdef REMOTEINC_SPECIALCACHE } #endif } add_remote_include(file, ce->ce_vardata, 0, NULL, ce->ce_fileptr->cf_filename, ce->ce_varlinenum); ret = load_conf(file, ce->ce_vardata); free(file); return ret; } else { if (errorbuf) { #ifdef REMOTEINC_SPECIALCACHE if (has_cached_version(ce->ce_vardata)) { config_warn("%s:%i: include: error downloading '%s': %s -- using cached version instead.", ce->ce_fileptr->cf_filename, ce->ce_varlinenum, ce->ce_vardata, errorbuf); /* Let it pass to load_conf()... */ file = strdup(unreal_mkcache(ce->ce_vardata)); } else { #endif config_error("%s:%i: include: error downloading '%s': %s", ce->ce_fileptr->cf_filename, ce->ce_varlinenum, ce->ce_vardata, errorbuf); return -1; #ifdef REMOTEINC_SPECIALCACHE } #endif } if (config_verbose > 0) config_status("Loading %s from download", ce->ce_vardata); add_remote_include(file, ce->ce_vardata, 0, NULL, ce->ce_fileptr->cf_filename, ce->ce_varlinenum); ret = load_conf(file, ce->ce_vardata); 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; #ifdef USE_SSL if (ctx_server && ctx_client) return 0; /* everything is functional */ #endif 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); }