unrealircd- supernets unrealircd source & configuration |
git clone git://git.acid.vegas/unrealircd.git |
Log | Files | Refs | Archive | README | LICENSE |
conf.c (295200B)
1 /* 2 * Unreal Internet Relay Chat Daemon, src/conf.c 3 * (C) 1998-2000 Chris Behrens & Fred Jacobs (comstud, moogle) 4 * (C) 2000-2002 Carsten V. Munk and the UnrealIRCd Team 5 * 6 * This program is free software; you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License as published by 8 * the Free Software Foundation; either version 1, or (at your option) 9 * any later version. 10 * 11 * This program is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU General Public License for more details. 15 * 16 * You should have received a copy of the GNU General Public License 17 * along with this program; if not, write to the Free Software 18 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 19 */ 20 21 #include "unrealircd.h" 22 23 /* 24 * Some typedefs.. 25 */ 26 typedef struct ConfigCommand ConfigCommand; 27 struct ConfigCommand 28 { 29 char *name; 30 int (*conffunc)(ConfigFile *conf, ConfigEntry *ce); 31 int (*testfunc)(ConfigFile *conf, ConfigEntry *ce); 32 }; 33 34 35 /* Config commands */ 36 37 static int _conf_admin (ConfigFile *conf, ConfigEntry *ce); 38 static int _conf_me (ConfigFile *conf, ConfigEntry *ce); 39 static int _conf_files (ConfigFile *conf, ConfigEntry *ce); 40 static int _conf_oper (ConfigFile *conf, ConfigEntry *ce); 41 static int _conf_operclass (ConfigFile *conf, ConfigEntry *ce); 42 static int _conf_class (ConfigFile *conf, ConfigEntry *ce); 43 static int _conf_drpass (ConfigFile *conf, ConfigEntry *ce); 44 static int _conf_ulines (ConfigFile *conf, ConfigEntry *ce); 45 static int _conf_include (ConfigFile *conf, ConfigEntry *ce); 46 static int _conf_tld (ConfigFile *conf, ConfigEntry *ce); 47 static int _conf_listen (ConfigFile *conf, ConfigEntry *ce); 48 static int _conf_allow (ConfigFile *conf, ConfigEntry *ce); 49 static int _conf_except (ConfigFile *conf, ConfigEntry *ce); 50 static int _conf_vhost (ConfigFile *conf, ConfigEntry *ce); 51 static int _conf_link (ConfigFile *conf, ConfigEntry *ce); 52 static int _conf_ban (ConfigFile *conf, ConfigEntry *ce); 53 static int _conf_set (ConfigFile *conf, ConfigEntry *ce); 54 static int _conf_deny (ConfigFile *conf, ConfigEntry *ce); 55 static int _conf_deny_channel (ConfigFile *conf, ConfigEntry *ce); 56 static int _conf_deny_version (ConfigFile *conf, ConfigEntry *ce); 57 static int _conf_require (ConfigFile *conf, ConfigEntry *ce); 58 static int _conf_allow_channel (ConfigFile *conf, ConfigEntry *ce); 59 static int _conf_loadmodule (ConfigFile *conf, ConfigEntry *ce); 60 static int _conf_alias (ConfigFile *conf, ConfigEntry *ce); 61 static int _conf_help (ConfigFile *conf, ConfigEntry *ce); 62 static int _conf_offchans (ConfigFile *conf, ConfigEntry *ce); 63 static int _conf_sni (ConfigFile *conf, ConfigEntry *ce); 64 extern int _conf_security_group (ConfigFile *conf, ConfigEntry *ce); 65 static int _conf_secret (ConfigFile *conf, ConfigEntry *ce); 66 67 /* 68 * Validation commands 69 */ 70 71 static int _test_admin (ConfigFile *conf, ConfigEntry *ce); 72 static int _test_me (ConfigFile *conf, ConfigEntry *ce); 73 static int _test_files (ConfigFile *conf, ConfigEntry *ce); 74 static int _test_oper (ConfigFile *conf, ConfigEntry *ce); 75 static int _test_operclass (ConfigFile *conf, ConfigEntry *ce); 76 static int _test_class (ConfigFile *conf, ConfigEntry *ce); 77 static int _test_drpass (ConfigFile *conf, ConfigEntry *ce); 78 static int _test_ulines (ConfigFile *conf, ConfigEntry *ce); 79 static int _test_include (ConfigFile *conf, ConfigEntry *ce); 80 static int _test_tld (ConfigFile *conf, ConfigEntry *ce); 81 static int _test_listen (ConfigFile *conf, ConfigEntry *ce); 82 static int _test_allow (ConfigFile *conf, ConfigEntry *ce); 83 static int _test_except (ConfigFile *conf, ConfigEntry *ce); 84 static int _test_vhost (ConfigFile *conf, ConfigEntry *ce); 85 static int _test_link (ConfigFile *conf, ConfigEntry *ce); 86 static int _test_ban (ConfigFile *conf, ConfigEntry *ce); 87 static int _test_require (ConfigFile *conf, ConfigEntry *ce); 88 static int _test_set (ConfigFile *conf, ConfigEntry *ce); 89 static int _test_deny (ConfigFile *conf, ConfigEntry *ce); 90 static int _test_allow_channel (ConfigFile *conf, ConfigEntry *ce); 91 static int _test_loadmodule (ConfigFile *conf, ConfigEntry *ce); 92 static int _test_blacklist_module (ConfigFile *conf, ConfigEntry *ce); 93 static int _test_alias (ConfigFile *conf, ConfigEntry *ce); 94 static int _test_help (ConfigFile *conf, ConfigEntry *ce); 95 static int _test_offchans (ConfigFile *conf, ConfigEntry *ce); 96 static int _test_sni (ConfigFile *conf, ConfigEntry *ce); 97 extern int _test_security_group (ConfigFile *conf, ConfigEntry *ce); 98 static int _test_secret (ConfigFile *conf, ConfigEntry *ce); 99 100 /* This MUST be alphabetized */ 101 static ConfigCommand _ConfigCommands[] = { 102 { "admin", _conf_admin, _test_admin }, 103 { "alias", _conf_alias, _test_alias }, 104 { "allow", _conf_allow, _test_allow }, 105 { "ban", _conf_ban, _test_ban }, 106 { "blacklist-module", NULL, NULL }, 107 { "class", _conf_class, _test_class }, 108 { "deny", _conf_deny, _test_deny }, 109 { "drpass", _conf_drpass, _test_drpass }, 110 { "except", _conf_except, _test_except }, 111 { "files", _conf_files, _test_files }, 112 { "help", _conf_help, _test_help }, 113 { "include", NULL, _test_include }, 114 { "link", _conf_link, _test_link }, 115 { "listen", _conf_listen, _test_listen }, 116 { "loadmodule", NULL, _test_loadmodule}, 117 { "log", config_run_log, config_test_log }, 118 { "me", _conf_me, _test_me }, 119 { "official-channels", _conf_offchans, _test_offchans }, 120 { "oper", _conf_oper, _test_oper }, 121 { "operclass", _conf_operclass, _test_operclass }, 122 { "require", _conf_require, _test_require }, 123 { "secret", _conf_secret, _test_secret }, 124 { "security-group", _conf_security_group, _test_security_group }, 125 { "set", _conf_set, _test_set }, 126 { "sni", _conf_sni, _test_sni }, 127 { "tld", _conf_tld, _test_tld }, 128 { "ulines", _conf_ulines, _test_ulines }, 129 { "vhost", _conf_vhost, _test_vhost }, 130 }; 131 132 /* This MUST be alphabetized */ 133 static NameValue _ListenerFlags[] = { 134 { LISTENER_CLIENTSONLY, "clientsonly"}, 135 { LISTENER_DEFER_ACCEPT, "defer-accept"}, 136 { LISTENER_SERVERSONLY, "serversonly"}, 137 { LISTENER_TLS, "ssl"}, 138 { LISTENER_NORMAL, "standard"}, 139 { LISTENER_TLS, "tls"}, 140 }; 141 142 /* This MUST be alphabetized */ 143 static NameValue _LinkFlags[] = { 144 { CONNECT_AUTO, "autoconnect" }, 145 { CONNECT_INSECURE, "insecure" }, 146 { CONNECT_QUARANTINE, "quarantine"}, 147 { CONNECT_TLS, "ssl" }, 148 { CONNECT_TLS, "tls" }, 149 }; 150 151 /* This MUST be alphabetized */ 152 static NameValue _TLSFlags[] = { 153 { TLSFLAG_FAILIFNOCERT, "fail-if-no-clientcert" }, 154 { TLSFLAG_DISABLECLIENTCERT, "no-client-certificate" }, 155 { TLSFLAG_NOSTARTTLS, "no-starttls" }, 156 }; 157 158 struct { 159 unsigned conf_me : 1; 160 unsigned conf_admin : 1; 161 unsigned conf_listen : 1; 162 } requiredstuff; 163 struct { 164 int min, max; 165 } nicklengths; 166 struct SetCheck settings; 167 /* 168 * Utilities 169 */ 170 171 void port_range(const char *string, int *start, int *end); 172 long config_checkval(const char *value, unsigned short flags); 173 174 /* 175 * Parser 176 */ 177 178 ConfigFile *config_load(const char *filename, const char *displayname); 179 void config_free(ConfigFile *cfptr); 180 ConfigFile *config_parse_with_offset(const char *filename, char *confdata, unsigned int line_offset); 181 ConfigFile *config_parse(const char *filename, char *confdata); 182 ConfigEntry *config_find_entry(ConfigEntry *ce, const char *name); 183 184 extern void add_entropy_configfile(struct stat *st, const char *buf); 185 extern void unload_all_unused_umodes(void); 186 extern void unload_all_unused_extcmodes(void); 187 extern void unload_all_unused_extbans(void); 188 extern void unload_all_unused_caps(void); 189 extern void unload_all_unused_history_backends(void); 190 extern void unload_all_unused_rpc_handlers(void); 191 192 int reloadable_perm_module_unloaded(void); 193 int tls_tests(void); 194 195 /* Conf sub-sub-functions */ 196 void test_tlsblock(ConfigFile *conf, ConfigEntry *cep, int *totalerrors); 197 void conf_tlsblock(ConfigFile *conf, ConfigEntry *cep, TLSOptions *tlsoptions); 198 void free_tls_options(TLSOptions *tlsoptions); 199 200 /* 201 * Config parser (IRCd) 202 */ 203 int config_read_file(const char *filename, const char *display_name); 204 void config_rehash(void); 205 int config_run_blocks(void); 206 int config_test_blocks(); 207 208 /* 209 * Configuration linked lists 210 */ 211 ConfigItem_me *conf_me = NULL; 212 ConfigItem_files *conf_files = NULL; 213 ConfigItem_class *conf_class = NULL; 214 ConfigItem_class *default_class = NULL; 215 ConfigItem_admin *conf_admin = NULL; 216 ConfigItem_admin *conf_admin_tail = NULL; 217 ConfigItem_drpass *conf_drpass = NULL; 218 ConfigItem_ulines *conf_ulines = NULL; 219 ConfigItem_tld *conf_tld = NULL; 220 ConfigItem_oper *conf_oper = NULL; 221 ConfigItem_operclass *conf_operclass = NULL; 222 ConfigItem_listen *conf_listen = NULL; 223 ConfigItem_sni *conf_sni = NULL; 224 ConfigItem_allow *conf_allow = NULL; 225 ConfigItem_vhost *conf_vhost = NULL; 226 ConfigItem_link *conf_link = NULL; 227 ConfigItem_ban *conf_ban = NULL; 228 ConfigItem_deny_channel *conf_deny_channel = NULL; 229 ConfigItem_allow_channel *conf_allow_channel = NULL; 230 ConfigItem_deny_version *conf_deny_version = NULL; 231 ConfigItem_alias *conf_alias = NULL; 232 ConfigResource *config_resources = NULL; 233 ConfigItem_blacklist_module *conf_blacklist_module = NULL; 234 ConfigItem_help *conf_help = NULL; 235 ConfigItem_offchans *conf_offchans = NULL; 236 Secret *secrets = NULL; 237 238 MODVAR Configuration iConf; 239 MODVAR Configuration tempiConf; 240 MODVAR ConfigFile *conf = NULL; 241 extern NameValueList *config_defines; 242 MODVAR int ipv6_disabled = 0; 243 MODVAR Client *remote_rehash_client = NULL; 244 MODVAR json_t *json_rehash_log = NULL; 245 246 MODVAR int config_error_flag = 0; 247 int config_verbose = 0; 248 249 int need_operclass_permissions_upgrade = 0; 250 int invalid_snomasks_encountered = 0; 251 int have_tls_listeners = 0; 252 char *port_6667_ip = NULL; 253 254 int add_config_resource(const char *resource, int type, ConfigEntry *ce); 255 void resource_download_complete(const char *url, const char *file, const char *errorbuf, int cached, void *rs_key); 256 void free_all_config_resources(void); 257 int rehash_internal(Client *client); 258 int is_blacklisted_module(const char *name); 259 260 /** Return the printable string of a 'cep' location, such as set::something::xyz */ 261 const char *config_var(ConfigEntry *cep) 262 { 263 static char buf[256]; 264 ConfigEntry *e; 265 char *elem[16]; 266 int numel = 0, i; 267 268 if (!cep) 269 return ""; 270 271 buf[0] = '\0'; 272 273 /* First, walk back to the top */ 274 for (e = cep; e; e = e->parent) 275 { 276 elem[numel++] = e->name; 277 if (numel == 15) 278 break; 279 } 280 281 /* Now construct the xxx::yyy::zzz string */ 282 for (i = numel-1; i >= 0; i--) 283 { 284 strlcat(buf, elem[i], sizeof(buf)); 285 if (i > 0) 286 strlcat(buf, "::", sizeof(buf)); 287 } 288 289 return buf; 290 } 291 292 void port_range(const char *string, int *start, int *end) 293 { 294 char buf[256]; 295 char *c; 296 strlcpy(buf, string, sizeof(buf)); 297 c = strchr(buf, '-'); 298 if (!c) 299 { 300 int tmp = atoi(string); 301 *start = tmp; 302 *end = tmp; 303 return; 304 } 305 *c = '\0'; 306 *start = atoi(string); 307 *end = atoi((c+1)); 308 *c = '-'; 309 } 310 311 /** Parses '5:60s' config values. 312 * orig: original string 313 * times: pointer to int, first value (before the :) 314 * period: pointer to int, second value (after the :) in seconds 315 * RETURNS: 0 for parse error, 1 if ok. 316 * REMARK: times&period should be ints! 317 */ 318 int config_parse_flood(const char *orig, int *times, int *period) 319 { 320 char buf[256]; 321 char *x; 322 323 strlcpy(buf, orig, sizeof(buf)); 324 325 *times = *period = 0; 326 x = strchr(buf, ':'); 327 /* 'blah', ':blah', '1:' */ 328 if (!x || (x == buf) || (*(x+1) == '\0')) 329 return 0; 330 331 *x = '\0'; 332 *times = atoi(buf); 333 *period = config_checkval(x+1, CFG_TIME); 334 *x = ':'; /* restore */ 335 return 1; 336 } 337 338 /** Find an anti-flood settings block by name. 339 * @param name The name of the set::anti-flood block 340 * @returns The FloodSettings block if found, or NULL if not found. 341 */ 342 FloodSettings *find_floodsettings_block_ex(Configuration *conf, const char *name) 343 { 344 FloodSettings *f; 345 346 for (f = conf->floodsettings; f; f = f->next) 347 if (!strcmp(f->name, name)) 348 return f; 349 350 return NULL; 351 } 352 353 /** Find an anti-flood settings block by name. 354 * @param name The name of the set::anti-flood block 355 * @returns The FloodSettings block if found, or NULL if not found. 356 */ 357 FloodSettings *find_floodsettings_block(const char *name) 358 { 359 return find_floodsettings_block_ex(&iConf, name); 360 } 361 362 /** Check if 'name' is in the array 'list'. 363 * @param name The name to check 364 * @param list The char *list[] with the list of valid names. 365 * @returns 1 if found, 0 if not 366 * @note The array in list must end in a NULL element! 367 */ 368 int text_in_array(const char *name, const char *list[]) 369 { 370 int i; 371 372 for (i=0; list[i]; i++) 373 if (!strcmp(name, list[i])) 374 return 1; 375 376 return 0; /* Not found */ 377 } 378 379 int flood_option_is_old(const char *name) 380 { 381 const char *opts[] = 382 { 383 "max-concurrent-conversations", 384 "unknown-flood-amount", 385 "unknown-flood-bantime", 386 "handshake-data-flood", 387 "away-count", 388 "away-period", 389 "away-flood", 390 "nick-flood", 391 "join-flood", 392 "invite-flood", 393 "knock-flood", 394 "connect-flood", 395 "target-flood", 396 NULL 397 }; 398 399 return text_in_array(name, opts); 400 } 401 402 int flood_option_is_for_everyone(const char *name) 403 { 404 const char *opts[] = 405 { 406 "connect-flood", 407 "handshake-data-flood", 408 "unknown-flood", 409 "target-flood", 410 NULL 411 }; 412 413 return text_in_array(name, opts); 414 } 415 416 /** Free a FloodSettings struct */ 417 void free_floodsettings(FloodSettings *f) 418 { 419 safe_free(f->name); 420 safe_free(f); 421 } 422 423 /** Parses a value like '5:60s' into a flood setting that we can store. 424 * @param str The string to parse (eg: '5:60s') 425 * @param settings The FloodSettings block to store the result in 426 * @param opt The option (eg: FLD_AWAY) 427 * @returns 1 if OK, 0 for parse error. 428 */ 429 int config_parse_flood_generic(const char *str, Configuration *conf, char *blockname, FloodOption opt) 430 { 431 char buf[64], *p; 432 FloodSettings *settings = find_floodsettings_block_ex(conf, blockname); 433 434 /* Create a new anti-flood block if it doesn't exist */ 435 if (!settings) 436 { 437 settings = safe_alloc(sizeof(FloodSettings)); 438 safe_strdup(settings->name, blockname); 439 AddListItem(settings, conf->floodsettings); 440 } 441 442 if (!strcmp(str, "unlimited") || !strcmp(str, "max")) 443 { 444 settings->limit[opt] = -1; 445 settings->period[opt] = 0; 446 return 1; 447 } 448 449 /* Work on a copy so we don't destroy 'str' */ 450 strlcpy(buf, str, sizeof(buf)); 451 452 p = strchr(buf, ':'); 453 454 /* 'blah', ':blah', '1:' */ 455 if (!p || (p == buf) || (*(p+1) == '\0')) 456 return 0; 457 458 *p++ = '\0'; 459 460 settings->limit[opt] = atoi(buf); 461 settings->period[opt] = config_checkval(p, CFG_TIME); 462 463 return 1; 464 } 465 466 long config_checkval(const char *orig, unsigned short flags) 467 { 468 char *value; 469 char *text; 470 long ret = 0; 471 472 /* Handle empty strings early, since we use +1 later in the code etc. */ 473 if (BadPtr(orig)) 474 return 0; 475 476 value = raw_strdup(orig); 477 if (flags == CFG_YESNO) { 478 for (text = value; *text; text++) { 479 if (!isalnum(*text)) 480 continue; 481 if (tolower(*text) == 'y' || (tolower(*text) == 'o' && 482 tolower(*(text+1)) == 'n') || *text == '1' || tolower(*text) == 't') { 483 ret = 1; 484 break; 485 } 486 } 487 } 488 else if (flags == CFG_SIZE) { 489 int mfactor = 1; 490 char *sz; 491 for (text = value; *text; text++) { 492 if (isalpha(*text)) { 493 if (tolower(*text) == 'k') 494 mfactor = 1024; 495 else if (tolower(*text) == 'm') 496 mfactor = 1048576; 497 else if (tolower(*text) == 'g') 498 mfactor = 1073741824; 499 else 500 mfactor = 1; 501 sz = text; 502 while (isalpha(*text)) 503 text++; 504 505 *sz = 0; 506 while (sz-- > value && *sz) { 507 if (isspace(*sz)) 508 *sz = 0; 509 if (!isdigit(*sz)) 510 break; 511 } 512 ret += atoi(sz+1)*mfactor; 513 if (*text == '\0') { 514 text++; 515 break; 516 } 517 } 518 } 519 mfactor = 1; 520 sz = text; 521 sz--; /* -1 because we are PAST the end of the string */ 522 while (sz-- > value) { 523 if (isspace(*sz)) 524 *sz = 0; 525 if (!isdigit(*sz)) 526 break; 527 } 528 ret += atoi(sz+1)*mfactor; 529 } 530 else if (flags == CFG_TIME) { 531 int mfactor = 1; 532 char *sz; 533 for (text = value; *text; text++) { 534 if (isalpha(*text)) { 535 if (tolower(*text) == 'w') 536 mfactor = 604800; 537 else if (tolower(*text) == 'd') 538 mfactor = 86400; 539 else if (tolower(*text) == 'h') 540 mfactor = 3600; 541 else if (tolower(*text) == 'm') 542 mfactor = 60; 543 else 544 mfactor = 1; 545 sz = text; 546 while (isalpha(*text)) 547 text++; 548 549 *sz = 0; 550 while (sz-- > value && *sz) { 551 if (isspace(*sz)) 552 *sz = 0; 553 if (!isdigit(*sz)) 554 break; 555 } 556 ret += atoi(sz+1)*mfactor; 557 if (*text == '\0') { 558 text++; 559 break; 560 } 561 } 562 } 563 mfactor = 1; 564 sz = text; 565 sz--; /* -1 because we are PAST the end of the string */ 566 while (sz-- > value) { 567 if (isspace(*sz)) 568 *sz = 0; 569 if (!isdigit(*sz)) 570 break; 571 } 572 ret += atoi(sz+1)*mfactor; 573 } 574 safe_free(value); 575 return ret; 576 } 577 578 /** Free configuration setting for set::modes-on-join */ 579 void free_conf_channelmodes(struct ChMode *store) 580 { 581 int i; 582 583 for (i=0; i < 255; i++) 584 safe_free(store->extparams[i]); 585 586 memset(store, 0, sizeof(struct ChMode)); 587 } 588 589 /* Set configuration, used for set::modes-on-join */ 590 void conf_channelmodes(const char *modes, struct ChMode *store) 591 { 592 Cmode *cm; 593 const char *m; 594 char *params = strchr(modes, ' '); 595 char *parambuf = NULL; 596 const char *param = NULL; 597 const char *param_in; 598 char *save = NULL; 599 int found; 600 601 /* Free existing parameters first (no inheritance) */ 602 free_conf_channelmodes(store); 603 604 if (params) 605 { 606 params++; 607 safe_strdup(parambuf, params); 608 param = strtoken(&save, parambuf, " "); 609 } 610 611 for (m = modes; *m && *m != ' '; m++) 612 { 613 if (*m == '+') 614 continue; 615 616 if (*m == '-') 617 { 618 /* When a channel is created it has no modes, so just ignore if the 619 * user asks us to unset anything -- codemastr 620 */ 621 while (*m && *m != '+') 622 m++; 623 continue; 624 } 625 626 found = 0; 627 for (cm=channelmodes; cm; cm = cm->next) 628 { 629 if (!(cm->letter)) 630 continue; 631 if (*m == cm->letter) 632 { 633 found = 1; 634 if (cm->paracount) 635 { 636 if (!param) 637 { 638 config_warn("set::modes-on-join '%s'. Parameter missing for mode %c.", modes, *m); 639 break; 640 } 641 param_in = param; /* save it */ 642 param = cm->conv_param(param, NULL, NULL); 643 if (!param) 644 { 645 config_warn("set::modes-on-join '%s'. Parameter for mode %c is invalid (%s).", modes, *m, param_in); 646 break; /* invalid parameter fmt, do not set mode. */ 647 } 648 safe_strdup(store->extparams[cm->letter], param); 649 /* Get next parameter */ 650 param = strtoken(&save, NULL, " "); 651 } 652 store->extmodes |= cm->mode; 653 break; 654 } 655 } 656 if (!found) 657 config_warn("set::modes-on-join '%s'. Channel mode %c not found.", modes, *m); 658 } 659 safe_free(parambuf); 660 } 661 662 void chmode_str(struct ChMode *modes, char *mbuf, char *pbuf, size_t mbuf_size, size_t pbuf_size) 663 { 664 Cmode *cm; 665 666 if (!(mbuf_size && pbuf_size)) 667 return; 668 669 *pbuf = 0; 670 *mbuf++ = '+'; 671 672 for (cm=channelmodes; cm; cm = cm->next) 673 { 674 if (!(cm->letter)) 675 continue; 676 677 if (modes->extmodes & cm->mode) 678 { 679 if (mbuf_size) 680 { 681 *mbuf++ = cm->letter; 682 if (!--mbuf_size) 683 { 684 *--mbuf=0; 685 break; 686 } 687 } 688 if (cm->paracount) 689 { 690 strlcat(pbuf, modes->extparams[cm->letter], pbuf_size); 691 strlcat(pbuf, " ", pbuf_size); 692 } 693 } 694 } 695 *mbuf=0; 696 } 697 698 const char *channellevel_to_string(const char *s) 699 { 700 /* Requested at http://bugs.unrealircd.org/view.php?id=3852 */ 701 if (!strcmp(s, "none")) 702 return ""; 703 if (!strcmp(s, "voice")) 704 return "v"; 705 if (!strcmp(s, "halfop")) 706 return "h"; 707 if (!strcmp(s, "op") || !strcmp(s, "chanop")) 708 return "o"; 709 if (!strcmp(s, "protect") || !strcmp(s, "chanprot") || !strcmp(s, "chanadmin") || !strcmp(s, "admin")) 710 return "a"; 711 if (!strcmp(s, "owner") || !strcmp(s, "chanowner")) 712 return "q"; 713 714 return NULL; /* unknown or unsupported */ 715 } 716 717 Policy policy_strtoval(const char *s) 718 { 719 if (!s) 720 return 0; 721 722 if (!strcmp(s, "allow")) 723 return POLICY_ALLOW; 724 725 if (!strcmp(s, "warn")) 726 return POLICY_WARN; 727 728 if (!strcmp(s, "deny")) 729 return POLICY_DENY; 730 731 return 0; 732 } 733 734 const char *policy_valtostr(Policy policy) 735 { 736 if (policy == POLICY_ALLOW) 737 return "allow"; 738 if (policy == POLICY_WARN) 739 return "warn"; 740 if (policy == POLICY_DENY) 741 return "deny"; 742 return "???"; 743 } 744 745 char policy_valtochar(Policy policy) 746 { 747 if (policy == POLICY_ALLOW) 748 return 'a'; 749 if (policy == POLICY_WARN) 750 return 'w'; 751 if (policy == POLICY_DENY) 752 return 'd'; 753 return '?'; 754 } 755 756 AllowedChannelChars allowed_channelchars_strtoval(const char *str) 757 { 758 if (!strcmp(str, "ascii")) 759 return ALLOWED_CHANNELCHARS_ASCII; 760 else if (!strcmp(str, "utf8")) 761 return ALLOWED_CHANNELCHARS_UTF8; 762 else if (!strcmp(str, "any")) 763 return ALLOWED_CHANNELCHARS_ANY; 764 return 0; 765 } 766 767 const char *allowed_channelchars_valtostr(AllowedChannelChars v) 768 { 769 switch(v) 770 { 771 case ALLOWED_CHANNELCHARS_ASCII: 772 return "ascii"; 773 case ALLOWED_CHANNELCHARS_UTF8: 774 return "utf8"; 775 case ALLOWED_CHANNELCHARS_ANY: 776 return "any"; 777 default: 778 /* Not possible */ 779 abort(); 780 return "NOTREACHED"; /* Windows.. */ 781 } 782 } 783 784 /* Used for set::automatic-ban-target and set::manual-ban-target */ 785 BanTarget ban_target_strtoval(const char *str) 786 { 787 if (!strcmp(str, "ip")) 788 return BAN_TARGET_IP; 789 else if (!strcmp(str, "userip")) 790 return BAN_TARGET_USERIP; 791 else if (!strcmp(str, "host")) 792 return BAN_TARGET_HOST; 793 else if (!strcmp(str, "userhost")) 794 return BAN_TARGET_USERHOST; 795 else if (!strcmp(str, "account")) 796 return BAN_TARGET_ACCOUNT; 797 else if (!strcmp(str, "certfp")) 798 return BAN_TARGET_CERTFP; 799 return 0; /* invalid */ 800 } 801 802 /* Used for set::automatic-ban-target and set::manual-ban-target */ 803 const char *ban_target_valtostr(BanTarget v) 804 { 805 switch(v) 806 { 807 case BAN_TARGET_IP: 808 return "ip"; 809 case BAN_TARGET_USERIP: 810 return "userip"; 811 case BAN_TARGET_HOST: 812 return "host"; 813 case BAN_TARGET_USERHOST: 814 return "userhost"; 815 case BAN_TARGET_ACCOUNT: 816 return "account"; 817 case BAN_TARGET_CERTFP: 818 return "certfp"; 819 default: 820 return "???"; 821 } 822 } 823 824 HideIdleTimePolicy hideidletime_strtoval(const char *str) 825 { 826 if (!strcmp(str, "never")) 827 return HIDE_IDLE_TIME_NEVER; 828 else if (!strcmp(str, "always")) 829 return HIDE_IDLE_TIME_ALWAYS; 830 else if (!strcmp(str, "usermode")) 831 return HIDE_IDLE_TIME_USERMODE; 832 else if (!strcmp(str, "oper-usermode")) 833 return HIDE_IDLE_TIME_OPER_USERMODE; 834 return 0; 835 } 836 837 const char *hideidletime_valtostr(HideIdleTimePolicy v) 838 { 839 switch(v) 840 { 841 case HIDE_IDLE_TIME_NEVER: 842 return "never"; 843 case HIDE_IDLE_TIME_ALWAYS: 844 return "always"; 845 case HIDE_IDLE_TIME_USERMODE: 846 return "usermode"; 847 case HIDE_IDLE_TIME_OPER_USERMODE: 848 return "oper-usermode"; 849 default: 850 return "INVALID"; 851 } 852 } 853 854 ConfigFile *config_load(const char *filename, const char *displayname) 855 { 856 struct stat sb; 857 int fd; 858 int ret; 859 char *buf = NULL; 860 ConfigFile *cfptr; 861 862 if (!displayname) 863 displayname = filename; 864 865 #ifndef _WIN32 866 fd = open(filename, O_RDONLY); 867 #else 868 fd = open(filename, O_RDONLY|O_BINARY); 869 #endif 870 if (fd == -1) 871 { 872 config_error("Couldn't open \"%s\": %s\n", filename, strerror(errno)); 873 return NULL; 874 } 875 if (fstat(fd, &sb) == -1) 876 { 877 config_error("Couldn't fstat \"%s\": %s\n", filename, strerror(errno)); 878 close(fd); 879 return NULL; 880 } 881 if (!sb.st_size) 882 { 883 /* Workaround for empty files */ 884 cfptr = config_parse(filename, " "); 885 close(fd); 886 return cfptr; 887 } 888 buf = safe_alloc(sb.st_size+1); 889 if (buf == NULL) 890 { 891 config_error("Out of memory trying to load \"%s\"\n", filename); 892 close(fd); 893 return NULL; 894 } 895 ret = read(fd, buf, sb.st_size); 896 if (ret != sb.st_size) 897 { 898 config_error("Error reading \"%s\": %s\n", filename, 899 ret == -1 ? strerror(errno) : strerror(EFAULT)); 900 safe_free(buf); 901 close(fd); 902 return NULL; 903 } 904 /* Just me or could this cause memory corrupted when ret <0 ? */ 905 buf[ret] = '\0'; 906 close(fd); 907 add_entropy_configfile(&sb, buf); 908 cfptr = config_parse(displayname, buf); 909 safe_free(buf); 910 return cfptr; 911 } 912 913 void config_free(ConfigFile *cfptr) 914 { 915 ConfigFile *nptr; 916 917 for(;cfptr;cfptr=nptr) 918 { 919 nptr = cfptr->next; 920 if (cfptr->items) 921 config_entry_free_all(cfptr->items); 922 safe_free(cfptr->filename); 923 safe_free(cfptr); 924 } 925 } 926 927 /** Remove quotes so that 'hello \"all\" \\ lala' becomes 'hello "all" \ lala' */ 928 void unreal_del_quotes(char *i) 929 { 930 char *o; 931 932 for (o = i; *i; i++) 933 { 934 if (*i == '\\') 935 { 936 if ((i[1] == '\\') || (i[1] == '"')) 937 { 938 i++; /* skip original \ */ 939 if (*i == '\0') 940 break; 941 } 942 } 943 *o++ = *i; 944 } 945 *o = '\0'; 946 } 947 948 /** Add quotes to a line, eg some"thing becomes some\"thing - extended version */ 949 int unreal_add_quotes_r(const char *i, char *o, size_t len) 950 { 951 if (len == 0) 952 return 0; 953 954 len--; /* reserve room for nul byte */ 955 956 if (len == 0) 957 { 958 *o = '\0'; 959 return 0; 960 } 961 962 for (; *i; i++) 963 { 964 if ((*i == '"') || (*i == '\\')) /* only " and \ need to be quoted */ 965 { 966 if (len < 2) 967 break; 968 *o++ = '\\'; 969 *o++ = *i; 970 len -= 2; 971 } else 972 { 973 if (len == 0) 974 break; 975 *o++ = *i; 976 len--; 977 } 978 } 979 *o = '\0'; 980 981 return 1; 982 } 983 984 /** Add quotes to a line, eg some"thing becomes some\"thing */ 985 const char *unreal_add_quotes(const char *str) 986 { 987 static char qbuf[2048]; 988 989 *qbuf = '\0'; 990 unreal_add_quotes_r(str, qbuf, sizeof(qbuf)); 991 return qbuf; 992 } 993 994 ConfigFile *config_parse(const char *filename, char *confdata) 995 { 996 return config_parse_with_offset(filename, confdata, 0); 997 } 998 999 /* This is the internal parser, made by Chris Behrens & Fred Jacobs <2005. 1000 * Enhanced (or mutilated) by Bram Matthys over the years (2015-2019). 1001 */ 1002 ConfigFile *config_parse_with_offset(const char *filename, char *confdata, unsigned int line_offset) 1003 { 1004 char *ptr; 1005 char *start; 1006 int linenumber = 1+line_offset; 1007 int errors = 0; 1008 int n; 1009 ConfigEntry *curce; 1010 ConfigEntry **lastce; 1011 ConfigEntry *cursection; 1012 ConfigFile *curcf; 1013 int preprocessor_level = 0; 1014 ConditionalConfig *cc, *cc_list = NULL; 1015 1016 curcf = safe_alloc(sizeof(ConfigFile)); 1017 safe_strdup(curcf->filename, filename); 1018 lastce = &(curcf->items); 1019 curce = NULL; 1020 cursection = NULL; 1021 /* Replace \r's with spaces .. ugly ugly -Stskeeps */ 1022 for (ptr=confdata; *ptr; ptr++) 1023 if (*ptr == '\r') 1024 *ptr = ' '; 1025 1026 for(ptr=confdata;*ptr;ptr++) 1027 { 1028 switch(*ptr) 1029 { 1030 case ';': 1031 if (!curce) 1032 { 1033 config_status("%s:%i Ignoring extra semicolon\n", 1034 filename, linenumber); 1035 break; 1036 } 1037 *lastce = curce; 1038 lastce = &(curce->next); 1039 curce->file_position_end = (ptr - confdata); 1040 curce = NULL; 1041 break; 1042 case '{': 1043 if (!curce) 1044 { 1045 config_error("%s:%i: New section start detected on line %d but the section has no name. " 1046 "Sections should start with a name like 'oper {' or 'set {'.", 1047 filename, linenumber, linenumber); 1048 errors++; 1049 continue; 1050 } 1051 else if (curce->items) 1052 { 1053 config_error("%s:%i: New section start but previous section did not end properly. " 1054 "Check line %d and the line(s) before, you are likely missing a '};' there.\n", 1055 filename, linenumber, linenumber); 1056 errors++; 1057 continue; 1058 } 1059 curce->section_linenumber = linenumber; 1060 lastce = &(curce->items); 1061 cursection = curce; 1062 curce = NULL; 1063 break; 1064 case '}': 1065 if (curce) 1066 { 1067 config_error("%s:%i: Missing semicolon (';') before close brace. Check line %d and the line(s) before.\n", 1068 filename, linenumber, linenumber); 1069 config_entry_free_all(curce); 1070 config_free(curcf); 1071 errors++; 1072 return NULL; 1073 } 1074 else if (!cursection) 1075 { 1076 config_error("%s:%i: You have a close brace ('};') too many. " 1077 "Check line %d AND the lines above it from the previous block.\n", 1078 filename, linenumber, linenumber); 1079 errors++; 1080 continue; 1081 } 1082 curce = cursection; 1083 cursection->file_position_end = (ptr - confdata); 1084 cursection = cursection->parent; 1085 if (!cursection) 1086 lastce = &(curcf->items); 1087 else 1088 lastce = &(cursection->items); 1089 for(;*lastce;lastce = &((*lastce)->next)) 1090 continue; 1091 if (*(ptr+1) != ';') 1092 { 1093 /* Simulate closing ; so you can get away with } instead of ugly }; */ 1094 *lastce = curce; 1095 lastce = &(curce->next); 1096 curce->file_position_end = (ptr - confdata); 1097 curce = NULL; 1098 } 1099 break; 1100 case '#': 1101 ptr++; 1102 while(*ptr && (*ptr != '\n')) 1103 ptr++; 1104 if (!*ptr) 1105 break; 1106 ptr--; 1107 continue; 1108 case '/': 1109 if (*(ptr+1) == '/') 1110 { 1111 ptr += 2; 1112 while(*ptr && (*ptr != '\n')) 1113 ptr++; 1114 if (!*ptr) 1115 break; 1116 ptr--; /* grab the \n on next loop thru */ 1117 continue; 1118 } 1119 else if (*(ptr+1) == '*') 1120 { 1121 int commentstart = linenumber; 1122 1123 for(ptr+=2;*ptr;ptr++) 1124 { 1125 if (*ptr == '\n') 1126 { 1127 linenumber++; 1128 } else 1129 if ((*ptr == '*') && (*(ptr+1) == '/')) 1130 { 1131 ptr++; 1132 break; 1133 } 1134 } 1135 if (!*ptr) 1136 { 1137 config_error("%s:%i Comment on line %d does not end\n", 1138 filename, commentstart, commentstart); 1139 errors++; 1140 config_entry_free_all(curce); 1141 config_free(curcf); 1142 return NULL; 1143 } 1144 } 1145 break; 1146 case '\'': 1147 if (curce) 1148 curce->escaped = 1; 1149 /* fallthrough */ 1150 case '\"': 1151 if (curce && curce->line_number != linenumber && cursection) 1152 { 1153 config_error("%s:%i: Missing semicolon (';') at end of line. " 1154 "Line %d must end with a ; character\n", 1155 filename, curce->line_number, curce->line_number); 1156 errors++; 1157 1158 *lastce = curce; 1159 lastce = &(curce->next); 1160 curce->file_position_end = (ptr - confdata); 1161 curce = NULL; 1162 } 1163 1164 start = ++ptr; 1165 for(;*ptr;ptr++) 1166 { 1167 if (*ptr == '\\') 1168 { 1169 if (strchr("\\\"'", ptr[1])) 1170 { 1171 /* \\ or \" in config file (escaped) */ 1172 ptr++; /* skip */ 1173 continue; 1174 } 1175 } 1176 else if (*ptr == '\n') 1177 break; 1178 else if (curce && curce->escaped && (*ptr == '\'')) 1179 break; 1180 else if ((!curce || !curce->escaped) && (*ptr == '"')) 1181 break; 1182 } 1183 if (!*ptr || (*ptr == '\n')) 1184 { 1185 config_error("%s:%i: Unterminated quote found\n", 1186 filename, linenumber); 1187 errors++; 1188 config_entry_free_all(curce); 1189 config_free(curcf); 1190 return NULL; 1191 } 1192 if (curce) 1193 { 1194 if (curce->value) 1195 { 1196 config_error("%s:%i: Extra data detected. Perhaps missing a ';' or one too many?\n", 1197 filename, linenumber); 1198 errors++; 1199 } 1200 else 1201 { 1202 safe_strldup(curce->value, start, ptr-start+1); 1203 preprocessor_replace_defines(&curce->value, curce); 1204 unreal_del_quotes(curce->value); 1205 } 1206 } 1207 else 1208 { 1209 curce = safe_alloc(sizeof(ConfigEntry)); 1210 curce->line_number = linenumber; 1211 curce->file = curcf; 1212 curce->parent = cursection; 1213 curce->file_position_start = (start - confdata); 1214 safe_strldup(curce->name, start, ptr-start+1); 1215 preprocessor_replace_defines(&curce->name, curce); 1216 unreal_del_quotes(curce->name); 1217 preprocessor_cc_duplicate_list(cc_list, &curce->conditional_config); 1218 } 1219 break; 1220 case '\n': 1221 linenumber++; 1222 /* fall through */ 1223 case '\t': 1224 case ' ': 1225 case '=': 1226 case '\r': 1227 break; 1228 case '@': 1229 /* Preprocessor item, such as @if, @define, etc. */ 1230 start = ptr; 1231 for (;*ptr; ptr++) 1232 { 1233 if (*ptr == '\n') 1234 break; 1235 } 1236 cc = NULL; 1237 n = parse_preprocessor_item(start, ptr, filename, linenumber, &cc); 1238 linenumber++; 1239 if (n == PREPROCESSOR_IF) 1240 { 1241 preprocessor_level++; 1242 cc->priority = preprocessor_level; 1243 AddListItem(cc, cc_list); 1244 } else 1245 if (n == PREPROCESSOR_ENDIF) 1246 { 1247 if (preprocessor_level == 0) 1248 { 1249 config_error("%s:%i: @endif unexpected. There was no preciding unclosed @if.", 1250 filename, linenumber); 1251 errors++; 1252 } 1253 preprocessor_cc_free_level(&cc_list, preprocessor_level); 1254 preprocessor_level--; 1255 } else 1256 if (n == PREPROCESSOR_ERROR) 1257 { 1258 errors++; 1259 goto breakout; 1260 } 1261 1262 if (!*ptr) 1263 goto breakout; /* special case, since we don't want the for loop to ptr++ */ 1264 1265 break; 1266 default: 1267 if ((*ptr == '*') && (*(ptr+1) == '/')) 1268 { 1269 config_status("%s:%i: Ignoring extra end comment\n", 1270 filename, linenumber); 1271 config_status("WARNING: Starting with UnrealIRCd 4.2.1 a /*-style comment stops as soon as the first */ is encountered. " 1272 "See https://www.unrealircd.org/docs/FAQ#Nesting_comments for more information."); 1273 ptr++; 1274 break; 1275 } 1276 start = ptr; 1277 for(;*ptr;ptr++) 1278 { 1279 if ((*ptr == ' ') || (*ptr == '=') || (*ptr == '\t') || (*ptr == '\n') || (*ptr == ';')) 1280 break; 1281 } 1282 if (!*ptr) 1283 { 1284 if (curce) 1285 config_error("%s: End of file reached but directive or block at line %i did not end properly. " 1286 "Perhaps a missing ; (semicolon) somewhere?\n", 1287 filename, curce->line_number); 1288 else if (cursection) 1289 config_error("%s: End of file reached but the section which starts at line %i did never end properly. " 1290 "Perhaps a missing }; ?\n", 1291 filename, cursection->section_linenumber); 1292 else 1293 config_error("%s: Unexpected end of file. Some line or block did not end properly. " 1294 "Look for any missing } and };\n", filename); 1295 errors++; 1296 config_entry_free_all(curce); 1297 config_free(curcf); 1298 return NULL; 1299 } 1300 if (curce) 1301 { 1302 if (curce->value) 1303 { 1304 config_error("%s:%i: Extra data detected. Check for a missing ; character at or around line %d\n", 1305 filename, linenumber, linenumber-1); 1306 errors++; 1307 } 1308 else 1309 { 1310 safe_strldup(curce->value, start, ptr-start+1); 1311 preprocessor_replace_defines(&curce->value, curce); 1312 } 1313 } 1314 else 1315 { 1316 curce = safe_alloc(sizeof(ConfigEntry)); 1317 memset(curce, 0, sizeof(ConfigEntry)); 1318 curce->line_number = linenumber; 1319 curce->file = curcf; 1320 curce->parent = cursection; 1321 curce->file_position_start = (start - confdata); 1322 safe_strldup(curce->name, start, ptr-start+1); 1323 preprocessor_replace_defines(&curce->name, curce); 1324 if (curce->conditional_config) 1325 abort(); 1326 preprocessor_cc_duplicate_list(cc_list, &curce->conditional_config); 1327 } 1328 if ((*ptr == ';') || (*ptr == '\n')) 1329 ptr--; 1330 break; 1331 } /* switch */ 1332 if (!*ptr) /* This IS possible. -- Syzop */ 1333 break; 1334 } /* for */ 1335 breakout: 1336 if (curce) 1337 { 1338 config_error("%s: End of file reached but directive or block at line %i did not end properly. " 1339 "Perhaps a missing ; (semicolon) somewhere?\n", 1340 filename, curce->line_number); 1341 errors++; 1342 config_entry_free_all(curce); 1343 } 1344 else if (cursection) 1345 { 1346 config_error("%s: End of file reached but the section which starts at line %i did never end properly. " 1347 "Perhaps a missing }; ?\n", 1348 filename, cursection->section_linenumber); 1349 errors++; 1350 } 1351 1352 if (errors) 1353 { 1354 config_free(curcf); 1355 return NULL; 1356 } 1357 return curcf; 1358 } 1359 1360 /** Free a ConfigEntry struct, all it's children, and all it's next entries. 1361 * Consider calling config_entry_free() instead of this one.. or at least 1362 * check which one of the two you actually need ;) 1363 */ 1364 void config_entry_free_all(ConfigEntry *ce) 1365 { 1366 ConfigEntry *nptr; 1367 1368 for(;ce;ce=nptr) 1369 { 1370 nptr = ce->next; 1371 if (ce->items) 1372 config_entry_free_all(ce->items); 1373 safe_free(ce->name); 1374 safe_free(ce->value); 1375 if (ce->conditional_config) 1376 preprocessor_cc_free_list(ce->conditional_config); 1377 safe_free(ce); 1378 } 1379 } 1380 1381 /** Free a specific ConfigEntry struct (and it's children). 1382 * Caller must ensure that the entry is not in the linked list anymore. 1383 */ 1384 void config_entry_free(ConfigEntry *ce) 1385 { 1386 if (ce->items) 1387 config_entry_free_all(ce->items); 1388 safe_free(ce->name); 1389 safe_free(ce->value); 1390 if (ce->conditional_config) 1391 preprocessor_cc_free_list(ce->conditional_config); 1392 safe_free(ce); 1393 } 1394 1395 ConfigEntry *config_find_entry(ConfigEntry *ce, const char *name) 1396 { 1397 ConfigEntry *cep; 1398 1399 for (cep = ce; cep; cep = cep->next) 1400 if (cep->name && !strcmp(cep->name, name)) 1401 break; 1402 return cep; 1403 } 1404 1405 void config_error(FORMAT_STRING(const char *format), ...) 1406 { 1407 va_list ap; 1408 char buffer[1024]; 1409 char *ptr; 1410 1411 va_start(ap, format); 1412 vsnprintf(buffer, sizeof(buffer), format, ap); 1413 va_end(ap); 1414 if ((ptr = strchr(buffer, '\n')) != NULL) 1415 *ptr = '\0'; 1416 unreal_log_raw(ULOG_ERROR, "config", "CONFIG_ERROR_GENERIC", NULL, buffer); 1417 /* We cannot live with this */ 1418 config_error_flag = 1; 1419 } 1420 1421 void config_error_missing(const char *filename, int line, const char *entry) 1422 { 1423 config_error("%s:%d: %s is missing", filename, line, entry); 1424 } 1425 1426 void config_error_unknown(const char *filename, int line, const char *block, 1427 const char *entry) 1428 { 1429 config_error("%s:%d: Unknown directive '%s::%s'", filename, line, block, entry); 1430 } 1431 1432 void config_error_unknownflag(const char *filename, int line, const char *block, 1433 const char *entry) 1434 { 1435 config_error("%s:%d: Unknown %s flag '%s'", filename, line, block, entry); 1436 } 1437 1438 void config_error_unknownopt(const char *filename, int line, const char *block, 1439 const char *entry) 1440 { 1441 config_error("%s:%d: Unknown %s option '%s'", filename, line, block, entry); 1442 } 1443 1444 void config_error_noname(const char *filename, int line, const char *block) 1445 { 1446 config_error("%s:%d: %s block has no name", filename, line, block); 1447 } 1448 1449 void config_error_blank(const char *filename, int line, const char *block) 1450 { 1451 config_error("%s:%d: Blank %s entry", filename, line, block); 1452 } 1453 1454 void config_error_empty(const char *filename, int line, const char *block, 1455 const char *entry) 1456 { 1457 config_error("%s:%d: %s::%s specified without a value", 1458 filename, line, block, entry); 1459 } 1460 1461 void config_status(FORMAT_STRING(const char *format), ...) 1462 { 1463 va_list ap; 1464 char buffer[1024]; 1465 char *ptr; 1466 1467 va_start(ap, format); 1468 vsnprintf(buffer, 1023, format, ap); 1469 va_end(ap); 1470 if ((ptr = strchr(buffer, '\n')) != NULL) 1471 *ptr = '\0'; 1472 unreal_log_raw(ULOG_INFO, "config", "CONFIG_INFO_GENERIC", NULL, buffer); 1473 } 1474 1475 void config_warn(FORMAT_STRING(const char *format), ...) 1476 { 1477 va_list ap; 1478 char buffer[1024]; 1479 char *ptr; 1480 1481 va_start(ap, format); 1482 vsnprintf(buffer, 1023, format, ap); 1483 va_end(ap); 1484 if ((ptr = strchr(buffer, '\n')) != NULL) 1485 *ptr = '\0'; 1486 unreal_log_raw(ULOG_WARNING, "config", "CONFIG_WARNING_GENERIC", NULL, buffer); 1487 } 1488 1489 void config_warn_duplicate(const char *filename, int line, const char *entry) 1490 { 1491 config_warn("%s:%d: Duplicate %s directive", filename, line, entry); 1492 } 1493 1494 /* returns 1 if the test fails */ 1495 int config_test_openfile(ConfigEntry *cep, int flags, mode_t mode, const char *entry, int fatal, int allow_url) 1496 { 1497 int fd; 1498 1499 if (!cep->value) 1500 { 1501 if (fatal) 1502 config_error("%s:%i: %s: <no file specified>: no file specified", 1503 cep->file->filename, 1504 cep->line_number, 1505 entry); 1506 else 1507 1508 config_warn("%s:%i: %s: <no file specified>: no file specified", 1509 cep->file->filename, 1510 cep->line_number, 1511 entry); 1512 return 1; 1513 } 1514 1515 /* There's not much checking that can be done for asynchronously downloaded files */ 1516 if (url_is_valid(cep->value)) 1517 { 1518 if (allow_url) 1519 return 0; 1520 1521 /* but we can check if a URL is used wrongly :-) */ 1522 config_warn("%s:%i: %s: %s: URL used where not allowed", 1523 cep->file->filename, 1524 cep->line_number, 1525 entry, cep->value); 1526 if (fatal) 1527 return 1; 1528 else 1529 return 0; 1530 } 1531 1532 /* 1533 * Make sure that files are created with the correct mode. This is 1534 * because we don't feel like unlink()ing them...which would require 1535 * stat()ing them to make sure that we don't delete existing ones 1536 * and that we deal with all of the bugs that come with complexity. 1537 * The only files we may be creating are the tunefile and pidfile so far. 1538 */ 1539 if (flags & O_CREAT) 1540 fd = open(cep->value, flags, mode); 1541 else 1542 fd = open(cep->value, flags); 1543 if (fd == -1) 1544 { 1545 if (fatal) 1546 config_error("%s:%i: %s: %s: %s", 1547 cep->file->filename, 1548 cep->line_number, 1549 entry, 1550 cep->value, 1551 strerror(errno)); 1552 else 1553 config_warn("%s:%i: %s: %s: %s", 1554 cep->file->filename, 1555 cep->line_number, 1556 entry, 1557 cep->value, 1558 strerror(errno)); 1559 return 1; 1560 } 1561 close(fd); 1562 return 0; 1563 } 1564 1565 int config_is_blankorempty(ConfigEntry *cep, const char *block) 1566 { 1567 if (!cep->value) 1568 { 1569 config_error_empty(cep->file->filename, cep->line_number, block, 1570 cep->name); 1571 return 1; 1572 } 1573 return 0; 1574 } 1575 1576 ConfigCommand *config_binary_search(const char *cmd) { 1577 int start = 0; 1578 int stop = ARRAY_SIZEOF(_ConfigCommands)-1; 1579 int mid; 1580 while (start <= stop) { 1581 mid = (start+stop)/2; 1582 if (smycmp(cmd,_ConfigCommands[mid].name) < 0) { 1583 stop = mid-1; 1584 } 1585 else if (strcmp(cmd,_ConfigCommands[mid].name) == 0) { 1586 return &_ConfigCommands[mid]; 1587 } 1588 else 1589 start = mid+1; 1590 } 1591 return NULL; 1592 } 1593 1594 void free_iConf(Configuration *i) 1595 { 1596 FloodSettings *f, *f_next; 1597 1598 safe_free(i->link_bindip); 1599 safe_free(i->kline_address); 1600 safe_free(i->gline_address); 1601 safe_free(i->oper_snomask); 1602 safe_free(i->auto_join_chans); 1603 safe_free(i->oper_auto_join_chans); 1604 safe_free(i->allow_user_stats); 1605 // allow_user_stats_ext is freed elsewhere 1606 safe_free(i->static_quit); 1607 safe_free(i->static_part); 1608 free_tls_options(i->tls_options); 1609 i->tls_options = NULL; 1610 safe_free(i->tls_options); 1611 safe_free_multiline(i->plaintext_policy_user_message); 1612 safe_free_multiline(i->plaintext_policy_oper_message); 1613 safe_free(i->outdated_tls_policy_user_message); 1614 safe_free(i->outdated_tls_policy_oper_message); 1615 safe_free(i->restrict_usermodes); 1616 safe_free(i->restrict_channelmodes); 1617 safe_free(i->restrict_extendedbans); 1618 safe_free(i->channel_command_prefix); 1619 safe_free(i->level_on_join); 1620 safe_free(i->spamfilter_ban_reason); 1621 safe_free(i->spamfilter_virus_help_channel); 1622 // spamexcept is freed elsewhere 1623 safe_free(i->spamexcept_line); 1624 safe_free(i->reject_message_too_many_connections); 1625 safe_free(i->reject_message_server_full); 1626 safe_free(i->reject_message_unauthorized); 1627 safe_free(i->reject_message_kline); 1628 safe_free(i->reject_message_gline); 1629 safe_free(i->network_name); 1630 safe_free(i->network_name_005); 1631 safe_free(i->default_server); 1632 safe_free(i->services_name); 1633 safe_free(i->cloak_prefix); 1634 safe_free(i->prefix_quit); 1635 safe_free(i->helpchan); 1636 safe_free(i->stats_server); 1637 safe_free(i->sasl_server); 1638 // anti-flood: 1639 for (f = i->floodsettings; f; f = f_next) 1640 { 1641 f_next = f->next; 1642 free_floodsettings(f); 1643 } 1644 i->floodsettings = NULL; 1645 } 1646 1647 void config_setdefaultsettings(Configuration *i) 1648 { 1649 char tmp[512]; 1650 1651 safe_strdup(i->oper_snomask, OPER_SNOMASKS); 1652 i->server_notice_colors = 1; 1653 i->server_notice_show_event = 1; 1654 i->ident_read_timeout = 7; 1655 i->ident_connect_timeout = 3; 1656 i->ban_version_tkl_time = 86400; /* 1d */ 1657 i->spamfilter_ban_time = 86400; /* 1d */ 1658 safe_strdup(i->spamfilter_ban_reason, "Spam/advertising"); 1659 safe_strdup(i->spamfilter_virus_help_channel, "#help"); 1660 i->spamfilter_detectslow_warn = 250; 1661 i->spamfilter_detectslow_fatal = 500; 1662 i->spamfilter_stop_on_first_match = 1; 1663 i->maxchannelsperuser = 10; 1664 i->maxdccallow = 10; 1665 safe_strdup(i->channel_command_prefix, "`!."); 1666 i->conn_modes = set_usermode("+ixw"); 1667 i->check_target_nick_bans = 1; 1668 i->maxbans = 60; 1669 i->maxbanlength = 2048; 1670 safe_strdup(i->level_on_join, "o"); 1671 i->watch_away_notification = 1; 1672 i->uhnames = 1; 1673 i->ping_cookie = 1; 1674 i->ping_warning = 15; /* default ping warning notices 15 seconds */ 1675 i->default_ipv6_clone_mask = 64; 1676 nicklengths.min = i->min_nick_length = 0; /* 0 means no minimum required */ 1677 nicklengths.max = i->nick_length = NICKLEN; 1678 i->topic_length = 360; 1679 i->away_length = 307; 1680 i->kick_length = 307; 1681 i->quit_length = 307; 1682 safe_strdup(i->link_bindip, "*"); 1683 safe_strdup(i->cloak_prefix, "Clk"); 1684 if (!ipv6_capable()) 1685 DISABLE_IPV6 = 1; 1686 safe_strdup(i->prefix_quit, "Quit"); 1687 i->max_unknown_connections_per_ip = 3; 1688 i->handshake_timeout = 30; 1689 i->sasl_timeout = 15; 1690 i->handshake_delay = -1; 1691 i->broadcast_channel_messages = BROADCAST_CHANNEL_MESSAGES_AUTO; 1692 1693 /* Flood options */ 1694 /* - everyone */ 1695 i->throttle_count = 3; i->throttle_period = 60; /* throttle protection: max 3 per 60s */ 1696 i->handshake_data_flood_amount = 4096; 1697 i->handshake_data_flood_ban_action = BAN_ACT_ZLINE; 1698 i->handshake_data_flood_ban_time = 600; 1699 // (targetflood is in the targetflood module) 1700 /* - known-users */ 1701 config_parse_flood_generic("3:60", i, "known-users", FLD_NICK); /* NICK flood protection: max 3 per 60s */ 1702 config_parse_flood_generic("3:90", i, "known-users", FLD_JOIN); /* JOIN flood protection: max 3 per 90s */ 1703 config_parse_flood_generic("3:90", i, "known-users", FLD_VHOST); /* MODE -x flood protection: max 3 per 90s */ 1704 config_parse_flood_generic("4:120", i, "known-users", FLD_AWAY); /* AWAY flood protection: max 4 per 120s */ 1705 config_parse_flood_generic("4:60", i, "known-users", FLD_INVITE); /* INVITE flood protection: max 4 per 60s */ 1706 config_parse_flood_generic("4:120", i, "known-users", FLD_KNOCK); /* KNOCK protection: max 4 per 120s */ 1707 config_parse_flood_generic("10:15", i, "known-users", FLD_CONVERSATIONS); /* 10 users, new user every 15s */ 1708 config_parse_flood_generic("180:750", i, "known-users", FLD_LAG_PENALTY); /* 180 bytes / 750 msec */ 1709 /* - unknown-users */ 1710 config_parse_flood_generic("2:60", i, "unknown-users", FLD_NICK); /* NICK flood protection: max 2 per 60s */ 1711 config_parse_flood_generic("2:90", i, "unknown-users", FLD_JOIN); /* JOIN flood protection: max 2 per 90s */ 1712 config_parse_flood_generic("2:90", i, "unknown-users", FLD_VHOST); /* MODE -x flood protection: max 2 per 90s */ 1713 config_parse_flood_generic("4:120", i, "unknown-users", FLD_AWAY); /* AWAY flood protection: max 4 per 120s */ 1714 config_parse_flood_generic("2:60", i, "unknown-users", FLD_INVITE); /* INVITE flood protection: max 2 per 60s */ 1715 config_parse_flood_generic("2:120", i, "unknown-users", FLD_KNOCK); /* KNOCK protection: max 2 per 120s */ 1716 config_parse_flood_generic("4:15", i, "unknown-users", FLD_CONVERSATIONS); /* 4 users, new user every 15s */ 1717 config_parse_flood_generic("90:1000", i, "unknown-users", FLD_LAG_PENALTY); /* 90 bytes / 1000 msec */ 1718 1719 /* TLS options */ 1720 i->tls_options = safe_alloc(sizeof(TLSOptions)); 1721 snprintf(tmp, sizeof(tmp), "%s/tls/server.cert.pem", CONFDIR); 1722 safe_strdup(i->tls_options->certificate_file, tmp); 1723 snprintf(tmp, sizeof(tmp), "%s/tls/server.key.pem", CONFDIR); 1724 safe_strdup(i->tls_options->key_file, tmp); 1725 snprintf(tmp, sizeof(tmp), "%s/tls/curl-ca-bundle.crt", CONFDIR); 1726 safe_strdup(i->tls_options->trusted_ca_file, tmp); 1727 safe_strdup(i->tls_options->ciphers, UNREALIRCD_DEFAULT_CIPHERS); 1728 safe_strdup(i->tls_options->ciphersuites, UNREALIRCD_DEFAULT_CIPHERSUITES); 1729 i->tls_options->protocols = TLS_PROTOCOL_TLSV1_2|TLS_PROTOCOL_TLSV1_3; /* TLSv1.2 & TLSv1.3 */ 1730 #ifdef HAS_SSL_CTX_SET1_CURVES_LIST 1731 safe_strdup(i->tls_options->ecdh_curves, UNREALIRCD_DEFAULT_ECDH_CURVES); 1732 #endif 1733 safe_strdup(i->tls_options->outdated_protocols, "TLSv1,TLSv1.1"); 1734 /* the following may look strange but "AES*" matches all 1735 * AES ciphersuites that do not have Forward Secrecy. 1736 * Any decent client using AES will use ECDHE-xx-AES. 1737 */ 1738 safe_strdup(i->tls_options->outdated_ciphers, "AES*,RC4*,DES*"); 1739 1740 i->plaintext_policy_user = POLICY_ALLOW; 1741 i->plaintext_policy_oper = POLICY_DENY; 1742 i->plaintext_policy_server = POLICY_DENY; 1743 1744 i->outdated_tls_policy_user = POLICY_WARN; 1745 i->outdated_tls_policy_oper = POLICY_DENY; 1746 i->outdated_tls_policy_server = POLICY_DENY; 1747 1748 safe_strdup(i->reject_message_too_many_connections, "Too many connections from your IP"); 1749 safe_strdup(i->reject_message_server_full, "This server is full"); 1750 safe_strdup(i->reject_message_unauthorized, "You are not authorized to connect to this server"); 1751 safe_strdup(i->reject_message_kline, "You are not welcome on this server. $bantype: $banreason. Email $klineaddr for more information."); 1752 safe_strdup(i->reject_message_gline, "You are not welcome on this network. $bantype: $banreason. Email $glineaddr for more information."); 1753 1754 i->topic_setter = SETTER_NICK; 1755 i->ban_setter = SETTER_NICK; 1756 i->ban_setter_sync = 1; 1757 1758 i->allowed_channelchars = ALLOWED_CHANNELCHARS_UTF8; 1759 1760 i->automatic_ban_target = BAN_TARGET_IP; 1761 i->manual_ban_target = BAN_TARGET_HOST; 1762 1763 i->hide_idle_time = HIDE_IDLE_TIME_OPER_USERMODE; 1764 1765 i->who_limit = 100; 1766 1767 i->named_extended_bans = 1; 1768 } 1769 1770 /** Similar to config_setdefaultsettings but this one is applied *AFTER* 1771 * the entire configuration has been ran (sometimes this is the only way it can be done..). 1772 * NOTE: iConf is thus already populated with (non-default) values. Only overwrite if necessary! 1773 */ 1774 void postconf_defaults(void) 1775 { 1776 TKL *tk; 1777 char *encoded; 1778 1779 if (!iConf.modes_on_join_set) 1780 { 1781 /* We could not do this in config_setdefaultsettings() 1782 * because the channel mode modules were not initialized yet. 1783 */ 1784 conf_channelmodes("+nt", &iConf.modes_on_join); 1785 } 1786 if (!iConf.plaintext_policy_user_message) 1787 { 1788 /* The message depends on whether it's reject or warn.. */ 1789 if (iConf.plaintext_policy_user == POLICY_DENY) 1790 addmultiline(&iConf.plaintext_policy_user_message, "Insecure connection. Please reconnect using TLS."); 1791 else if (iConf.plaintext_policy_user == POLICY_WARN) 1792 addmultiline(&iConf.plaintext_policy_user_message, "WARNING: Insecure connection. Please consider using TLS."); 1793 } 1794 1795 if (!iConf.plaintext_policy_oper_message) 1796 { 1797 /* The message depends on whether it's reject or warn.. */ 1798 if (iConf.plaintext_policy_oper == POLICY_DENY) 1799 { 1800 addmultiline(&iConf.plaintext_policy_oper_message, "You need to use a secure connection (TLS) in order to /OPER."); 1801 addmultiline(&iConf.plaintext_policy_oper_message, "See https://www.unrealircd.org/docs/FAQ#oper-requires-tls"); 1802 } 1803 else if (iConf.plaintext_policy_oper == POLICY_WARN) 1804 addmultiline(&iConf.plaintext_policy_oper_message, "WARNING: You /OPER'ed up from an insecure connection. Please consider using TLS."); 1805 } 1806 1807 if (!iConf.outdated_tls_policy_user_message) 1808 { 1809 /* The message depends on whether it's reject or warn.. */ 1810 if (iConf.outdated_tls_policy_user == POLICY_DENY) 1811 safe_strdup(iConf.outdated_tls_policy_user_message, "Your IRC client is using an outdated TLS protocol or ciphersuite ($protocol-$cipher). Please upgrade your IRC client."); 1812 else if (iConf.outdated_tls_policy_user == POLICY_WARN) 1813 safe_strdup(iConf.outdated_tls_policy_user_message, "WARNING: Your IRC client is using an outdated TLS protocol or ciphersuite ($protocol-$cipher). Please upgrade your IRC client."); 1814 } 1815 1816 if (!iConf.outdated_tls_policy_oper_message) 1817 { 1818 /* The message depends on whether it's reject or warn.. */ 1819 if (iConf.outdated_tls_policy_oper == POLICY_DENY) 1820 safe_strdup(iConf.outdated_tls_policy_oper_message, "Your IRC client is using an outdated TLS protocol or ciphersuite ($protocol-$cipher). Please upgrade your IRC client."); 1821 else if (iConf.outdated_tls_policy_oper == POLICY_WARN) 1822 safe_strdup(iConf.outdated_tls_policy_oper_message, "WARNING: Your IRC client is using an outdated TLS protocol or ciphersuite ($protocol-$cipher). Please upgrade your IRC client."); 1823 } 1824 1825 postconf_defaults_log_block(); 1826 } 1827 1828 void postconf_fixes(void) 1829 { 1830 /* If set::topic-setter is set to "nick-user-host" then the 1831 * maximum topic length becomes shorter. 1832 */ 1833 if ((iConf.topic_setter == SETTER_NICK_USER_HOST) && 1834 (iConf.topic_length > 340)) 1835 { 1836 config_warn("set::topic-length adjusted from %d to 340, which is the maximum because " 1837 "set::topic-setter is set to 'nick-user-host'.", iConf.topic_length); 1838 iConf.topic_length = 340; 1839 } 1840 } 1841 1842 /* Needed for set::options::allow-part-if-shunned, 1843 * we can't just make it CMD_SHUN and do a ALLOW_PART_IF_SHUNNED in 1844 * cmd_part itself because that will also block internal calls (like sapart). -- Syzop 1845 */ 1846 static void do_weird_shun_stuff() 1847 { 1848 RealCommand *cmptr; 1849 1850 if ((cmptr = find_command_simple("PART"))) 1851 { 1852 if (ALLOW_PART_IF_SHUNNED) 1853 cmptr->flags |= CMD_SHUN; 1854 else 1855 cmptr->flags &= ~CMD_SHUN; 1856 } 1857 } 1858 1859 /** Various things that are done at the very end after the configuration file 1860 * has been read and almost all values have been set. This is to deal with 1861 * things like adding a default log { } block if there is none and that kind 1862 * of things. 1863 * This function is called by config_test(), both on boot and on rehash. 1864 */ 1865 void postconf(void) 1866 { 1867 postconf_defaults(); 1868 postconf_fixes(); 1869 do_weird_shun_stuff(); 1870 isupport_init(); /* for all the 005 values that changed.. */ 1871 tls_check_expiry(NULL); 1872 1873 #if OPENSSL_VERSION_NUMBER >= 0x10101000L 1874 if (loop.rehashing) 1875 reinit_tls(); 1876 #endif 1877 } 1878 1879 int isanyserverlinked(void) 1880 { 1881 return !list_empty(&server_list); 1882 } 1883 1884 void applymeblock(void) 1885 { 1886 if (!conf_me) 1887 return; /* uh-huh? */ 1888 1889 /* Info text may always change, just wouldn't show up on other servers, that's all.. */ 1890 strlcpy(me.info, conf_me->info, sizeof(me.info)); 1891 1892 /* Name can only be set once (on boot) */ 1893 if (!*me.name) 1894 strlcpy(me.name, conf_me->name, sizeof(me.name)); 1895 else if (strcmp(me.name, conf_me->name)) 1896 { 1897 config_warn("You changed the servername (me::name). " 1898 "This change will NOT be effective unless you restart the IRC Server."); 1899 } 1900 1901 if (!*me.id) 1902 strlcpy(me.id, conf_me->sid, sizeof(me.id)); 1903 } 1904 1905 /** Run config test and all post config tests. */ 1906 int config_test_all(void) 1907 { 1908 if ((config_test_blocks() < 0) || (callbacks_check() < 0) || (efunctions_check() < 0) || 1909 reloadable_perm_module_unloaded() || !tls_tests() || !log_tests()) 1910 { 1911 return 0; 1912 } 1913 1914 special_delayed_unloading(); 1915 1916 return 1; 1917 } 1918 1919 /** Process all loadmodule directives in all includes. 1920 * This was previously done at the same time as 'include' was called but 1921 * that was too early now that we have blacklist-module, so moved here. 1922 * @retval 1 on success, 0 on any failed loadmodule directive. 1923 */ 1924 int config_loadmodules(void) 1925 { 1926 ConfigFile *cfptr; 1927 ConfigEntry *ce; 1928 ConfigItem_blacklist_module *blm, *blm_next; 1929 1930 int fatal_ret = 0, ret; 1931 1932 for (cfptr = conf; cfptr; cfptr = cfptr->next) 1933 { 1934 if (config_verbose > 1) 1935 config_status("Testing %s", cfptr->filename); 1936 for (ce = cfptr->items; ce; ce = ce->next) 1937 { 1938 if (!strcmp(ce->name, "loadmodule")) 1939 { 1940 if (ce->conditional_config) 1941 { 1942 config_error("%s:%d: Currently you cannot have a 'loadmodule' statement " 1943 "within an @if block, sorry.", 1944 ce->file->filename, ce->line_number); 1945 return 0; 1946 } 1947 ret = _conf_loadmodule(cfptr, ce); 1948 if (ret < fatal_ret) 1949 fatal_ret = ret; /* lowest wins */ 1950 } 1951 } 1952 } 1953 1954 /* Let's free the blacklist-module list here as well */ 1955 for (blm = conf_blacklist_module; blm; blm = blm_next) 1956 { 1957 blm_next = blm->next; 1958 safe_free(blm->name); 1959 safe_free(blm); 1960 } 1961 conf_blacklist_module = NULL; 1962 /* End of freeing code */ 1963 1964 /* If any loadmodule returned a fatal (-1) error code then we return fail status (0) */ 1965 if (fatal_ret < 0) 1966 return 0; /* FAIL */ 1967 return 1; /* SUCCESS */ 1968 } 1969 1970 /** Reject the configuration load. 1971 * This is called both from boot and from rehash. 1972 */ 1973 void config_load_failed(void) 1974 { 1975 if (conf) 1976 unreal_log(ULOG_ERROR, "config", "CONFIG_NOT_LOADED", NULL, "IRCd configuration failed to load"); 1977 loop.config_status = CONFIG_STATUS_ROLLBACK; 1978 Unload_all_testing_modules(); 1979 free_all_config_resources(); 1980 config_free(conf); 1981 conf = NULL; 1982 free_iConf(&tempiConf); 1983 #ifdef _WIN32 1984 if (!loop.rehashing) 1985 win_error(); /* GUI popup */ 1986 #endif 1987 } 1988 1989 int config_read_start(void) 1990 { 1991 int ret; 1992 1993 config_status("Loading IRCd configuration.."); 1994 loop.config_load_failed = 0; 1995 1996 if (conf) 1997 { 1998 config_error("%s:%i - Someone forgot to clean up", __FILE__, __LINE__); 1999 return -1; 2000 } 2001 2002 /* We set this to 1 because otherwise we may call rehash_internal() 2003 * already from config_read_file() which is too soon (race). 2004 */ 2005 loop.rehash_download_busy = 1; 2006 add_config_resource(configfile, RESOURCE_INCLUDE, NULL); 2007 ret = config_read_file(configfile, configfile); 2008 loop.rehash_download_busy = 0; 2009 if (ret < 0) 2010 { 2011 config_load_failed(); 2012 return -1; 2013 } 2014 return 1; 2015 } 2016 2017 int is_config_read_finished(void) 2018 { 2019 ConfigResource *rs; 2020 2021 if (loop.rehash_download_busy) 2022 return 0; 2023 2024 for (rs = config_resources; rs; rs = rs->next) 2025 { 2026 if (rs->type & RESOURCE_DLQUEUED) 2027 { 2028 //config_status("Waiting for %s...", rs->url); 2029 return 0; 2030 } 2031 } 2032 2033 return 1; 2034 } 2035 2036 int config_test(void) 2037 { 2038 char *old_pid_file = NULL; 2039 2040 if (loop.config_load_failed) 2041 { 2042 /* An error was already printed to the user. 2043 * This happens in case of a failed loaded remote URL 2044 */ 2045 config_load_failed(); 2046 return -1; 2047 } 2048 2049 config_status("Testing IRCd configuration.."); 2050 loop.config_status = CONFIG_STATUS_TEST; 2051 2052 memset(&tempiConf, 0, sizeof(iConf)); 2053 memset(&settings, 0, sizeof(settings)); 2054 memset(&requiredstuff, 0, sizeof(requiredstuff)); 2055 memset(&nicklengths, 0, sizeof(nicklengths)); 2056 config_setdefaultsettings(&tempiConf); 2057 clicap_pre_rehash(); 2058 log_pre_rehash(); 2059 free_config_defines(); 2060 2061 if (!config_loadmodules()) 2062 { 2063 config_load_failed(); 2064 return -1; 2065 } 2066 2067 loop.config_status = CONFIG_STATUS_POSTTEST; 2068 2069 preprocessor_resolve_conditionals_all(PREPROCESSOR_PHASE_MODULE); 2070 2071 if (!config_test_all()) 2072 { 2073 config_error("IRCd configuration failed to pass testing"); 2074 config_load_failed(); 2075 return -1; 2076 } 2077 loop.config_status = CONFIG_STATUS_PRE_INIT; 2078 callbacks_switchover(); 2079 efunctions_switchover(); 2080 set_targmax_defaults(); 2081 set_security_group_defaults(); 2082 if (loop.rehashing) 2083 { 2084 Hook *h; 2085 safe_strdup(old_pid_file, conf_files->pid_file); 2086 unrealdns_delasyncconnects(); 2087 config_rehash(); 2088 /* Notify permanent modules of the rehash */ 2089 for (h = Hooks[HOOKTYPE_REHASH]; h; h = h->next) 2090 { 2091 if (!h->owner) 2092 continue; 2093 if (!(h->owner->options & MOD_OPT_PERM)) 2094 continue; 2095 (*(h->func.intfunc))(); 2096 } 2097 /* Last step: */ 2098 Unload_all_loaded_modules(); 2099 } 2100 config_pre_run_log(); 2101 2102 loop.config_status = CONFIG_STATUS_INIT; 2103 Init_all_testing_modules(); 2104 2105 loop.config_status = CONFIG_STATUS_RUN_CONFIG; 2106 if (config_run_blocks() < 0) 2107 { 2108 config_error("Bad case of config errors. Server will now die. This really shouldn't happen"); 2109 #ifdef _WIN32 2110 if (!loop.rehashing) 2111 win_error(); 2112 #endif 2113 abort(); 2114 } 2115 2116 applymeblock(); 2117 2118 if (old_pid_file && strcmp(old_pid_file, conf_files->pid_file)) 2119 { 2120 write_pidfile(); 2121 unlink(old_pid_file); 2122 } 2123 safe_free(old_pid_file); 2124 2125 config_free(conf); 2126 conf = NULL; 2127 if (loop.rehashing) 2128 { 2129 /* loop.config_status = CONFIG_STATUS_LOAD is done by module_loadall() */ 2130 module_loadall(); 2131 RunHook(HOOKTYPE_REHASH_COMPLETE); 2132 } 2133 loop.config_status = CONFIG_STATUS_POSTLOAD; 2134 postconf(); 2135 unreal_log(ULOG_INFO, "config", "CONFIG_LOADED", NULL, "Configuration loaded"); 2136 unload_all_unused_mtag_handlers(); 2137 return 0; 2138 } 2139 2140 void config_parse_and_queue_urls(ConfigEntry *ce) 2141 { 2142 for (; ce; ce = ce->next) 2143 { 2144 if (loop.config_load_failed) 2145 break; 2146 if (ce->name && !strcmp(ce->name, "include")) 2147 continue; /* handled elsewhere */ 2148 if (ce->value && !ce->escaped && url_is_valid(ce->value)) 2149 add_config_resource(ce->value, 0, ce); 2150 if (ce->items) 2151 config_parse_and_queue_urls(ce->items); 2152 } 2153 } 2154 2155 /** 2156 * Read configuration file into ConfigEntry items and add it to the 'conf' 2157 * list. This checks the file for parse errors, but doesn't do much 2158 * otherwise. Only: module blacklist checking and checking for "include" 2159 * items to see if we need to read and parse more configuration files 2160 * that are included from this one. 2161 * 2162 * One _must_ call add_config_resource() before calling config_read_file(). 2163 * This way, include recursion may be detected and reported to the user 2164 * as an error instead of causing the IRCd to hang in an infinite 2165 * recursion, eat up memory, and eventually overflow its stack ;-). 2166 * 2167 * @param filename the file where the conf may be read from 2168 * @param display_name The path or URL used to refer to this file. 2169 * (mostly to support remote includes' URIs for recursive include detection). 2170 * @return 1 on success, a negative number on error 2171 */ 2172 int config_read_file(const char *filename, const char *display_name) 2173 { 2174 ConfigFile *cfptr, *cfptr2, **cfptr3; 2175 ConfigEntry *ce; 2176 ConfigResource *rs; 2177 int ret; 2178 int counter; 2179 2180 if (config_verbose > 0) 2181 config_status("Loading config file %s ..", filename); 2182 2183 need_operclass_permissions_upgrade = 0; 2184 2185 /* Check if we're accidentally including a file a second 2186 * time. We should expect to find one entry in this list: the 2187 * entry for our current file. 2188 * Note that no user should be able to trigger this, this 2189 * can only happen if we have buggy code somewhere. 2190 */ 2191 counter = 0; 2192 for (rs = config_resources; rs; rs = rs->next) 2193 { 2194 #ifndef _WIN32 2195 if (rs->file && !strcmp(filename, rs->file)) 2196 #else 2197 if (rs->file && !strcasecmp(filename, rs->file)) 2198 #endif 2199 { 2200 counter ++; 2201 continue; 2202 } 2203 if (rs->url && !strcmp(display_name, rs->url)) 2204 { 2205 counter ++; 2206 continue; 2207 } 2208 } 2209 if (counter > 1) 2210 { 2211 unreal_log(ULOG_ERROR, "config", "CONFIG_BUG_DUPLICATE_RESOURCE", NULL, 2212 "[BUG] Config file $file has been loaded $counter times. " 2213 "This should not happen. Someone forgot to call " 2214 "add_config_resource() or check its return value!", 2215 log_data_string("file", filename), 2216 log_data_integer("counter", counter)); 2217 return -1; 2218 } 2219 /* end include recursion checking code */ 2220 2221 if ((cfptr = config_load(filename, display_name))) 2222 { 2223 for (cfptr3 = &conf, cfptr2 = conf; cfptr2; cfptr2 = cfptr2->next) 2224 cfptr3 = &cfptr2->next; 2225 *cfptr3 = cfptr; 2226 2227 if (config_verbose > 1) 2228 config_status("Loading module blacklist in %s", filename); 2229 2230 preprocessor_resolve_conditionals_ce(&cfptr->items, PREPROCESSOR_PHASE_INITIAL); 2231 2232 for (ce = cfptr->items; ce; ce = ce->next) 2233 if (!strcmp(ce->name, "blacklist-module")) 2234 _test_blacklist_module(cfptr, ce); 2235 2236 preprocessor_resolve_conditionals_ce(&cfptr->items, PREPROCESSOR_PHASE_SECONDARY); 2237 2238 /* Load urls */ 2239 config_parse_and_queue_urls(cfptr->items); 2240 2241 if(loop.config_load_failed) /* something bad happened while processing urls */ 2242 return -1; 2243 2244 /* Load includes */ 2245 if (config_verbose > 1) 2246 config_status("Searching through %s for include files..", filename); 2247 2248 for (ce = cfptr->items; ce; ce = ce->next) 2249 { 2250 if (!strcmp(ce->name, "include")) 2251 { 2252 if (ce->conditional_config) 2253 { 2254 config_error("%s:%d: Currently you cannot have an 'include' statement " 2255 "within an @if block, sorry. However, you CAN do it the other " 2256 "way around, that is: put the @if within the included file itself.", 2257 ce->file->filename, ce->line_number); 2258 return -1; 2259 } 2260 ret = _conf_include(cfptr, ce); 2261 if (ret < 0) 2262 return ret; 2263 } 2264 } 2265 return 1; 2266 } 2267 else 2268 { 2269 unreal_log(ULOG_ERROR, "config", "CONFIG_LOAD_FILE_FAILED", NULL, 2270 "Could not load configuration file: $resource", 2271 log_data_string("resource", display_name), 2272 log_data_string("filename", filename)); 2273 #ifdef _WIN32 2274 if (!strcmp(filename, "conf/unrealircd.conf")) 2275 { 2276 if (file_exists("unrealircd.conf")) 2277 { 2278 config_error("Note that 'unrealircd.conf' now belongs in the 'conf' subdirectory! (So move it to there)"); 2279 } else { 2280 config_error("New to UnrealIRCd? Be sure to read https://www.unrealircd.org/docs/Installing_%%28Windows%%29"); 2281 } 2282 } 2283 #endif 2284 return -1; 2285 } 2286 } 2287 2288 /** Remove all TKL's that were added by the config file(s). 2289 * This is done after config passed testing and right before 2290 * adding the (new) entries. 2291 */ 2292 void remove_config_tkls(void) 2293 { 2294 TKL *tk, *tk_next; 2295 int index, index2; 2296 2297 /* IP hashed TKL list */ 2298 for (index = 0; index < TKLIPHASHLEN1; index++) 2299 { 2300 for (index2 = 0; index2 < TKLIPHASHLEN2; index2++) 2301 { 2302 for (tk = tklines_ip_hash[index][index2]; tk; tk = tk_next) 2303 { 2304 tk_next = tk->next; 2305 if (tk->flags & TKL_FLAG_CONFIG) 2306 tkl_del_line(tk); 2307 } 2308 } 2309 } 2310 2311 /* Generic TKL list */ 2312 for (index = 0; index < TKLISTLEN; index++) 2313 { 2314 for (tk = tklines[index]; tk; tk = tk_next) 2315 { 2316 tk_next = tk->next; 2317 if (tk->flags & TKL_FLAG_CONFIG) 2318 tkl_del_line(tk); 2319 } 2320 } 2321 } 2322 2323 void config_rehash() 2324 { 2325 ConfigItem_oper *oper_ptr; 2326 ConfigItem_class *class_ptr; 2327 ConfigItem_ulines *uline_ptr; 2328 ConfigItem_allow *allow_ptr; 2329 ConfigItem_ban *ban_ptr; 2330 ConfigItem_link *link_ptr; 2331 ConfigItem_listen *listen_ptr; 2332 ConfigItem_tld *tld_ptr; 2333 ConfigItem_vhost *vhost_ptr; 2334 ConfigItem_deny_channel *deny_channel_ptr; 2335 ConfigItem_allow_channel *allow_channel_ptr; 2336 ConfigItem_admin *admin_ptr; 2337 ConfigItem_deny_version *deny_version_ptr; 2338 ConfigItem_alias *alias_ptr; 2339 ConfigItem_help *help_ptr; 2340 ConfigItem_offchans *of_ptr; 2341 ConfigItem_sni *sni; 2342 OperStat *os_ptr; 2343 ListStruct *next, *next2; 2344 SpamExcept *spamex_ptr; 2345 2346 USE_BAN_VERSION = 0; 2347 2348 for (admin_ptr = conf_admin; admin_ptr; admin_ptr = (ConfigItem_admin *)next) 2349 { 2350 next = (ListStruct *)admin_ptr->next; 2351 safe_free(admin_ptr->line); 2352 DelListItem(admin_ptr, conf_admin); 2353 safe_free(admin_ptr); 2354 } 2355 2356 for (oper_ptr = conf_oper; oper_ptr; oper_ptr = (ConfigItem_oper *)next) 2357 { 2358 SWhois *s, *s_next; 2359 next = (ListStruct *)oper_ptr->next; 2360 safe_free(oper_ptr->name); 2361 safe_free(oper_ptr->snomask); 2362 safe_free(oper_ptr->operclass); 2363 safe_free(oper_ptr->vhost); 2364 Auth_FreeAuthConfig(oper_ptr->auth); 2365 free_security_group(oper_ptr->match); 2366 DelListItem(oper_ptr, conf_oper); 2367 for (s = oper_ptr->swhois; s; s = s_next) 2368 { 2369 s_next = s->next; 2370 safe_free(s->line); 2371 safe_free(s->setby); 2372 safe_free(s); 2373 } 2374 safe_free(oper_ptr); 2375 } 2376 2377 for (link_ptr = conf_link; link_ptr; link_ptr = (ConfigItem_link *) next) 2378 { 2379 next = (ListStruct *)link_ptr->next; 2380 if (link_ptr->refcount == 0) 2381 { 2382 delete_linkblock(link_ptr); 2383 } 2384 else 2385 { 2386 link_ptr->flag.temporary = 1; 2387 } 2388 } 2389 for (class_ptr = conf_class; class_ptr; class_ptr = (ConfigItem_class *) next) 2390 { 2391 next = (ListStruct *)class_ptr->next; 2392 if (class_ptr->flag.permanent == 1) 2393 continue; 2394 class_ptr->flag.temporary = 1; 2395 /* We'll wipe it out when it has no clients */ 2396 if (!class_ptr->clients && !class_ptr->xrefcount) 2397 { 2398 delete_classblock(class_ptr); 2399 } 2400 } 2401 for (uline_ptr = conf_ulines; uline_ptr; uline_ptr = (ConfigItem_ulines *) next) 2402 { 2403 next = (ListStruct *)uline_ptr->next; 2404 /* We'll wipe it out when it has no clients */ 2405 safe_free(uline_ptr->servername); 2406 DelListItem(uline_ptr, conf_ulines); 2407 safe_free(uline_ptr); 2408 } 2409 for (allow_ptr = conf_allow; allow_ptr; allow_ptr = (ConfigItem_allow *) next) 2410 { 2411 next = (ListStruct *)allow_ptr->next; 2412 free_security_group(allow_ptr->match); 2413 Auth_FreeAuthConfig(allow_ptr->auth); 2414 DelListItem(allow_ptr, conf_allow); 2415 safe_free(allow_ptr); 2416 } 2417 /* Free ban realname { }, ban server { } and ban version { } */ 2418 for (ban_ptr = conf_ban; ban_ptr; ban_ptr = (ConfigItem_ban *) next) 2419 { 2420 next = (ListStruct *)ban_ptr->next; 2421 if (ban_ptr->flag.type2 == CONF_BAN_TYPE_CONF || ban_ptr->flag.type2 == CONF_BAN_TYPE_TEMPORARY) 2422 { 2423 safe_free(ban_ptr->mask); 2424 safe_free(ban_ptr->reason); 2425 DelListItem(ban_ptr, conf_ban); 2426 safe_free(ban_ptr); 2427 } 2428 } 2429 for (listen_ptr = conf_listen; listen_ptr; listen_ptr = listen_ptr->next) 2430 { 2431 if (!(listen_ptr->options & LISTENER_CONTROL)) 2432 listen_ptr->flag.temporary = 1; 2433 } 2434 for (tld_ptr = conf_tld; tld_ptr; tld_ptr = (ConfigItem_tld *) next) 2435 { 2436 next = (ListStruct *)tld_ptr->next; 2437 safe_free(tld_ptr->motd_file); 2438 safe_free(tld_ptr->rules_file); 2439 safe_free(tld_ptr->smotd_file); 2440 safe_free(tld_ptr->opermotd_file); 2441 safe_free(tld_ptr->botmotd_file); 2442 2443 free_motd(&tld_ptr->motd); 2444 free_motd(&tld_ptr->rules); 2445 free_motd(&tld_ptr->smotd); 2446 free_motd(&tld_ptr->opermotd); 2447 free_motd(&tld_ptr->botmotd); 2448 2449 free_security_group(tld_ptr->match); 2450 2451 DelListItem(tld_ptr, conf_tld); 2452 safe_free(tld_ptr); 2453 } 2454 for (vhost_ptr = conf_vhost; vhost_ptr; vhost_ptr = (ConfigItem_vhost *) next) 2455 { 2456 SWhois *s, *s_next; 2457 2458 next = (ListStruct *)vhost_ptr->next; 2459 2460 safe_free(vhost_ptr->login); 2461 Auth_FreeAuthConfig(vhost_ptr->auth); 2462 safe_free(vhost_ptr->virthost); 2463 safe_free(vhost_ptr->virtuser); 2464 free_security_group(vhost_ptr->match); 2465 for (s = vhost_ptr->swhois; s; s = s_next) 2466 { 2467 s_next = s->next; 2468 safe_free(s->line); 2469 safe_free(s->setby); 2470 safe_free(s); 2471 } 2472 DelListItem(vhost_ptr, conf_vhost); 2473 safe_free(vhost_ptr); 2474 } 2475 2476 remove_config_tkls(); 2477 2478 for (deny_version_ptr = conf_deny_version; deny_version_ptr; deny_version_ptr = (ConfigItem_deny_version *) next) { 2479 next = (ListStruct *)deny_version_ptr->next; 2480 safe_free(deny_version_ptr->mask); 2481 safe_free(deny_version_ptr->version); 2482 safe_free(deny_version_ptr->flags); 2483 DelListItem(deny_version_ptr, conf_deny_version); 2484 safe_free(deny_version_ptr); 2485 } 2486 for (deny_channel_ptr = conf_deny_channel; deny_channel_ptr; deny_channel_ptr = (ConfigItem_deny_channel *) next) 2487 { 2488 next = (ListStruct *)deny_channel_ptr->next; 2489 safe_free(deny_channel_ptr->redirect); 2490 safe_free(deny_channel_ptr->channel); 2491 safe_free(deny_channel_ptr->reason); 2492 safe_free(deny_channel_ptr->class); 2493 DelListItem(deny_channel_ptr, conf_deny_channel); 2494 free_security_group(deny_channel_ptr->match); 2495 safe_free(deny_channel_ptr); 2496 } 2497 2498 for (allow_channel_ptr = conf_allow_channel; allow_channel_ptr; allow_channel_ptr = (ConfigItem_allow_channel *) next) 2499 { 2500 next = (ListStruct *)allow_channel_ptr->next; 2501 safe_free(allow_channel_ptr->channel); 2502 safe_free(allow_channel_ptr->class); 2503 DelListItem(allow_channel_ptr, conf_allow_channel); 2504 free_security_group(allow_channel_ptr->match); 2505 safe_free(allow_channel_ptr); 2506 } 2507 2508 if (conf_drpass) 2509 { 2510 Auth_FreeAuthConfig(conf_drpass->restartauth); 2511 conf_drpass->restartauth = NULL; 2512 Auth_FreeAuthConfig(conf_drpass->dieauth); 2513 conf_drpass->dieauth = NULL; 2514 safe_free(conf_drpass); 2515 } 2516 for (alias_ptr = conf_alias; alias_ptr; alias_ptr = (ConfigItem_alias *)next) { 2517 RealCommand *cmptr = find_command(alias_ptr->alias, 0); 2518 ConfigItem_alias_format *fmt; 2519 next = (ListStruct *)alias_ptr->next; 2520 safe_free(alias_ptr->nick); 2521 if (cmptr) 2522 CommandDelX(NULL, cmptr); 2523 safe_free(alias_ptr->alias); 2524 if (alias_ptr->format && (alias_ptr->type == ALIAS_COMMAND)) { 2525 for (fmt = (ConfigItem_alias_format *) alias_ptr->format; fmt; fmt = (ConfigItem_alias_format *) next2) 2526 { 2527 next2 = (ListStruct *)fmt->next; 2528 safe_free(fmt->format); 2529 safe_free(fmt->nick); 2530 safe_free(fmt->parameters); 2531 unreal_delete_match(fmt->expr); 2532 DelListItem(fmt, alias_ptr->format); 2533 safe_free(fmt); 2534 } 2535 } 2536 DelListItem(alias_ptr, conf_alias); 2537 safe_free(alias_ptr); 2538 } 2539 for (help_ptr = conf_help; help_ptr; help_ptr = (ConfigItem_help *)next) { 2540 MOTDLine *text; 2541 next = (ListStruct *)help_ptr->next; 2542 safe_free(help_ptr->command); 2543 while (help_ptr->text) { 2544 text = help_ptr->text->next; 2545 safe_free(help_ptr->text->line); 2546 safe_free(help_ptr->text); 2547 help_ptr->text = text; 2548 } 2549 DelListItem(help_ptr, conf_help); 2550 safe_free(help_ptr); 2551 } 2552 for (os_ptr = iConf.allow_user_stats_ext; os_ptr; os_ptr = (OperStat *)next) 2553 { 2554 next = (ListStruct *)os_ptr->next; 2555 safe_free(os_ptr->flag); 2556 safe_free(os_ptr); 2557 } 2558 iConf.allow_user_stats_ext = NULL; 2559 for (spamex_ptr = iConf.spamexcept; spamex_ptr; spamex_ptr = (SpamExcept *)next) 2560 { 2561 next = (ListStruct *)spamex_ptr->next; 2562 safe_free(spamex_ptr); 2563 } 2564 iConf.spamexcept = NULL; 2565 for (of_ptr = conf_offchans; of_ptr; of_ptr = (ConfigItem_offchans *)next) 2566 { 2567 next = (ListStruct *)of_ptr->next; 2568 safe_free(of_ptr->topic); 2569 safe_free(of_ptr); 2570 } 2571 conf_offchans = NULL; 2572 2573 /* Free sni { } blocks */ 2574 for (sni = conf_sni; sni; sni = (ConfigItem_sni *)next) 2575 { 2576 next = (ListStruct *)sni->next; 2577 SSL_CTX_free(sni->ssl_ctx); 2578 free_tls_options(sni->tls_options); 2579 safe_free(sni->name); 2580 safe_free(sni); 2581 } 2582 conf_sni = NULL; 2583 2584 free_conf_channelmodes(&iConf.modes_on_join); 2585 2586 /* 2587 reset conf_files -- should this be in its own function? no, because 2588 it's only used here 2589 */ 2590 safe_free(conf_files->motd_file); 2591 safe_free(conf_files->smotd_file); 2592 safe_free(conf_files->opermotd_file); 2593 safe_free(conf_files->svsmotd_file); 2594 safe_free(conf_files->botmotd_file); 2595 safe_free(conf_files->rules_file); 2596 safe_free(conf_files->pid_file); 2597 safe_free(conf_files->tune_file); 2598 /* 2599 Don't free conf_files->pid_file here; the old value is used to determine if 2600 the pidfile location has changed and write_pidfile() needs to be called 2601 again. 2602 */ 2603 safe_free(conf_files); 2604 conf_files = NULL; 2605 } 2606 2607 int config_post_test() 2608 { 2609 #define Error(x) { config_error((x)); errors++; } 2610 int errors = 0; 2611 Hook *h; 2612 2613 if (!requiredstuff.conf_me) 2614 Error("me {} block is missing"); 2615 if (!requiredstuff.conf_admin) 2616 Error("admin {} block is missing"); 2617 if (!requiredstuff.conf_listen) 2618 Error("listen {} block is missing"); 2619 if (!settings.has_kline_address) 2620 Error("set::kline-address is missing"); 2621 if (!settings.has_default_server) 2622 Error("set::default-server is missing"); 2623 if (!settings.has_network_name) 2624 Error("set::network-name is missing"); 2625 if (!settings.has_help_channel) 2626 Error("set::help-channel is missing"); 2627 if (nicklengths.min > nicklengths.max) 2628 Error("set::nick-length is smaller than set::min-nick-length"); 2629 2630 for (h = Hooks[HOOKTYPE_CONFIGPOSTTEST]; h; h = h->next) 2631 { 2632 int value, errs = 0; 2633 if (h->owner && !(h->owner->flags & MODFLAG_TESTING) && 2634 !(h->owner->options & MOD_OPT_PERM)) 2635 continue; 2636 value = (*(h->func.intfunc))(&errs); 2637 if (value == -1) 2638 { 2639 errors += errs; 2640 break; 2641 } 2642 if (value == -2) 2643 errors += errs; 2644 } 2645 return errors; 2646 } 2647 2648 /** Make the "read" config the "live" config */ 2649 void config_switchover(void) 2650 { 2651 free_iConf(&iConf); 2652 memcpy(&iConf, &tempiConf, sizeof(iConf)); 2653 memset(&tempiConf, 0, sizeof(tempiConf)); 2654 log_blocks_switchover(); 2655 } 2656 2657 /** Priority of config blocks during CONFIG_TEST stage */ 2658 static const char *config_test_priority_blocks[] = 2659 { 2660 "me", 2661 "secret", 2662 "log", /* "log" needs to be before "set" in CONFIG_TEST */ 2663 "set", 2664 "class", 2665 }; 2666 2667 /** Priority of config blocks during CONFIG_RUN stage */ 2668 static const char *config_run_priority_blocks[] = 2669 { 2670 "me", 2671 "secret", 2672 "set", 2673 "log", /* "log" needs to be after "set" in CONFIG_RUN */ 2674 "class", 2675 }; 2676 2677 int config_test_blocks() 2678 { 2679 ConfigEntry *ce; 2680 ConfigFile *cfptr; 2681 ConfigCommand *cc; 2682 int errors = 0; 2683 int i; 2684 Hook *h; 2685 2686 invalid_snomasks_encountered = 0; 2687 2688 /* Stage 1: first the priority blocks, in the order as specified 2689 * in config_test_priority_blocks[] 2690 */ 2691 for (i=0; i < ARRAY_SIZEOF(config_test_priority_blocks); i++) 2692 { 2693 const char *config_block = config_test_priority_blocks[i]; 2694 cc = config_binary_search(config_block); 2695 if (!cc) 2696 abort(); /* internal fuckup */ 2697 for (cfptr = conf; cfptr; cfptr = cfptr->next) 2698 { 2699 if (config_verbose > 1) 2700 config_status("Running %s", cfptr->filename); 2701 for (ce = cfptr->items; ce; ce = ce->next) 2702 { 2703 if (!strcmp(ce->name, config_block)) 2704 { 2705 int n = cc->testfunc(cfptr, ce); 2706 errors += n; 2707 if (!strcmp(config_block, "secret") && (n == 0)) 2708 { 2709 /* Yeah special case: secret { } blocks we run 2710 * immediately here. 2711 */ 2712 _conf_secret(cfptr, ce); 2713 } 2714 } 2715 } 2716 } 2717 } 2718 2719 /* Stage 2: now all the other config blocks */ 2720 for (cfptr = conf; cfptr; cfptr = cfptr->next) 2721 { 2722 if (config_verbose > 1) 2723 config_status("Running %s", cfptr->filename); 2724 for (ce = cfptr->items; ce; ce = ce->next) 2725 { 2726 char skip = 0; 2727 for (i=0; i < ARRAY_SIZEOF(config_test_priority_blocks); i++) 2728 { 2729 if (!strcmp(ce->name, config_test_priority_blocks[i])) 2730 { 2731 skip = 1; 2732 break; 2733 } 2734 } 2735 if (skip) 2736 continue; 2737 2738 if ((cc = config_binary_search(ce->name))) { 2739 if (cc->testfunc) 2740 errors += (cc->testfunc(cfptr, ce)); 2741 } 2742 else 2743 { 2744 int used = 0; 2745 for (h = Hooks[HOOKTYPE_CONFIGTEST]; h; h = h->next) 2746 { 2747 int value, errs = 0; 2748 if (h->owner && !(h->owner->flags & MODFLAG_TESTING) 2749 && !(h->owner->options & MOD_OPT_PERM)) 2750 2751 2752 continue; 2753 value = (*(h->func.intfunc))(cfptr,ce,CONFIG_MAIN,&errs); 2754 if (value == 2) 2755 used = 1; 2756 if (value == 1) 2757 { 2758 used = 1; 2759 break; 2760 } 2761 if (value == -1) 2762 { 2763 used = 1; 2764 errors += errs; 2765 break; 2766 } 2767 if (value == -2) 2768 { 2769 used = 1; 2770 errors += errs; 2771 } 2772 2773 } 2774 if (!used) 2775 { 2776 config_error("%s:%i: unknown directive %s", 2777 ce->file->filename, ce->line_number, 2778 ce->name); 2779 errors++; 2780 if (strchr(ce->name, ':')) 2781 { 2782 config_error("You cannot use :: in a directive, you have to write them out. " 2783 "For example 'set::auto-join #something' needs to be written as: " 2784 "set { auto-join \"#something\"; }"); 2785 config_error("See also https://www.unrealircd.org/docs/Set_block#Syntax_used_in_this_documentation"); 2786 } 2787 } 2788 } 2789 } 2790 } 2791 2792 errors += config_post_test(); 2793 2794 if (errors > 0) 2795 { 2796 config_error("%i errors encountered", errors); 2797 } 2798 2799 if (invalid_snomasks_encountered) 2800 { 2801 config_error("It seems your set::snomask-on-oper and/or oper::snomask needs to be updated. Are you perhaps upgrading from an older version to UnrealIRCd 6?"); 2802 config_error("See https://www.unrealircd.org/docs/Upgrading_from_5.x#Update_your_snomasks"); 2803 } 2804 2805 return (errors > 0 ? -1 : 1); 2806 } 2807 2808 int config_run_blocks(void) 2809 { 2810 ConfigEntry *ce; 2811 ConfigFile *cfptr; 2812 ConfigCommand *cc; 2813 int errors = 0; 2814 int i; 2815 Hook *h; 2816 ConfigItem_allow *allow; 2817 2818 /* Stage 1: first the priority blocks, in the order as specified 2819 * in config_run_priority_blocks[] 2820 */ 2821 for (i=0; i < ARRAY_SIZEOF(config_run_priority_blocks); i++) 2822 { 2823 const char *config_block = config_run_priority_blocks[i]; 2824 cc = config_binary_search(config_block); 2825 if (!cc) 2826 abort(); /* internal fuckup */ 2827 if (!strcmp(config_block, "secret")) 2828 continue; /* yeah special case, we already processed the run part in test for these */ 2829 for (cfptr = conf; cfptr; cfptr = cfptr->next) 2830 { 2831 if (config_verbose > 1) 2832 config_status("Running %s", cfptr->filename); 2833 for (ce = cfptr->items; ce; ce = ce->next) 2834 { 2835 if (!strcmp(ce->name, config_block)) 2836 { 2837 if (cc->conffunc(cfptr, ce) < 0) 2838 errors++; 2839 } 2840 } 2841 } 2842 } 2843 2844 /* Stage 2: now all the other config blocks */ 2845 for (cfptr = conf; cfptr; cfptr = cfptr->next) 2846 { 2847 if (config_verbose > 1) 2848 config_status("Running %s", cfptr->filename); 2849 for (ce = cfptr->items; ce; ce = ce->next) 2850 { 2851 char skip = 0; 2852 for (i=0; i < ARRAY_SIZEOF(config_run_priority_blocks); i++) 2853 { 2854 if (!strcmp(ce->name, config_run_priority_blocks[i])) 2855 { 2856 skip = 1; 2857 break; 2858 } 2859 } 2860 if (skip) 2861 continue; 2862 2863 if ((cc = config_binary_search(ce->name))) { 2864 if ((cc->conffunc) && (cc->conffunc(cfptr, ce) < 0)) 2865 errors++; 2866 } 2867 else 2868 { 2869 int value; 2870 for (h = Hooks[HOOKTYPE_CONFIGRUN]; h; h = h->next) 2871 { 2872 value = (*(h->func.intfunc))(cfptr,ce,CONFIG_MAIN); 2873 if (value == 1) 2874 break; 2875 } 2876 } 2877 } 2878 } 2879 2880 close_unbound_listeners(); 2881 listen_cleanup(); 2882 close_unbound_listeners(); 2883 loop.do_bancheck = 1; 2884 config_switchover(); 2885 update_throttling_timer_settings(); 2886 2887 /* initialize conf_files with defaults if the block isn't set: */ 2888 if (!conf_files) 2889 _conf_files(NULL, NULL); 2890 2891 if (errors > 0) 2892 { 2893 config_error("%i fatal errors encountered", errors); 2894 } 2895 return (errors > 0 ? -1 : 1); 2896 } 2897 2898 /* 2899 * Service functions 2900 */ 2901 2902 ConfigItem_alias *find_alias(const char *name) 2903 { 2904 ConfigItem_alias *e; 2905 2906 if (!name) 2907 return NULL; 2908 2909 for (e = conf_alias; e; e = e->next) 2910 { 2911 if (!strcasecmp(e->alias, name)) 2912 return e; 2913 } 2914 return NULL; 2915 } 2916 2917 ConfigItem_class *find_class(const char *name) 2918 { 2919 ConfigItem_class *e; 2920 2921 if (!name) 2922 return NULL; 2923 2924 for (e = conf_class; e; e = e->next) 2925 { 2926 if (!strcmp(name, e->name)) 2927 return e; 2928 } 2929 return NULL; 2930 } 2931 2932 2933 ConfigItem_oper *find_oper(const char *name) 2934 { 2935 ConfigItem_oper *e; 2936 2937 if (!name) 2938 return NULL; 2939 2940 for (e = conf_oper; e; e = e->next) 2941 { 2942 if (!strcmp(name, e->name)) 2943 return e; 2944 } 2945 return NULL; 2946 } 2947 2948 ConfigItem_operclass *find_operclass(const char *name) 2949 { 2950 ConfigItem_operclass *e; 2951 2952 if (!name) 2953 return NULL; 2954 2955 for (e = conf_operclass; e; e = e->next) 2956 { 2957 if (!strcmp(name,e->classStruct->name)) 2958 return e; 2959 } 2960 return NULL; 2961 } 2962 2963 int count_oper_sessions(const char *name) 2964 { 2965 int count = 0; 2966 Client *client; 2967 2968 list_for_each_entry(client, &oper_list, special_node) 2969 { 2970 if (client->user->operlogin != NULL && !strcmp(client->user->operlogin, name)) 2971 count++; 2972 } 2973 2974 return count; 2975 } 2976 2977 ConfigItem_listen *find_listen(const char *ipmask, int port, SocketType socket_type) 2978 { 2979 ConfigItem_listen *e; 2980 2981 if (!ipmask) 2982 return NULL; 2983 2984 for (e = conf_listen; e; e = e->next) 2985 { 2986 if (e->socket_type != socket_type) 2987 continue; 2988 if (e->socket_type == SOCKET_TYPE_UNIX) 2989 { 2990 if (!strcmp(e->file, ipmask)) 2991 return e; 2992 } else 2993 { 2994 if ((e->socket_type == socket_type) && (e->port == port) && !strcmp(e->ip, ipmask)) 2995 return e; 2996 } 2997 } 2998 2999 return NULL; 3000 } 3001 3002 /** Find an SNI match. 3003 * @param name The hostname to look for (eg: irc.xyz.com). 3004 */ 3005 ConfigItem_sni *find_sni(const char *name) 3006 { 3007 ConfigItem_sni *e; 3008 3009 if (!name) 3010 return NULL; 3011 3012 for (e = conf_sni; e; e = e->next) 3013 { 3014 if (match_simple(e->name, name)) 3015 return e; 3016 } 3017 return NULL; 3018 } 3019 3020 ConfigItem_ulines *find_uline(const char *host) 3021 { 3022 ConfigItem_ulines *ulines; 3023 3024 if (!host) 3025 return NULL; 3026 3027 for(ulines = conf_ulines; ulines; ulines = ulines->next) 3028 { 3029 if (!strcasecmp(host, ulines->servername)) 3030 return ulines; 3031 } 3032 return NULL; 3033 } 3034 3035 3036 ConfigItem_tld *find_tld(Client *client) 3037 { 3038 ConfigItem_tld *tld; 3039 3040 for (tld = conf_tld; tld; tld = tld->next) 3041 { 3042 if (user_allowed_by_security_group(client, tld->match)) 3043 { 3044 if ((tld->options & TLD_TLS) && !IsSecureConnect(client)) 3045 continue; 3046 if ((tld->options & TLD_REMOTE) && MyUser(client)) 3047 continue; 3048 return tld; 3049 } 3050 } 3051 3052 return NULL; 3053 } 3054 3055 /** Find a link block by server name (but don't check any restrictions like IP or auth) */ 3056 ConfigItem_link *find_link(const char *servername) 3057 { 3058 ConfigItem_link *link; 3059 3060 for (link = conf_link; link; link = link->next) 3061 { 3062 if (!link->flag.temporary && match_simple(link->servername, servername)) 3063 { 3064 return link; 3065 } 3066 } 3067 return NULL; 3068 } 3069 3070 /** Find a ban of type CONF_BAN_*, which is currently only 3071 * CONF_BAN_SERVER, CONF_BAN_VERSION and CONF_BAN_REALNAME 3072 */ 3073 ConfigItem_ban *find_ban(Client *client, const char *host, short type) 3074 { 3075 ConfigItem_ban *ban; 3076 3077 for (ban = conf_ban; ban; ban = ban->next) 3078 { 3079 if (ban->flag.type == type) 3080 { 3081 if (client) 3082 { 3083 if (match_user(ban->mask, client, MATCH_CHECK_REAL)) 3084 return ban; 3085 } 3086 else if (match_simple(ban->mask, host)) 3087 return ban; 3088 } 3089 } 3090 return NULL; 3091 } 3092 3093 /** Find a ban of type CONF_BAN_*, which is currently only 3094 * CONF_BAN_SERVER, CONF_BAN_VERSION and CONF_BAN_REALNAME 3095 * This is the extended version, only used by cmd_svsnline. 3096 */ 3097 ConfigItem_ban *find_banEx(Client *client, const char *host, short type, short type2) 3098 { 3099 ConfigItem_ban *ban; 3100 3101 for (ban = conf_ban; ban; ban = ban->next) 3102 { 3103 if ((ban->flag.type == type) && (ban->flag.type2 == type2)) 3104 { 3105 if (client) 3106 { 3107 if (match_user(ban->mask, client, MATCH_CHECK_REAL)) 3108 return ban; 3109 } 3110 else if (match_simple(ban->mask, host)) 3111 return ban; 3112 } 3113 } 3114 return NULL; 3115 } 3116 3117 ConfigItem_vhost *find_vhost(const char *name) 3118 { 3119 ConfigItem_vhost *vhost; 3120 3121 for (vhost = conf_vhost; vhost; vhost = vhost->next) 3122 { 3123 if (!strcmp(name, vhost->login)) 3124 return vhost; 3125 } 3126 3127 return NULL; 3128 } 3129 3130 3131 /** returns NULL if allowed and struct if denied */ 3132 ConfigItem_deny_channel *find_channel_allowed(Client *client, const char *name) 3133 { 3134 ConfigItem_deny_channel *dchannel; 3135 ConfigItem_allow_channel *achannel; 3136 3137 for (dchannel = conf_deny_channel; dchannel; dchannel = dchannel->next) 3138 { 3139 if (match_simple(dchannel->channel, name)) 3140 { 3141 if (dchannel->class && strcmp(client->local->class->name, dchannel->class)) 3142 continue; 3143 if (dchannel->match && !user_allowed_by_security_group(client, dchannel->match)) 3144 continue; 3145 break; /* MATCH deny channel { } */ 3146 } 3147 } 3148 3149 if (dchannel) 3150 { 3151 /* Check exceptions... ('allow channel') */ 3152 for (achannel = conf_allow_channel; achannel; achannel = achannel->next) 3153 { 3154 if (match_simple(achannel->channel, name)) 3155 { 3156 if (achannel->class && strcmp(client->local->class->name, achannel->class)) 3157 continue; 3158 if (achannel->match && !user_allowed_by_security_group(client, achannel->match)) 3159 continue; 3160 break; /* MATCH allow channel { } */ 3161 } 3162 } 3163 if (achannel) 3164 return NULL; /* Matches an 'allow channel' - so not forbidden */ 3165 else 3166 return dchannel; 3167 } 3168 return NULL; 3169 } 3170 3171 void init_dynconf(void) 3172 { 3173 memset(&iConf, 0, sizeof(iConf)); 3174 memset(&tempiConf, 0, sizeof(iConf)); 3175 } 3176 3177 const char *pretty_time_val_r(char *buf, size_t buflen, long timeval) 3178 { 3179 if (timeval == 0) 3180 return "0"; 3181 3182 buf[0] = 0; 3183 3184 if (timeval/86400) 3185 snprintf(buf, buflen, "%ldd", timeval/86400); 3186 if ((timeval/3600) % 24) 3187 snprintf(buf+strlen(buf), buflen-strlen(buf), "%ldh", (timeval/3600)%24); 3188 if ((timeval/60)%60) 3189 snprintf(buf+strlen(buf), buflen-strlen(buf), "%ldm", (timeval/60)%60); 3190 if ((timeval%60)) 3191 snprintf(buf+strlen(buf), buflen-strlen(buf), "%lds", timeval%60); 3192 3193 return buf; 3194 } 3195 3196 const char *pretty_time_val(long timeval) 3197 { 3198 static char buf[512]; 3199 return pretty_time_val_r(buf, sizeof(buf), timeval); 3200 } 3201 3202 /* This converts a relative path to an absolute path, but only if necessary. */ 3203 void convert_to_absolute_path(char **path, const char *reldir) 3204 { 3205 char *s; 3206 3207 if (!*path || !**path) 3208 return; /* NULL or empty */ 3209 3210 if (strstr(*path, "://")) 3211 return; /* URL: don't touch */ 3212 3213 #ifdef _WIN32 3214 if (!strncmp(*path, "cache/", 6)) 3215 return; /* downloaded from URL: don't touch (is only relative path on Windows) */ 3216 #endif 3217 3218 if ((**path == '/') || (**path == '\\')) 3219 return; /* already absolute path */ 3220 3221 if (!strncmp(*path, reldir, strlen(reldir))) 3222 return; /* already contains reldir */ 3223 3224 s = safe_alloc(strlen(reldir) + strlen(*path) + 2); 3225 sprintf(s, "%s/%s", reldir, *path); /* safe, see line above */ 3226 safe_free(*path); 3227 *path = s; 3228 } 3229 3230 /* Similar to convert_to_absolute_path() but returns a duplicated string. 3231 * Don't forget to free! 3232 */ 3233 char *convert_to_absolute_path_duplicate(char *path, char *reldir) 3234 { 3235 char *xpath = strdup(path); 3236 convert_to_absolute_path(&xpath, reldir); 3237 return xpath; 3238 } 3239 3240 /* 3241 * Actual config parser funcs 3242 */ 3243 3244 int _conf_include(ConfigFile *conf, ConfigEntry *ce) 3245 { 3246 int ret = 0; 3247 #ifdef GLOBH 3248 glob_t files; 3249 int i; 3250 #elif defined(_WIN32) 3251 HANDLE hFind; 3252 WIN32_FIND_DATA FindData; 3253 char cPath[MAX_PATH], *cSlash = NULL, *path; 3254 #endif 3255 if (!ce->value) 3256 { 3257 config_status("%s:%i: include: no filename given", 3258 ce->file->filename, 3259 ce->line_number); 3260 return -1; 3261 } 3262 3263 convert_to_absolute_path(&ce->value, CONFDIR); 3264 3265 if (url_is_valid(ce->value)) 3266 { 3267 add_config_resource(ce->value, RESOURCE_INCLUDE, ce); 3268 return 0; 3269 } 3270 #if !defined(_WIN32) && !defined(_AMIGA) && !defined(OSXTIGER) && DEFAULT_PERMISSIONS != 0 3271 (void)chmod(ce->value, DEFAULT_PERMISSIONS); 3272 #endif 3273 #ifdef GLOBH 3274 #if defined(__OpenBSD__) && defined(GLOB_LIMIT) 3275 glob(ce->value, GLOB_NOSORT|GLOB_NOCHECK|GLOB_LIMIT, NULL, &files); 3276 #else 3277 glob(ce->value, GLOB_NOSORT|GLOB_NOCHECK, NULL, &files); 3278 #endif 3279 if (!files.gl_pathc) { 3280 globfree(&files); 3281 config_status("%s:%i: include %s: invalid file given", 3282 ce->file->filename, ce->line_number, 3283 ce->value); 3284 return -1; 3285 } 3286 for (i = 0; i < files.gl_pathc; i++) 3287 { 3288 if (add_config_resource(files.gl_pathv[i], RESOURCE_INCLUDE, ce)) 3289 { 3290 ret = config_read_file(files.gl_pathv[i], files.gl_pathv[i]); 3291 if (ret < 0) 3292 { 3293 globfree(&files); 3294 return ret; 3295 } 3296 } 3297 } 3298 globfree(&files); 3299 #elif defined(_WIN32) 3300 memset(cPath, 0, MAX_PATH); 3301 if (strchr(ce->value, '/') || strchr(ce->value, '\\')) { 3302 strlcpy(cPath,ce->value,MAX_PATH); 3303 cSlash=cPath+strlen(cPath); 3304 while(*cSlash != '\\' && *cSlash != '/' && cSlash > cPath) 3305 cSlash--; 3306 *(cSlash+1)=0; 3307 } 3308 if ( (hFind = FindFirstFile(ce->value, &FindData)) == INVALID_HANDLE_VALUE ) 3309 { 3310 config_status("%s:%i: include %s: invalid file given", 3311 ce->file->filename, ce->line_number, 3312 ce->value); 3313 return -1; 3314 } 3315 if (cPath) { 3316 path = safe_alloc(strlen(cPath) + strlen(FindData.cFileName)+1); 3317 strcpy(path, cPath); 3318 strcat(path, FindData.cFileName); 3319 3320 if (add_config_resource(path, RESOURCE_INCLUDE, ce)) 3321 { 3322 ret = config_read_file(path, path); 3323 safe_free(path); 3324 } 3325 } 3326 else 3327 { 3328 if (add_config_resource(FindData.cFileName, RESOURCE_INCLUDE, ce)) 3329 ret = config_read_file(FindData.cFileName, FindData.cFileName); 3330 } 3331 if (ret < 0) 3332 { 3333 FindClose(hFind); 3334 return ret; 3335 } 3336 3337 ret = 0; 3338 while (FindNextFile(hFind, &FindData) != 0) { 3339 if (cPath) { 3340 path = safe_alloc(strlen(cPath) + strlen(FindData.cFileName)+1); 3341 strcpy(path,cPath); 3342 strcat(path,FindData.cFileName); 3343 3344 if (add_config_resource(path, RESOURCE_INCLUDE, ce)) 3345 { 3346 ret = config_read_file(path, path); 3347 safe_free(path); 3348 if (ret < 0) 3349 break; 3350 } 3351 } 3352 else 3353 { 3354 if (add_config_resource(FindData.cFileName, RESOURCE_INCLUDE, ce)) 3355 ret = config_read_file(FindData.cFileName, FindData.cFileName); 3356 } 3357 } 3358 FindClose(hFind); 3359 if (ret < 0) 3360 return ret; 3361 #else 3362 if (add_config_resource(ce->value, RESOURCE_INCLUDE, ce)) 3363 ret = config_read_file(ce->value, ce->value); 3364 return ret; 3365 #endif 3366 return 1; 3367 } 3368 3369 int _test_include(ConfigFile *conf, ConfigEntry *ce) 3370 { 3371 return 0; 3372 } 3373 3374 int _conf_admin(ConfigFile *conf, ConfigEntry *ce) 3375 { 3376 ConfigEntry *cep; 3377 ConfigItem_admin *ca; 3378 3379 for (cep = ce->items; cep; cep = cep->next) 3380 { 3381 ca = safe_alloc(sizeof(ConfigItem_admin)); 3382 if (!conf_admin) 3383 conf_admin_tail = ca; 3384 safe_strdup(ca->line, cep->name); 3385 AddListItem(ca, conf_admin); 3386 } 3387 return 1; 3388 } 3389 3390 int _test_admin(ConfigFile *conf, ConfigEntry *ce) 3391 { 3392 ConfigEntry *cep; 3393 int errors = 0; 3394 3395 if (requiredstuff.conf_admin) 3396 { 3397 config_warn_duplicate(ce->file->filename, ce->line_number, "admin"); 3398 return 0; 3399 } 3400 3401 for (cep = ce->items; cep; cep = cep->next) 3402 { 3403 if (strlen(cep->name) > 500) 3404 { 3405 config_error("%s:%i: oversized data in admin block", 3406 cep->file->filename, 3407 cep->line_number); 3408 errors++; 3409 continue; 3410 } 3411 } 3412 requiredstuff.conf_admin = 1; 3413 return errors; 3414 } 3415 3416 int _conf_me(ConfigFile *conf, ConfigEntry *ce) 3417 { 3418 ConfigEntry *cep; 3419 3420 if (!conf_me) 3421 conf_me = safe_alloc(sizeof(ConfigItem_me)); 3422 3423 for (cep = ce->items; cep; cep = cep->next) 3424 { 3425 if (!strcmp(cep->name, "name")) 3426 { 3427 safe_strdup(conf_me->name, cep->value); 3428 } 3429 else if (!strcmp(cep->name, "info")) 3430 { 3431 safe_strdup(conf_me->info, cep->value); 3432 } 3433 else if (!strcmp(cep->name, "sid")) 3434 { 3435 safe_strdup(conf_me->sid, cep->value); 3436 } 3437 } 3438 return 1; 3439 } 3440 3441 int _test_me(ConfigFile *conf, ConfigEntry *ce) 3442 { 3443 char has_name = 0, has_info = 0, has_sid = 0; 3444 ConfigEntry *cep; 3445 int errors = 0; 3446 3447 if (requiredstuff.conf_me) 3448 { 3449 config_warn_duplicate(ce->file->filename, ce->line_number, "me"); 3450 return 0; 3451 } 3452 3453 for (cep = ce->items; cep; cep = cep->next) 3454 { 3455 if (config_is_blankorempty(cep, "me")) 3456 continue; 3457 3458 /* me::name */ 3459 if (!strcmp(cep->name, "name")) 3460 { 3461 if (has_name) 3462 { 3463 config_warn_duplicate(cep->file->filename, 3464 cep->line_number, "me::name"); 3465 continue; 3466 } 3467 has_name = 1; 3468 if (!strchr(cep->value, '.')) 3469 { 3470 config_error("%s:%i: illegal me::name, must be fully qualified hostname", 3471 cep->file->filename, 3472 cep->line_number); 3473 errors++; 3474 } 3475 if (strlen(cep->value) > HOSTLEN) 3476 { 3477 config_error("%s:%i: illegal me::name, must be less or equal to %i characters", 3478 cep->file->filename, 3479 cep->line_number, HOSTLEN); 3480 errors++; 3481 } 3482 if (!valid_server_name(cep->value)) 3483 { 3484 config_error("%s:%i: illegal me::name contains invalid character(s) [only a-z, 0-9, _, -, . are allowed]", 3485 cep->file->filename, 3486 cep->line_number); 3487 errors++; 3488 } 3489 } 3490 /* me::info */ 3491 else if (!strcmp(cep->name, "info")) 3492 { 3493 char *p; 3494 char valid = 0; 3495 if (has_info) 3496 { 3497 config_warn_duplicate(cep->file->filename, 3498 cep->line_number, "me::info"); 3499 continue; 3500 } 3501 has_info = 1; 3502 if (strlen(cep->value) > (REALLEN-1)) 3503 { 3504 config_error("%s:%i: too long me::info, must be max. %i characters", 3505 cep->file->filename, cep->line_number, 3506 REALLEN-1); 3507 errors++; 3508 } 3509 3510 /* Valid me::info? Any data except spaces is ok */ 3511 for (p=cep->value; *p; p++) 3512 { 3513 if (*p != ' ') 3514 { 3515 valid = 1; 3516 break; 3517 } 3518 } 3519 if (!valid) 3520 { 3521 config_error("%s:%i: empty me::info, should be a server description.", 3522 cep->file->filename, cep->line_number); 3523 errors++; 3524 } 3525 } 3526 else if (!strcmp(cep->name, "numeric")) 3527 { 3528 config_error("%s:%i: me::numeric has been removed, you must now specify a Server ID (SID) instead. " 3529 "Edit your configuration file and change 'numeric' to 'sid' and make up " 3530 "a server id of exactly 3 characters, starting with a digit, eg: \"001\" or \"0AB\".", 3531 cep->file->filename, cep->line_number); 3532 errors++; 3533 } 3534 else if (!strcmp(cep->name, "sid")) 3535 { 3536 if (has_sid) 3537 { 3538 config_warn_duplicate(cep->file->filename, 3539 cep->line_number, "me::sid"); 3540 continue; 3541 } 3542 has_sid = 1; 3543 3544 if (!valid_sid(cep->value)) 3545 { 3546 config_error("%s:%i: me::sid must be 3 characters long, begin with a number, " 3547 "and the 2nd and 3rd character must be a number or uppercase letter. " 3548 "Example: \"001\" and \"0AB\" is good. \"AAA\" and \"0ab\" are bad. ", 3549 cep->file->filename, cep->line_number); 3550 errors++; 3551 } 3552 3553 if (!isdigit(*cep->value)) 3554 { 3555 config_error("%s:%i: me::sid must be 3 characters long and begin with a number", 3556 cep->file->filename, cep->line_number); 3557 errors++; 3558 } 3559 } 3560 /* Unknown entry */ 3561 else 3562 { 3563 config_error_unknown(ce->file->filename, ce->line_number, 3564 "me", cep->name); 3565 errors++; 3566 } 3567 } 3568 if (!has_name) 3569 { 3570 config_error_missing(ce->file->filename, ce->line_number, "me::name"); 3571 errors++; 3572 } 3573 if (!has_info) 3574 { 3575 config_error_missing(ce->file->filename, ce->line_number, "me::info"); 3576 errors++; 3577 } 3578 if (!has_sid) 3579 { 3580 config_error_missing(ce->file->filename, ce->line_number, "me::sid"); 3581 errors++; 3582 } 3583 requiredstuff.conf_me = 1; 3584 return errors; 3585 } 3586 3587 /* 3588 * The files {} block 3589 */ 3590 int _conf_files(ConfigFile *conf, ConfigEntry *ce) 3591 { 3592 ConfigEntry *cep; 3593 3594 if (!conf_files) 3595 { 3596 conf_files = safe_alloc(sizeof(ConfigItem_files)); 3597 3598 /* set defaults */ 3599 safe_strdup(conf_files->motd_file, MPATH); 3600 safe_strdup(conf_files->rules_file, RPATH); 3601 safe_strdup(conf_files->smotd_file, SMPATH); 3602 safe_strdup(conf_files->botmotd_file, BPATH); 3603 safe_strdup(conf_files->opermotd_file, OPATH); 3604 safe_strdup(conf_files->svsmotd_file, VPATH); 3605 3606 safe_strdup(conf_files->pid_file, IRCD_PIDFILE); 3607 safe_strdup(conf_files->tune_file, IRCDTUNE); 3608 3609 /* we let actual files get read in later by the motd caching mechanism */ 3610 } 3611 /* 3612 * hack to allow initialization of conf_files (above) when there is no files block in 3613 * CPATH. The caller calls _conf_files(NULL, NULL); to do this. We return here because 3614 * the for loop's initialization of cep would segfault otherwise. We return 1 because 3615 * if config_run_blocks() calls us with a NULL ce, it's got a bug...but we can't detect that. 3616 */ 3617 if (!ce) 3618 return 1; 3619 3620 for (cep = ce->items; cep; cep = cep->next) 3621 { 3622 if (!strcmp(cep->name, "motd")) 3623 safe_strdup(conf_files->motd_file, cep->value); 3624 else if (!strcmp(cep->name, "shortmotd")) 3625 safe_strdup(conf_files->smotd_file, cep->value); 3626 else if (!strcmp(cep->name, "opermotd")) 3627 safe_strdup(conf_files->opermotd_file, cep->value); 3628 else if (!strcmp(cep->name, "svsmotd")) 3629 safe_strdup(conf_files->svsmotd_file, cep->value); 3630 else if (!strcmp(cep->name, "botmotd")) 3631 safe_strdup(conf_files->botmotd_file, cep->value); 3632 else if (!strcmp(cep->name, "rules")) 3633 safe_strdup(conf_files->rules_file, cep->value); 3634 else if (!strcmp(cep->name, "tunefile")) 3635 safe_strdup(conf_files->tune_file, cep->value); 3636 else if (!strcmp(cep->name, "pidfile")) 3637 safe_strdup(conf_files->pid_file, cep->value); 3638 } 3639 return 1; 3640 } 3641 3642 int _test_files(ConfigFile *conf, ConfigEntry *ce) 3643 { 3644 ConfigEntry *cep; 3645 int errors = 0; 3646 char has_motd = 0, has_smotd = 0, has_rules = 0; 3647 char has_botmotd = 0, has_opermotd = 0, has_svsmotd = 0; 3648 char has_pidfile = 0, has_tunefile = 0; 3649 3650 for (cep = ce->items; cep; cep = cep->next) 3651 { 3652 /* files::motd */ 3653 if (!strcmp(cep->name, "motd")) 3654 { 3655 if (has_motd) 3656 { 3657 config_warn_duplicate(cep->file->filename, 3658 cep->line_number, "files::motd"); 3659 continue; 3660 } 3661 convert_to_absolute_path(&cep->value, CONFDIR); 3662 config_test_openfile(cep, O_RDONLY, 0, "files::motd", 0, 1); 3663 has_motd = 1; 3664 } 3665 /* files::smotd */ 3666 else if (!strcmp(cep->name, "shortmotd")) 3667 { 3668 if (has_smotd) 3669 { 3670 config_warn_duplicate(cep->file->filename, 3671 cep->line_number, "files::shortmotd"); 3672 continue; 3673 } 3674 convert_to_absolute_path(&cep->value, CONFDIR); 3675 config_test_openfile(cep, O_RDONLY, 0, "files::shortmotd", 0, 1); 3676 has_smotd = 1; 3677 } 3678 /* files::rules */ 3679 else if (!strcmp(cep->name, "rules")) 3680 { 3681 if (has_rules) 3682 { 3683 config_warn_duplicate(cep->file->filename, 3684 cep->line_number, "files::rules"); 3685 continue; 3686 } 3687 convert_to_absolute_path(&cep->value, CONFDIR); 3688 config_test_openfile(cep, O_RDONLY, 0, "files::rules", 0, 1); 3689 has_rules = 1; 3690 } 3691 /* files::botmotd */ 3692 else if (!strcmp(cep->name, "botmotd")) 3693 { 3694 if (has_botmotd) 3695 { 3696 config_warn_duplicate(cep->file->filename, 3697 cep->line_number, "files::botmotd"); 3698 continue; 3699 } 3700 convert_to_absolute_path(&cep->value, CONFDIR); 3701 config_test_openfile(cep, O_RDONLY, 0, "files::botmotd", 0, 1); 3702 has_botmotd = 1; 3703 } 3704 /* files::opermotd */ 3705 else if (!strcmp(cep->name, "opermotd")) 3706 { 3707 if (has_opermotd) 3708 { 3709 config_warn_duplicate(cep->file->filename, 3710 cep->line_number, "files::opermotd"); 3711 continue; 3712 } 3713 convert_to_absolute_path(&cep->value, CONFDIR); 3714 config_test_openfile(cep, O_RDONLY, 0, "files::opermotd", 0, 1); 3715 has_opermotd = 1; 3716 } 3717 /* files::svsmotd 3718 * This config stuff should somehow be inside of modules/svsmotd.c!!!... right? 3719 */ 3720 else if (!strcmp(cep->name, "svsmotd")) 3721 { 3722 if (has_svsmotd) 3723 { 3724 config_warn_duplicate(cep->file->filename, 3725 cep->line_number, "files::svsmotd"); 3726 continue; 3727 } 3728 convert_to_absolute_path(&cep->value, CONFDIR); 3729 /* svsmotd can't be a URL because we have to be able to write to it */ 3730 config_test_openfile(cep, O_RDONLY, 0, "files::svsmotd", 0, 0); 3731 has_svsmotd = 1; 3732 } 3733 /* files::pidfile */ 3734 else if (!strcmp(cep->name, "pidfile")) 3735 { 3736 if (has_pidfile) 3737 { 3738 config_warn_duplicate(cep->file->filename, 3739 cep->line_number, "files::pidfile"); 3740 continue; 3741 } 3742 convert_to_absolute_path(&cep->value, PERMDATADIR); 3743 errors += config_test_openfile(cep, O_WRONLY | O_CREAT, 0600, "files::pidfile", 1, 0); 3744 has_pidfile = 1; 3745 } 3746 /* files::tunefile */ 3747 else if (!strcmp(cep->name, "tunefile")) 3748 { 3749 if (has_tunefile) 3750 { 3751 config_warn_duplicate(cep->file->filename, 3752 cep->line_number, "files::tunefile"); 3753 continue; 3754 } 3755 convert_to_absolute_path(&cep->value, PERMDATADIR); 3756 errors += config_test_openfile(cep, O_RDWR | O_CREAT, 0600, "files::tunefile", 1, 0); 3757 has_tunefile = 1; 3758 } 3759 /* <random directive here> */ 3760 else 3761 { 3762 config_error("%s:%d: Unknown directive: \"%s\" in files {}", cep->file->filename, 3763 cep->line_number, cep->name); 3764 errors ++; 3765 } 3766 } 3767 return errors; 3768 } 3769 3770 /* 3771 * The operclass {} block parser 3772 */ 3773 3774 OperClassACLEntry* _conf_parseACLEntry(ConfigEntry *ce) 3775 { 3776 ConfigEntry *cep; 3777 OperClassACLEntry *entry = NULL; 3778 entry = safe_alloc(sizeof(OperClassACLEntry)); 3779 3780 if (!strcmp(ce->name,"allow")) 3781 entry->type = OPERCLASSENTRY_ALLOW; 3782 else 3783 entry->type = OPERCLASSENTRY_DENY; 3784 3785 for (cep = ce->items; cep; cep = cep->next) 3786 { 3787 OperClassACLEntryVar *var = safe_alloc(sizeof(OperClassACLEntryVar)); 3788 safe_strdup(var->name, cep->name); 3789 if (cep->value) 3790 { 3791 safe_strdup(var->value, cep->value); 3792 } 3793 AddListItem(var,entry->variables); 3794 } 3795 3796 return entry; 3797 } 3798 3799 OperClassACL* _conf_parseACL(const char *name, ConfigEntry *ce) 3800 { 3801 ConfigEntry *cep; 3802 OperClassACL *acl = NULL; 3803 3804 acl = safe_alloc(sizeof(OperClassACL)); 3805 safe_strdup(acl->name, name); 3806 3807 for (cep = ce->items; cep; cep = cep->next) 3808 { 3809 if (!strcmp(cep->name, "deny") || !strcmp(cep->name, "allow")) 3810 { 3811 OperClassACLEntry *entry = _conf_parseACLEntry(cep); 3812 AddListItem(entry,acl->entries); 3813 } 3814 else { 3815 OperClassACL *subAcl = _conf_parseACL(cep->name,cep); 3816 AddListItem(subAcl,acl->acls); 3817 } 3818 } 3819 3820 return acl; 3821 } 3822 3823 int _conf_operclass(ConfigFile *conf, ConfigEntry *ce) 3824 { 3825 ConfigEntry *cep; 3826 ConfigEntry *cepp; 3827 ConfigItem_operclass *operClass = NULL; 3828 operClass = safe_alloc(sizeof(ConfigItem_operclass)); 3829 operClass->classStruct = safe_alloc(sizeof(OperClass)); 3830 safe_strdup(operClass->classStruct->name, ce->value); 3831 3832 for (cep = ce->items; cep; cep = cep->next) 3833 { 3834 if (!strcmp(cep->name, "parent")) 3835 { 3836 safe_strdup(operClass->classStruct->ISA, cep->value); 3837 } 3838 else if (!strcmp(cep->name, "permissions")) 3839 { 3840 for (cepp = cep->items; cepp; cepp = cepp->next) 3841 { 3842 OperClassACL *acl = _conf_parseACL(cepp->name,cepp); 3843 AddListItem(acl,operClass->classStruct->acls); 3844 } 3845 } 3846 } 3847 3848 AddListItem(operClass, conf_operclass); 3849 return 1; 3850 } 3851 3852 void new_permissions_system(ConfigFile *conf, ConfigEntry *ce) 3853 { 3854 if (need_operclass_permissions_upgrade) 3855 return; /* error already shown */ 3856 3857 config_error("%s:%i: UnrealIRCd 4.2.1 and higher have a new operclass permissions system.", 3858 ce->file->filename, ce->line_number); 3859 config_error("Please see https://www.unrealircd.org/docs/FAQ#New_operclass_permissions"); 3860 config_error("(additional errors regarding this are suppressed)"); 3861 /* 3862 config_error("First of all, operclass::privileges has been renamed to operclass::permissions."); 3863 config_error("However, the permissions themselves have also been changed. You cannot simply " 3864 "rename 'privileges' to 'permissions' and be done with it! "); 3865 config_error("See https://www.unrealircd.org/docs/Operclass_permissions for the new list of permissions."); 3866 config_error("Or just use the default operclasses from operclass.default.conf, then no need to change anything."); */ 3867 need_operclass_permissions_upgrade = 1; 3868 } 3869 3870 int _test_operclass(ConfigFile *conf, ConfigEntry *ce) 3871 { 3872 char has_permissions = 0, has_parent = 0; 3873 ConfigEntry *cep; 3874 int errors = 0; 3875 3876 if (!ce->value) 3877 { 3878 config_error_noname(ce->file->filename, ce->line_number, "operclass"); 3879 errors++; 3880 } 3881 for (cep = ce->items; cep; cep = cep->next) 3882 { 3883 if (!strcmp(cep->name, "parent")) 3884 { 3885 if (has_parent) 3886 { 3887 config_warn_duplicate(cep->file->filename, 3888 cep->line_number, "operclass::parent"); 3889 continue; 3890 } 3891 has_parent = 1; 3892 continue; 3893 } else 3894 if (!strcmp(cep->name, "permissions")) 3895 { 3896 if (has_permissions) 3897 { 3898 config_warn_duplicate(cep->file->filename, 3899 cep->line_number, "oper::permissions"); 3900 continue; 3901 } 3902 has_permissions = 1; 3903 continue; 3904 } else 3905 if (!strcmp(cep->name, "privileges")) 3906 { 3907 new_permissions_system(conf, cep); 3908 errors++; 3909 return errors; 3910 } else 3911 { 3912 config_error_unknown(cep->file->filename, 3913 cep->line_number, "operclass", cep->name); 3914 errors++; 3915 continue; 3916 } 3917 } 3918 3919 if (!has_permissions) 3920 { 3921 config_error_missing(ce->file->filename, ce->line_number, 3922 "oper::permissions"); 3923 errors++; 3924 } 3925 3926 return errors; 3927 } 3928 3929 /* 3930 * The oper {} block parser 3931 */ 3932 3933 int _conf_oper(ConfigFile *conf, ConfigEntry *ce) 3934 { 3935 ConfigEntry *cep; 3936 ConfigEntry *cepp; 3937 ConfigItem_oper *oper = NULL; 3938 3939 oper = safe_alloc(sizeof(ConfigItem_oper)); 3940 safe_strdup(oper->name, ce->value); 3941 oper->match = safe_alloc(sizeof(SecurityGroup)); 3942 3943 /* Inherit some defaults: */ 3944 oper->server_notice_colors = tempiConf.server_notice_colors; 3945 oper->server_notice_show_event = tempiConf.server_notice_show_event; 3946 3947 for (cep = ce->items; cep; cep = cep->next) 3948 { 3949 if (!strcmp(cep->name, "operclass")) 3950 safe_strdup(oper->operclass, cep->value); 3951 if (!strcmp(cep->name, "password")) 3952 oper->auth = AuthBlockToAuthConfig(cep); 3953 else if (!strcmp(cep->name, "class")) 3954 { 3955 oper->class = find_class(cep->value); 3956 if (!oper->class || (oper->class->flag.temporary == 1)) 3957 { 3958 config_status("%s:%i: illegal oper::class, unknown class '%s' using default of class 'default'", 3959 cep->file->filename, cep->line_number, 3960 cep->value); 3961 oper->class = default_class; 3962 } 3963 } 3964 else if (!strcmp(cep->name, "swhois")) 3965 { 3966 SWhois *s; 3967 if (cep->items) 3968 { 3969 for (cepp = cep->items; cepp; cepp = cepp->next) 3970 { 3971 s = safe_alloc(sizeof(SWhois)); 3972 safe_strdup(s->line, cepp->name); 3973 safe_strdup(s->setby, "oper"); 3974 AddListItem(s, oper->swhois); 3975 } 3976 } else 3977 if (cep->value) 3978 { 3979 s = safe_alloc(sizeof(SWhois)); 3980 safe_strdup(s->line, cep->value); 3981 safe_strdup(s->setby, "oper"); 3982 AddListItem(s, oper->swhois); 3983 } 3984 } 3985 else if (!strcmp(cep->name, "snomask")) 3986 { 3987 safe_strdup(oper->snomask, cep->value); 3988 } 3989 else if (!strcmp(cep->name, "server-notice-colors")) 3990 { 3991 oper->server_notice_colors = config_checkval(cep->value, CFG_YESNO); 3992 } 3993 else if (!strcmp(cep->name, "server-notice-show-event")) 3994 { 3995 oper->server_notice_show_event = config_checkval(cep->value, CFG_YESNO); 3996 } 3997 else if (!strcmp(cep->name, "auto-login")) 3998 { 3999 oper->auto_login = config_checkval(cep->value, CFG_YESNO); 4000 } 4001 else if (!strcmp(cep->name, "modes")) 4002 { 4003 oper->modes = set_usermode(cep->value); 4004 } 4005 else if (!strcmp(cep->name, "require-modes")) 4006 { 4007 oper->require_modes = set_usermode(cep->value); 4008 } 4009 else if (!strcmp(cep->name, "maxlogins")) 4010 { 4011 oper->maxlogins = atoi(cep->value); 4012 } 4013 else if (!strcmp(cep->name, "mask") || !strcmp(cep->name, "match")) 4014 { 4015 conf_match_block(conf, cep, &oper->match); 4016 } 4017 else if (!strcmp(cep->name, "vhost")) 4018 { 4019 safe_strdup(oper->vhost, cep->value); 4020 } 4021 } 4022 AddListItem(oper, conf_oper); 4023 return 1; 4024 } 4025 4026 int _test_oper(ConfigFile *conf, ConfigEntry *ce) 4027 { 4028 char has_class = 0, has_password = 0, has_snomask = 0; 4029 char has_modes = 0, has_require_modes = 0, has_mask = 0, has_match = 0, has_broad_match = 0; 4030 char has_maxlogins = 0, has_operclass = 0, has_vhost = 0, has_auto_login = 0; 4031 ConfigEntry *cep; 4032 int errors = 0; 4033 4034 if (!ce->value) 4035 { 4036 config_error_noname(ce->file->filename, ce->line_number, "oper"); 4037 errors++; 4038 } 4039 for (cep = ce->items; cep; cep = cep->next) 4040 { 4041 /* Regular variables */ 4042 if (!cep->items) 4043 { 4044 if (config_is_blankorempty(cep, "oper")) 4045 { 4046 errors++; 4047 continue; 4048 } 4049 /* oper::password */ 4050 if (!strcmp(cep->name, "password")) 4051 { 4052 if (has_password) 4053 { 4054 config_warn_duplicate(cep->file->filename, 4055 cep->line_number, "oper::password"); 4056 continue; 4057 } 4058 has_password = 1; 4059 if (Auth_CheckError(cep) < 0) 4060 errors++; 4061 4062 if (ce->value && cep->value && 4063 !strcmp(ce->value, "bobsmith") && 4064 !strcmp(cep->value, "test")) 4065 { 4066 config_error("%s:%i: please change the the name and password of the " 4067 "default 'bobsmith' oper block", 4068 ce->file->filename, ce->line_number); 4069 errors++; 4070 } 4071 continue; 4072 } 4073 /* oper::operclass */ 4074 else if (!strcmp(cep->name, "operclass")) 4075 { 4076 if (has_operclass) 4077 { 4078 config_warn_duplicate(cep->file->filename, 4079 cep->line_number, "oper::operclass"); 4080 continue; 4081 } 4082 has_operclass = 1; 4083 continue; 4084 } 4085 /* oper::class */ 4086 else if (!strcmp(cep->name, "class")) 4087 { 4088 if (has_class) 4089 { 4090 config_warn_duplicate(cep->file->filename, 4091 cep->line_number, "oper::class"); 4092 continue; 4093 } 4094 has_class = 1; 4095 } 4096 /* oper::swhois */ 4097 else if (!strcmp(cep->name, "swhois")) 4098 { 4099 } 4100 /* oper::vhost */ 4101 else if (!strcmp(cep->name, "vhost")) 4102 { 4103 if (has_vhost) 4104 { 4105 config_warn_duplicate(cep->file->filename, 4106 cep->line_number, "oper::vhost"); 4107 continue; 4108 } 4109 if (!valid_vhost(cep->value)) 4110 { 4111 config_error("%s:%i: oper::vhost contains illegal characters or is too long: '%s'", 4112 cep->file->filename, cep->line_number, cep->value); 4113 errors++; 4114 } 4115 has_vhost = 1; 4116 } 4117 /* oper::snomask */ 4118 else if (!strcmp(cep->name, "snomask")) 4119 { 4120 char *wrong_snomask; 4121 if (has_snomask) 4122 { 4123 config_warn_duplicate(cep->file->filename, 4124 cep->line_number, "oper::snomask"); 4125 continue; 4126 } 4127 if (!is_valid_snomask_string_testing(cep->value, &wrong_snomask)) 4128 { 4129 config_error("%s:%i: oper::snomask contains unknown snomask letter(s) '%s'", 4130 cep->file->filename, cep->line_number, wrong_snomask); 4131 errors++; 4132 invalid_snomasks_encountered++; 4133 } 4134 has_snomask = 1; 4135 } 4136 else if (!strcmp(cep->name, "server-notice-colors")) 4137 { 4138 } 4139 else if (!strcmp(cep->name, "server-notice-show-event")) 4140 { 4141 } 4142 else if (!strcmp(cep->name, "auto-login")) 4143 { 4144 has_auto_login = config_checkval(cep->value, CFG_YESNO); 4145 } 4146 /* oper::modes */ 4147 else if (!strcmp(cep->name, "modes")) 4148 { 4149 char *p; 4150 for (p = cep->value; *p; p++) 4151 if (strchr("orzS", *p)) 4152 { 4153 config_error("%s:%i: oper::modes may not include mode '%c'", 4154 cep->file->filename, cep->line_number, *p); 4155 errors++; 4156 } 4157 if (has_modes) 4158 { 4159 config_warn_duplicate(cep->file->filename, 4160 cep->line_number, "oper::modes"); 4161 continue; 4162 } 4163 has_modes = 1; 4164 } 4165 /* oper::require-modes */ 4166 else if (!strcmp(cep->name, "require-modes")) 4167 { 4168 char *p; 4169 for (p = cep->value; *p; p++) 4170 if (strchr("o", *p)) 4171 { 4172 config_warn("%s:%i: oper::require-modes probably shouldn't include mode '%c'", 4173 cep->file->filename, cep->line_number, *p); 4174 } 4175 if (has_require_modes) 4176 { 4177 config_warn_duplicate(cep->file->filename, 4178 cep->line_number, "oper::require-modes"); 4179 continue; 4180 } 4181 has_require_modes = 1; 4182 } 4183 /* oper::maxlogins */ 4184 else if (!strcmp(cep->name, "maxlogins")) 4185 { 4186 int l; 4187 4188 if (has_maxlogins) 4189 { 4190 config_warn_duplicate(cep->file->filename, 4191 cep->line_number, "oper::maxlogins"); 4192 continue; 4193 } 4194 has_maxlogins = 1; 4195 4196 l = atoi(cep->value); 4197 if ((l < 0) || (l > 5000)) 4198 { 4199 config_error("%s:%i: oper::maxlogins: value out of range (%d) should be 0-5000", 4200 cep->file->filename, cep->line_number, l); 4201 errors++; 4202 continue; 4203 } 4204 } 4205 else if (!strcmp(cep->name, "mask")) 4206 { 4207 if (cep->value || cep->items) 4208 { 4209 has_mask = 1; 4210 test_match_block(conf, cep, &errors); 4211 if (test_match_block_too_broad(conf, cep)) 4212 has_broad_match = 1; 4213 } 4214 } 4215 else if (!strcmp(cep->name, "match")) 4216 { 4217 if (cep->value || cep->items) 4218 { 4219 has_match = 1; 4220 test_match_block(conf, cep, &errors); 4221 if (test_match_block_too_broad(conf, cep)) 4222 has_broad_match = 1; 4223 } 4224 } 4225 else 4226 { 4227 config_error_unknown(cep->file->filename, 4228 cep->line_number, "oper", cep->name); 4229 errors++; 4230 continue; 4231 } 4232 } 4233 /* Sections */ 4234 else 4235 { 4236 if (!strcmp(cep->name, "swhois")) 4237 { 4238 /* ok */ 4239 } 4240 else if (!strcmp(cep->name, "mask")) 4241 { 4242 if (cep->value || cep->items) 4243 { 4244 has_mask = 1; 4245 test_match_block(conf, cep, &errors); 4246 if (test_match_block_too_broad(conf, cep)) 4247 has_broad_match = 1; 4248 } 4249 } 4250 else if (!strcmp(cep->name, "match")) 4251 { 4252 if (cep->value || cep->items) 4253 { 4254 has_match = 1; 4255 test_match_block(conf, cep, &errors); 4256 if (test_match_block_too_broad(conf, cep)) 4257 has_broad_match = 1; 4258 } 4259 } 4260 else if (!strcmp(cep->name, "password")) 4261 { 4262 if (has_password) 4263 { 4264 config_warn_duplicate(cep->file->filename, 4265 cep->line_number, "oper::password"); 4266 continue; 4267 } 4268 has_password = 1; 4269 if (Auth_CheckError(cep) < 0) 4270 errors++; 4271 } 4272 else 4273 { 4274 config_error_unknown(cep->file->filename, 4275 cep->line_number, "oper", cep->name); 4276 errors++; 4277 continue; 4278 } 4279 } 4280 } 4281 4282 if (has_auto_login && has_broad_match) 4283 { 4284 config_error("%s:%i: your oper block for '%s' has auto-login but is completely unrestricted (mask *@*)!", 4285 ce->file->filename, ce->line_number, ce->value); 4286 errors++; 4287 } else 4288 if (!has_password && has_broad_match) 4289 { 4290 config_error("%s:%i: your oper block for '%s' has no password and is completely unrestricted (mask *@*)!", 4291 ce->file->filename, ce->line_number, ce->value); 4292 errors++; 4293 } 4294 4295 /* The rest should NOT be in an 'else'... */ 4296 if (has_password && has_auto_login) 4297 { 4298 config_error("%s:%i: You have auto-login enabled for your oper block '%s' but you also have a password set. " 4299 "Remove the password if you want to use auto-login.", 4300 ce->file->filename, ce->line_number, ce->value); 4301 errors++; 4302 } 4303 if (!has_mask && !has_match) 4304 { 4305 config_error_missing(ce->file->filename, ce->line_number, 4306 "oper::match"); 4307 errors++; 4308 } 4309 if (has_mask && has_match) 4310 { 4311 config_error("%s:%d: You cannot have both ::mask and ::match. " 4312 "You should only use oper::match.", 4313 ce->file->filename, ce->line_number); 4314 errors++; 4315 } 4316 if (!has_class) 4317 { 4318 config_error_missing(ce->file->filename, ce->line_number, 4319 "oper::class"); 4320 errors++; 4321 } 4322 if (!has_operclass) 4323 { 4324 config_error_missing(ce->file->filename, ce->line_number, 4325 "oper::operclass"); 4326 errors++; 4327 } 4328 4329 return errors; 4330 4331 } 4332 4333 /* 4334 * The class {} block parser 4335 */ 4336 int _conf_class(ConfigFile *conf, ConfigEntry *ce) 4337 { 4338 ConfigEntry *cep, *cep2; 4339 ConfigItem_class *class; 4340 unsigned char isnew = 0; 4341 4342 if (!(class = find_class(ce->value))) 4343 { 4344 class = safe_alloc(sizeof(ConfigItem_class)); 4345 safe_strdup(class->name, ce->value); 4346 isnew = 1; 4347 } 4348 else 4349 { 4350 isnew = 0; 4351 class->flag.temporary = 0; 4352 class->options = 0; /* RESET OPTIONS */ 4353 } 4354 safe_strdup(class->name, ce->value); 4355 4356 class->connfreq = 15; /* default */ 4357 4358 for (cep = ce->items; cep; cep = cep->next) 4359 { 4360 if (!strcmp(cep->name, "pingfreq")) 4361 class->pingfreq = config_checkval(cep->value,CFG_TIME); 4362 else if (!strcmp(cep->name, "connfreq")) 4363 class->connfreq = config_checkval(cep->value,CFG_TIME); 4364 else if (!strcmp(cep->name, "maxclients")) 4365 class->maxclients = atol(cep->value); 4366 else if (!strcmp(cep->name, "sendq")) 4367 class->sendq = config_checkval(cep->value,CFG_SIZE); 4368 else if (!strcmp(cep->name, "recvq")) 4369 class->recvq = config_checkval(cep->value,CFG_SIZE); 4370 else if (!strcmp(cep->name, "options")) 4371 { 4372 for (cep2 = cep->items; cep2; cep2 = cep2->next) 4373 if (!strcmp(cep2->name, "nofakelag")) 4374 class->options |= CLASS_OPT_NOFAKELAG; 4375 } 4376 } 4377 if (isnew) 4378 AddListItem(class, conf_class); 4379 return 1; 4380 } 4381 4382 int _test_class(ConfigFile *conf, ConfigEntry *ce) 4383 { 4384 ConfigEntry *cep, *cep2; 4385 int errors = 0; 4386 char has_pingfreq = 0, has_connfreq = 0, has_maxclients = 0, has_sendq = 0; 4387 char has_recvq = 0; 4388 4389 if (!ce->value) 4390 { 4391 config_error_noname(ce->file->filename, ce->line_number, "class"); 4392 return 1; 4393 } 4394 if (!strcasecmp(ce->value, "default")) 4395 { 4396 config_error("%s:%d: Class cannot be named 'default', this class name is reserved for internal use.", 4397 ce->file->filename, ce->line_number); 4398 errors++; 4399 } 4400 4401 for (cep = ce->items; cep; cep = cep->next) 4402 { 4403 if (!strcmp(cep->name, "options")) 4404 { 4405 for (cep2 = cep->items; cep2; cep2 = cep2->next) 4406 { 4407 #ifdef FAKELAG_CONFIGURABLE 4408 if (!strcmp(cep2->name, "nofakelag")) 4409 ; 4410 else 4411 #endif 4412 { 4413 config_error("%s:%d: Unknown option '%s' in class::options", 4414 cep2->file->filename, cep2->line_number, cep2->name); 4415 errors++; 4416 } 4417 } 4418 } 4419 else if (config_is_blankorempty(cep, "class")) 4420 { 4421 errors++; 4422 continue; 4423 } 4424 /* class::pingfreq */ 4425 else if (!strcmp(cep->name, "pingfreq")) 4426 { 4427 int v = config_checkval(cep->value,CFG_TIME); 4428 if (has_pingfreq) 4429 { 4430 config_warn_duplicate(cep->file->filename, 4431 cep->line_number, "class::pingfreq"); 4432 continue; 4433 } 4434 has_pingfreq = 1; 4435 if ((v < 30) || (v > 600)) 4436 { 4437 config_error("%s:%i: class::pingfreq should be a reasonable value (30-600)", 4438 cep->file->filename, cep->line_number); 4439 errors++; 4440 continue; 4441 } 4442 } 4443 /* class::maxclients */ 4444 else if (!strcmp(cep->name, "maxclients")) 4445 { 4446 long l; 4447 if (has_maxclients) 4448 { 4449 config_warn_duplicate(cep->file->filename, 4450 cep->line_number, "class::maxclients"); 4451 continue; 4452 } 4453 has_maxclients = 1; 4454 l = atol(cep->value); 4455 if ((l < 1) || (l > 1000000)) 4456 { 4457 config_error("%s:%i: class::maxclients with illegal value", 4458 cep->file->filename, cep->line_number); 4459 errors++; 4460 } 4461 } 4462 /* class::connfreq */ 4463 else if (!strcmp(cep->name, "connfreq")) 4464 { 4465 long l; 4466 if (has_connfreq) 4467 { 4468 config_warn_duplicate(cep->file->filename, 4469 cep->line_number, "class::connfreq"); 4470 continue; 4471 } 4472 has_connfreq = 1; 4473 l = config_checkval(cep->value,CFG_TIME); 4474 if ((l < 5) || (l > 604800)) 4475 { 4476 config_error("%s:%i: class::connfreq with illegal value (must be >5 and <7d)", 4477 cep->file->filename, cep->line_number); 4478 errors++; 4479 } 4480 } 4481 /* class::sendq */ 4482 else if (!strcmp(cep->name, "sendq")) 4483 { 4484 long l; 4485 if (has_sendq) 4486 { 4487 config_warn_duplicate(cep->file->filename, 4488 cep->line_number, "class::sendq"); 4489 continue; 4490 } 4491 has_sendq = 1; 4492 l = config_checkval(cep->value,CFG_SIZE); 4493 if ((l <= 0) || (l > 2000000000)) 4494 { 4495 config_error("%s:%i: class::sendq with illegal value", 4496 cep->file->filename, cep->line_number); 4497 errors++; 4498 } 4499 } 4500 /* class::recvq */ 4501 else if (!strcmp(cep->name, "recvq")) 4502 { 4503 long l; 4504 if (has_recvq) 4505 { 4506 config_warn_duplicate(cep->file->filename, 4507 cep->line_number, "class::recvq"); 4508 continue; 4509 } 4510 has_recvq = 1; 4511 l = config_checkval(cep->value,CFG_SIZE); 4512 if ((l < 512) || (l > 32768)) 4513 { 4514 config_error("%s:%i: class::recvq with illegal value (must be >512 and <32k)", 4515 cep->file->filename, cep->line_number); 4516 errors++; 4517 } 4518 } 4519 /* Unknown */ 4520 else 4521 { 4522 config_error_unknown(cep->file->filename, cep->line_number, 4523 "class", cep->name); 4524 errors++; 4525 continue; 4526 } 4527 } 4528 if (!has_pingfreq) 4529 { 4530 config_error_missing(ce->file->filename, ce->line_number, 4531 "class::pingfreq"); 4532 errors++; 4533 } 4534 if (!has_maxclients) 4535 { 4536 config_error_missing(ce->file->filename, ce->line_number, 4537 "class::maxclients"); 4538 errors++; 4539 } 4540 if (!has_sendq) 4541 { 4542 config_error_missing(ce->file->filename, ce->line_number, 4543 "class::sendq"); 4544 errors++; 4545 } 4546 4547 return errors; 4548 } 4549 4550 int _conf_drpass(ConfigFile *conf, ConfigEntry *ce) 4551 { 4552 ConfigEntry *cep; 4553 4554 if (!conf_drpass) 4555 { 4556 conf_drpass = safe_alloc(sizeof(ConfigItem_drpass)); 4557 } 4558 4559 for (cep = ce->items; cep; cep = cep->next) 4560 { 4561 if (!strcmp(cep->name, "restart")) 4562 { 4563 if (conf_drpass->restartauth) 4564 Auth_FreeAuthConfig(conf_drpass->restartauth); 4565 4566 conf_drpass->restartauth = AuthBlockToAuthConfig(cep); 4567 } 4568 else if (!strcmp(cep->name, "die")) 4569 { 4570 if (conf_drpass->dieauth) 4571 Auth_FreeAuthConfig(conf_drpass->dieauth); 4572 4573 conf_drpass->dieauth = AuthBlockToAuthConfig(cep); 4574 } 4575 } 4576 return 1; 4577 } 4578 4579 int _test_drpass(ConfigFile *conf, ConfigEntry *ce) 4580 { 4581 ConfigEntry *cep; 4582 int errors = 0; 4583 char has_restart = 0, has_die = 0; 4584 4585 for (cep = ce->items; cep; cep = cep->next) 4586 { 4587 if (config_is_blankorempty(cep, "drpass")) 4588 { 4589 errors++; 4590 continue; 4591 } 4592 /* drpass::restart */ 4593 if (!strcmp(cep->name, "restart")) 4594 { 4595 if (has_restart) 4596 { 4597 config_warn_duplicate(cep->file->filename, 4598 cep->line_number, "drpass::restart"); 4599 continue; 4600 } 4601 has_restart = 1; 4602 if (Auth_CheckError(cep) < 0) 4603 errors++; 4604 continue; 4605 } 4606 /* drpass::die */ 4607 else if (!strcmp(cep->name, "die")) 4608 { 4609 if (has_die) 4610 { 4611 config_warn_duplicate(cep->file->filename, 4612 cep->line_number, "drpass::die"); 4613 continue; 4614 } 4615 has_die = 1; 4616 if (Auth_CheckError(cep) < 0) 4617 errors++; 4618 continue; 4619 } 4620 /* Unknown */ 4621 else 4622 { 4623 config_error_unknown(cep->file->filename, cep->line_number, 4624 "drpass", cep->name); 4625 errors++; 4626 continue; 4627 } 4628 } 4629 return errors; 4630 } 4631 4632 /* 4633 * The ulines {} block parser 4634 */ 4635 int _conf_ulines(ConfigFile *conf, ConfigEntry *ce) 4636 { 4637 ConfigEntry *cep; 4638 ConfigItem_ulines *ca; 4639 4640 for (cep = ce->items; cep; cep = cep->next) 4641 { 4642 ca = safe_alloc(sizeof(ConfigItem_ulines)); 4643 safe_strdup(ca->servername, cep->name); 4644 AddListItem(ca, conf_ulines); 4645 } 4646 return 1; 4647 } 4648 4649 int _test_ulines(ConfigFile *conf, ConfigEntry *ce) 4650 { 4651 /* No check needed */ 4652 return 0; 4653 } 4654 4655 int _conf_tld(ConfigFile *conf, ConfigEntry *ce) 4656 { 4657 ConfigEntry *cep; 4658 ConfigItem_tld *ca; 4659 4660 ca = safe_alloc(sizeof(ConfigItem_tld)); 4661 4662 for (cep = ce->items; cep; cep = cep->next) 4663 { 4664 if (!strcmp(cep->name, "match") || !strcmp(cep->name, "mask")) 4665 conf_match_block(conf, cep, &ca->match); 4666 else if (!strcmp(cep->name, "motd")) 4667 { 4668 safe_strdup(ca->motd_file, cep->value); 4669 read_motd(cep->value, &ca->motd); 4670 } 4671 else if (!strcmp(cep->name, "shortmotd")) 4672 { 4673 safe_strdup(ca->smotd_file, cep->value); 4674 read_motd(cep->value, &ca->smotd); 4675 } 4676 else if (!strcmp(cep->name, "opermotd")) 4677 { 4678 safe_strdup(ca->opermotd_file, cep->value); 4679 read_motd(cep->value, &ca->opermotd); 4680 } 4681 else if (!strcmp(cep->name, "botmotd")) 4682 { 4683 safe_strdup(ca->botmotd_file, cep->value); 4684 read_motd(cep->value, &ca->botmotd); 4685 } 4686 else if (!strcmp(cep->name, "rules")) 4687 { 4688 safe_strdup(ca->rules_file, cep->value); 4689 read_motd(cep->value, &ca->rules); 4690 } 4691 else if (!strcmp(cep->name, "options")) 4692 { 4693 ConfigEntry *cepp; 4694 for (cepp = cep->items; cepp; cepp = cepp->next) 4695 { 4696 if (!strcmp(cepp->name, "ssl") || !strcmp(cepp->name, "tls")) 4697 ca->options |= TLD_TLS; 4698 else if (!strcmp(cepp->name, "remote")) 4699 ca->options |= TLD_REMOTE; 4700 } 4701 } 4702 else if (!strcmp(cep->name, "channel")) 4703 safe_strdup(ca->channel, cep->value); 4704 } 4705 AddListItem(ca, conf_tld); 4706 return 1; 4707 } 4708 4709 int _test_tld(ConfigFile *conf, ConfigEntry *ce) 4710 { 4711 ConfigEntry *cep; 4712 int errors = 0; 4713 int fd = -1; 4714 char has_mask = 0, has_match = 0, has_motd = 0, has_rules = 0, has_shortmotd = 0; 4715 char has_channel = 0, has_opermotd = 0, has_botmotd = 0, has_options = 0; 4716 4717 for (cep = ce->items; cep; cep = cep->next) 4718 { 4719 if (!cep->value && strcmp(cep->name, "options") && strcmp(cep->name, "mask") && strcmp(cep->name, "match")) 4720 { 4721 config_error_empty(cep->file->filename, cep->line_number, 4722 "tld", cep->name); 4723 errors++; 4724 continue; 4725 } 4726 /* tld::mask */ 4727 if (!strcmp(cep->name, "mask")) 4728 { 4729 if (cep->value || cep->items) 4730 { 4731 has_mask = 1; 4732 test_match_block(conf, cep, &errors); 4733 } 4734 } 4735 else if (!strcmp(cep->name, "match")) 4736 { 4737 if (cep->value || cep->items) 4738 { 4739 has_match = 1; 4740 test_match_block(conf, cep, &errors); 4741 } 4742 } 4743 /* tld::motd */ 4744 else if (!strcmp(cep->name, "motd")) 4745 { 4746 if (has_motd) 4747 { 4748 config_warn_duplicate(cep->file->filename, 4749 cep->line_number, "tld::motd"); 4750 continue; 4751 } 4752 has_motd = 1; 4753 convert_to_absolute_path(&cep->value, CONFDIR); 4754 if (((fd = open(cep->value, O_RDONLY)) == -1)) 4755 { 4756 config_error("%s:%i: tld::motd: %s: %s", 4757 cep->file->filename, cep->line_number, 4758 cep->value, strerror(errno)); 4759 errors++; 4760 } 4761 else 4762 close(fd); 4763 } 4764 /* tld::rules */ 4765 else if (!strcmp(cep->name, "rules")) 4766 { 4767 if (has_rules) 4768 { 4769 config_warn_duplicate(cep->file->filename, 4770 cep->line_number, "tld::rules"); 4771 continue; 4772 } 4773 has_rules = 1; 4774 convert_to_absolute_path(&cep->value, CONFDIR); 4775 if (((fd = open(cep->value, O_RDONLY)) == -1)) 4776 { 4777 config_error("%s:%i: tld::rules: %s: %s", 4778 cep->file->filename, cep->line_number, 4779 cep->value, strerror(errno)); 4780 errors++; 4781 } 4782 else 4783 close(fd); 4784 } 4785 /* tld::channel */ 4786 else if (!strcmp(cep->name, "channel")) 4787 { 4788 if (has_channel) 4789 { 4790 config_warn_duplicate(cep->file->filename, 4791 cep->line_number, "tld::channel"); 4792 continue; 4793 } 4794 has_channel = 1; 4795 } 4796 /* tld::shortmotd */ 4797 else if (!strcmp(cep->name, "shortmotd")) 4798 { 4799 if (has_shortmotd) 4800 { 4801 config_warn_duplicate(cep->file->filename, 4802 cep->line_number, "tld::shortmotd"); 4803 continue; 4804 } 4805 has_shortmotd = 1; 4806 convert_to_absolute_path(&cep->value, CONFDIR); 4807 if (((fd = open(cep->value, O_RDONLY)) == -1)) 4808 { 4809 config_error("%s:%i: tld::shortmotd: %s: %s", 4810 cep->file->filename, cep->line_number, 4811 cep->value, strerror(errno)); 4812 errors++; 4813 } 4814 else 4815 close(fd); 4816 } 4817 /* tld::opermotd */ 4818 else if (!strcmp(cep->name, "opermotd")) 4819 { 4820 if (has_opermotd) 4821 { 4822 config_warn_duplicate(cep->file->filename, 4823 cep->line_number, "tld::opermotd"); 4824 continue; 4825 } 4826 has_opermotd = 1; 4827 convert_to_absolute_path(&cep->value, CONFDIR); 4828 if (((fd = open(cep->value, O_RDONLY)) == -1)) 4829 { 4830 config_error("%s:%i: tld::opermotd: %s: %s", 4831 cep->file->filename, cep->line_number, 4832 cep->value, strerror(errno)); 4833 errors++; 4834 } 4835 else 4836 close(fd); 4837 } 4838 /* tld::botmotd */ 4839 else if (!strcmp(cep->name, "botmotd")) 4840 { 4841 if (has_botmotd) 4842 { 4843 config_warn_duplicate(cep->file->filename, 4844 cep->line_number, "tld::botmotd"); 4845 continue; 4846 } 4847 has_botmotd = 1; 4848 convert_to_absolute_path(&cep->value, CONFDIR); 4849 if (((fd = open(cep->value, O_RDONLY)) == -1)) 4850 { 4851 config_error("%s:%i: tld::botmotd: %s: %s", 4852 cep->file->filename, cep->line_number, 4853 cep->value, strerror(errno)); 4854 errors++; 4855 } 4856 else 4857 close(fd); 4858 } 4859 /* tld::options */ 4860 else if (!strcmp(cep->name, "options")) { 4861 ConfigEntry *cep2; 4862 4863 if (has_options) 4864 { 4865 config_warn_duplicate(cep->file->filename, 4866 cep->line_number, "tld::options"); 4867 continue; 4868 } 4869 has_options = 1; 4870 4871 for (cep2 = cep->items; cep2; cep2 = cep2->next) 4872 { 4873 if (strcmp(cep2->name, "ssl") && 4874 strcmp(cep2->name, "tls") && 4875 strcmp(cep2->name, "remote")) 4876 { 4877 config_error_unknownopt(cep2->file->filename, 4878 cep2->line_number, "tld", cep2->name); 4879 errors++; 4880 } 4881 } 4882 } 4883 else 4884 { 4885 config_error_unknown(cep->file->filename, cep->line_number, 4886 "tld", cep->name); 4887 errors++; 4888 continue; 4889 } 4890 } 4891 if (!has_mask && !has_match) 4892 { 4893 config_error_missing(ce->file->filename, ce->line_number, 4894 "tld::match"); 4895 errors++; 4896 } 4897 if (has_mask && has_match) 4898 { 4899 config_error("%s:%d: You cannot have both ::mask and ::match. " 4900 "You should only use %s::match.", 4901 ce->file->filename, ce->line_number, ce->name); 4902 errors++; 4903 } 4904 return errors; 4905 } 4906 4907 /* Helper for _conf_listen() */ 4908 void conf_listen_configure(const char *ip, int port, SocketType socket_type, int options, ConfigEntry *ce, ConfigEntry *tlsconfig) 4909 { 4910 ConfigItem_listen *listen; 4911 ConfigEntry *cep, *cepp; 4912 Hook *h; 4913 char isnew = 0; 4914 4915 if (!(listen = find_listen(ip, port, socket_type))) 4916 { 4917 listen = safe_alloc(sizeof(ConfigItem_listen)); 4918 if (socket_type == SOCKET_TYPE_UNIX) 4919 { 4920 safe_strdup(listen->file, ip); 4921 } else { 4922 safe_strdup(listen->ip, ip); 4923 listen->port = port; 4924 } 4925 listen->fd = -1; 4926 listen->socket_type = socket_type; 4927 isnew = 1; 4928 } 4929 4930 if (listen->options & LISTENER_BOUND) 4931 options |= LISTENER_BOUND; 4932 listen->options = options; 4933 4934 if (isnew) 4935 AddListItem(listen, conf_listen); 4936 4937 /* Reset all settings of the current listener (free and set defaults): */ 4938 listen->flag.temporary = 0; 4939 listen->start_handshake = start_of_normal_client_handshake; 4940 if (listen->ssl_ctx) 4941 { 4942 SSL_CTX_free(listen->ssl_ctx); 4943 listen->ssl_ctx = NULL; 4944 } 4945 if (listen->tls_options) 4946 { 4947 free_tls_options(listen->tls_options); 4948 listen->tls_options = NULL; 4949 } 4950 safe_free(listen->websocket_forward); 4951 safe_free(listen->webserver); 4952 4953 /* Now set the new settings: */ 4954 if (tlsconfig) 4955 { 4956 listen->tls_options = safe_alloc(sizeof(TLSOptions)); 4957 conf_tlsblock(conf, tlsconfig, listen->tls_options); 4958 listen->ssl_ctx = init_ctx(listen->tls_options, 1); 4959 } 4960 4961 /* For modules that hook CONFIG_LISTEN and CONFIG_LISTEN_OPTIONS. 4962 * Yeah, ugly we have this here.. 4963 * and again about 100 lines down too. 4964 */ 4965 for (cep = ce->items; cep; cep = cep->next) 4966 { 4967 if (!strcmp(cep->name, "mode")) 4968 { 4969 /* Yeah, we actually do something with this one.. */ 4970 if (cep->value) 4971 listen->mode = strtol(cep->value, NULL, 8); /* octal */ 4972 } 4973 else if (!strcmp(cep->name, "spoof-ip")) 4974 safe_strdup(listen->spoof_ip, cep->value); 4975 else if (!strcmp(cep->name, "ip")) 4976 ; 4977 else if (!strcmp(cep->name, "port")) 4978 ; 4979 else if (!strcmp(cep->name, "options")) 4980 { 4981 for (cepp = cep->items; cepp; cepp = cepp->next) 4982 { 4983 NameValue *ofp; 4984 if (!nv_find_by_name(_ListenerFlags, cepp->name)) 4985 { 4986 for (h = Hooks[HOOKTYPE_CONFIGRUN_EX]; h; h = h->next) 4987 { 4988 int value = (*(h->func.intfunc))(conf, cepp, CONFIG_LISTEN_OPTIONS, listen); 4989 if (value == 1) 4990 break; 4991 } 4992 } 4993 } 4994 } else 4995 if (!strcmp(cep->name, "ssl-options") || !strcmp(cep->name, "tls-options")) 4996 ; 4997 else 4998 { 4999 for (h = Hooks[HOOKTYPE_CONFIGRUN_EX]; h; h = h->next) 5000 { 5001 int value = (*(h->func.intfunc))(conf, cep, CONFIG_LISTEN, listen); 5002 if (value == 1) 5003 break; 5004 } 5005 } 5006 } 5007 } 5008 5009 int _conf_listen(ConfigFile *conf, ConfigEntry *ce) 5010 { 5011 ConfigEntry *cep, *cepp; 5012 ConfigEntry *tlsconfig = NULL; 5013 char *file = NULL; 5014 char *ip = NULL; 5015 char *spoof_ip = NULL; 5016 int start=0, end=0, port; 5017 int listener_flags =0; 5018 Hook *h; 5019 5020 for (cep = ce->items; cep; cep = cep->next) 5021 { 5022 if (!strcmp(cep->name, "file")) 5023 { 5024 convert_to_absolute_path(&cep->value, PERMDATADIR); 5025 file = cep->value; 5026 } else 5027 if (!strcmp(cep->name, "mode")) 5028 { 5029 // Handled elsewhere, but need to be caught here as noop 5030 } else 5031 if (!strcmp(cep->name, "ip")) 5032 { 5033 ip = cep->value; 5034 } else 5035 if (!strcmp(cep->name, "spoof-ip")) 5036 { 5037 spoof_ip = cep->value; 5038 } else 5039 if (!strcmp(cep->name, "port")) 5040 { 5041 port_range(cep->value, &start, &end); 5042 if ((start < 0) || (start > 65535) || (end < 0) || (end > 65535)) 5043 return -1; /* this is already validated in _test_listen, but okay.. */ 5044 } else 5045 if (!strcmp(cep->name, "options")) 5046 { 5047 for (cepp = cep->items; cepp; cepp = cepp->next) 5048 { 5049 long v; 5050 if ((v = nv_find_by_name(_ListenerFlags, cepp->name))) 5051 { 5052 listener_flags |= v; 5053 } else { 5054 for (h = Hooks[HOOKTYPE_CONFIGRUN]; h; h = h->next) 5055 { 5056 int value = (*(h->func.intfunc))(conf, cepp, CONFIG_LISTEN_OPTIONS); 5057 if (value == 1) 5058 break; 5059 } 5060 } 5061 } 5062 } else 5063 if (!strcmp(cep->name, "ssl-options") || !strcmp(cep->name, "tls-options")) 5064 { 5065 tlsconfig = cep; 5066 } else 5067 { 5068 for (h = Hooks[HOOKTYPE_CONFIGRUN]; h; h = h->next) 5069 { 5070 int value = (*(h->func.intfunc))(conf, cep, CONFIG_LISTEN); 5071 if (value == 1) 5072 break; 5073 } 5074 } 5075 } 5076 5077 /* UNIX domain socket */ 5078 if (file) 5079 { 5080 conf_listen_configure(file, 0, SOCKET_TYPE_UNIX, listener_flags, ce, tlsconfig); 5081 return 1; 5082 } 5083 5084 for (port = start; port <= end; port++) 5085 { 5086 /* First deal with IPv4 */ 5087 if (!strchr(ip, ':')) 5088 conf_listen_configure(ip, port, SOCKET_TYPE_IPV4, listener_flags, ce, tlsconfig); 5089 5090 /* Then deal with IPv6 (if available/enabled) */ 5091 if (!DISABLE_IPV6 && (strchr(ip, ':') || (*ip == '*'))) 5092 conf_listen_configure(ip, port, SOCKET_TYPE_IPV6, listener_flags, ce, tlsconfig); 5093 } 5094 5095 return 1; 5096 } 5097 5098 int _test_listen(ConfigFile *conf, ConfigEntry *ce) 5099 { 5100 ConfigEntry *cep; 5101 ConfigEntry *cepp; 5102 int errors = 0; 5103 char has_file = 0, has_ip = 0, has_port = 0, has_options = 0, port_6667 = 0, has_spoof_ip = 0; 5104 char *file = NULL; 5105 char *ip = NULL; 5106 Hook *h; 5107 5108 if (ce->value) 5109 { 5110 config_error("%s:%i: listen block has a new syntax, see https://www.unrealircd.org/docs/Listen_block", 5111 ce->file->filename, ce->line_number); 5112 return 1; 5113 } 5114 5115 for (cep = ce->items; cep; cep = cep->next) 5116 { 5117 int used_by_module = 0; 5118 5119 /* First, check if a module knows about this listen::something */ 5120 for (h = Hooks[HOOKTYPE_CONFIGTEST]; h; h = h->next) 5121 { 5122 int value, errs = 0; 5123 if (h->owner && !(h->owner->flags & MODFLAG_TESTING) 5124 && !(h->owner->options & MOD_OPT_PERM)) 5125 { 5126 continue; 5127 } 5128 value = (*(h->func.intfunc))(conf, cep, CONFIG_LISTEN, &errs); 5129 if (value == 2) 5130 used_by_module = 1; 5131 if (value == 1) 5132 { 5133 used_by_module = 1; 5134 break; 5135 } 5136 if (value == -1) 5137 { 5138 used_by_module = 1; 5139 errors += errs; 5140 break; 5141 } 5142 if (value == -2) 5143 { 5144 used_by_module = 1; 5145 errors += errs; 5146 } 5147 } 5148 if (!strcmp(cep->name, "options")) 5149 { 5150 if (has_options) 5151 { 5152 config_warn_duplicate(cep->file->filename, 5153 cep->line_number, "listen::options"); 5154 continue; 5155 } 5156 has_options = 1; 5157 for (cepp = cep->items; cepp; cepp = cepp->next) 5158 { 5159 if (!nv_find_by_name(_ListenerFlags, cepp->name)) 5160 { 5161 /* Check if a module knows about this listen::options::something */ 5162 int used_by_module = 0; 5163 for (h = Hooks[HOOKTYPE_CONFIGTEST]; h; h = h->next) 5164 { 5165 int value, errs = 0; 5166 if (h->owner && !(h->owner->flags & MODFLAG_TESTING) 5167 && !(h->owner->options & MOD_OPT_PERM)) 5168 { 5169 continue; 5170 } 5171 value = (*(h->func.intfunc))(conf, cepp, CONFIG_LISTEN_OPTIONS, &errs); 5172 if (value == 2) 5173 used_by_module = 1; 5174 if (value == 1) 5175 { 5176 used_by_module = 1; 5177 break; 5178 } 5179 if (value == -1) 5180 { 5181 used_by_module = 1; 5182 errors += errs; 5183 break; 5184 } 5185 if (value == -2) 5186 { 5187 used_by_module = 1; 5188 errors += errs; 5189 } 5190 } 5191 if (!used_by_module) 5192 { 5193 config_error_unknownopt(cepp->file->filename, 5194 cepp->line_number, "listen::options", cepp->name); 5195 errors++; 5196 continue; 5197 } 5198 } 5199 if (!strcmp(cepp->name, "ssl") || !strcmp(cepp->name, "tls")) 5200 have_tls_listeners = 1; /* for ssl config test */ 5201 } 5202 } 5203 else 5204 if (!strcmp(cep->name, "ssl-options") || !strcmp(cep->name, "tls-options")) 5205 { 5206 test_tlsblock(conf, cep, &errors); 5207 } 5208 else 5209 if (!cep->value) 5210 { 5211 if (!used_by_module) 5212 { 5213 config_error_empty(cep->file->filename, 5214 cep->line_number, "listen", cep->name); 5215 errors++; 5216 } 5217 continue; /* always */ 5218 } else 5219 if (!strcmp(cep->name, "file")) 5220 { 5221 has_file = 1; 5222 file = cep->value; 5223 } else 5224 if (!strcmp(cep->name, "spoof-ip")) 5225 { 5226 has_spoof_ip = 1; 5227 if (!is_valid_ip(cep->value)) 5228 { 5229 config_error("%s:%i: listen::spoof-ip is not a valid IP address (%s)", 5230 cep->file->filename, cep->line_number, cep->value); 5231 errors++; 5232 } 5233 } else 5234 if (!strcmp(cep->name, "mode")) 5235 { 5236 int mode = strtol(cep->value, NULL, 8); 5237 if ((mode != 0700) && (mode != 0770) && (mode != 0777)) 5238 { 5239 config_error("%s:%i: listen::mode must be one of: 0700 (user only, the default), " 5240 "0770 (user and group readable/writable), or " 5241 "0777 (world readable and writable, not recommended).", 5242 cep->file->filename, cep->line_number); 5243 errors++; 5244 } 5245 } else 5246 if (!strcmp(cep->name, "ip")) 5247 { 5248 has_ip = 1; 5249 5250 if (strcmp(cep->value, "*") && !is_valid_ip(cep->value)) 5251 { 5252 config_error("%s:%i: listen: illegal listen::ip (%s). Must be either '*' or contain a valid IP.", 5253 cep->file->filename, cep->line_number, cep->value); 5254 return 1; 5255 } 5256 ip = cep->value; 5257 } else 5258 if (!strcmp(cep->name, "host")) 5259 { 5260 config_error("%s:%i: listen: unknown option listen::host, did you mean listen::ip?", 5261 cep->file->filename, cep->line_number); 5262 errors++; 5263 } else 5264 if (!strcmp(cep->name, "port")) 5265 { 5266 int start = 0, end = 0; 5267 5268 has_port = 1; 5269 5270 port_range(cep->value, &start, &end); 5271 if (start == end) 5272 { 5273 if ((start < 1) || (start > 65535)) 5274 { 5275 config_error("%s:%i: listen: illegal port (must be 1..65535)", 5276 cep->file->filename, cep->line_number); 5277 return 1; 5278 } 5279 } 5280 else 5281 { 5282 if (end < start) 5283 { 5284 config_error("%s:%i: listen: illegal port range end value is less than starting value", 5285 cep->file->filename, cep->line_number); 5286 return 1; 5287 } 5288 if (end - start >= 100) 5289 { 5290 config_error("%s:%i: listen: you requested port %d-%d, that's %d ports " 5291 "(and thus consumes %d sockets) this is probably not what you want.", 5292 cep->file->filename, cep->line_number, start, end, 5293 end - start + 1, end - start + 1); 5294 return 1; 5295 } 5296 if ((start < 1) || (start > 65535) || (end < 1) || (end > 65535)) 5297 { 5298 config_error("%s:%i: listen: illegal port range values must be between 1 and 65535", 5299 cep->file->filename, cep->line_number); 5300 return 1; 5301 } 5302 } 5303 5304 if ((6667 >= start) && (6667 <= end)) 5305 port_6667 = 1; 5306 } else 5307 { 5308 if (!used_by_module) 5309 { 5310 config_error_unknown(cep->file->filename, cep->line_number, 5311 "listen", cep->name); 5312 errors++; 5313 } 5314 continue; /* always */ 5315 } 5316 } 5317 5318 if (has_file) 5319 { 5320 if (has_ip || has_port) 5321 { 5322 config_error("%s:%d: listen block should either have a 'file' (for *NIX domain socket), " 5323 "OR have an 'ip' and 'port' (for IPv4/IPv6). You cannot combine both in one listen block.", 5324 ce->file->filename, ce->line_number); 5325 errors++; 5326 } else { 5327 // TODO: check if file can be created fresh etc. 5328 } 5329 } else 5330 { 5331 if (!has_ip) 5332 { 5333 config_error("%s:%d: listen block requires an listen::ip", 5334 ce->file->filename, ce->line_number); 5335 errors++; 5336 } 5337 5338 if (!has_port) 5339 { 5340 config_error("%s:%d: listen block requires an listen::port", 5341 ce->file->filename, ce->line_number); 5342 errors++; 5343 } 5344 } 5345 5346 if (has_spoof_ip && !has_file) 5347 { 5348 config_error("%s:%d: listen::spoof-ip is only valid when listen::file is used (UNIX domain sockets)", 5349 ce->file->filename, ce->line_number); 5350 errors++; 5351 } 5352 5353 if (port_6667) 5354 safe_strdup(port_6667_ip, ip); 5355 5356 requiredstuff.conf_listen = 1; 5357 return errors; 5358 } 5359 5360 5361 int _conf_allow(ConfigFile *conf, ConfigEntry *ce) 5362 { 5363 ConfigEntry *cep, *cepp; 5364 ConfigItem_allow *allow; 5365 Hook *h; 5366 5367 if (ce->value) 5368 { 5369 if (!strcmp(ce->value, "channel")) 5370 return (_conf_allow_channel(conf, ce)); 5371 else 5372 { 5373 int value; 5374 for (h = Hooks[HOOKTYPE_CONFIGRUN]; h; h = h->next) 5375 { 5376 value = (*(h->func.intfunc))(conf,ce,CONFIG_ALLOW); 5377 if (value == 1) 5378 break; 5379 } 5380 return 0; 5381 } 5382 } 5383 allow = safe_alloc(sizeof(ConfigItem_allow)); 5384 allow->ipv6_clone_mask = tempiConf.default_ipv6_clone_mask; 5385 allow->match = safe_alloc(sizeof(SecurityGroup)); 5386 5387 for (cep = ce->items; cep; cep = cep->next) 5388 { 5389 if (!strcmp(cep->name, "match") || !strcmp(cep->name, "mask") || !strcmp(cep->name, "ip") || !strcmp(cep->name, "hostname")) 5390 { 5391 conf_match_block(conf, cep, &allow->match); 5392 } 5393 else if (!strcmp(cep->name, "password")) 5394 allow->auth = AuthBlockToAuthConfig(cep); 5395 else if (!strcmp(cep->name, "class")) 5396 { 5397 allow->class = find_class(cep->value); 5398 if (!allow->class || (allow->class->flag.temporary == 1)) 5399 { 5400 config_status("%s:%i: illegal allow::class, unknown class '%s' using default of class 'default'", 5401 cep->file->filename, 5402 cep->line_number, 5403 cep->value); 5404 allow->class = default_class; 5405 } 5406 } 5407 else if (!strcmp(cep->name, "maxperip")) 5408 allow->maxperip = atoi(cep->value); 5409 else if (!strcmp(cep->name, "global-maxperip")) 5410 allow->global_maxperip = atoi(cep->value); 5411 else if (!strcmp(cep->name, "redirect-server")) 5412 safe_strdup(allow->server, cep->value); 5413 else if (!strcmp(cep->name, "redirect-port")) 5414 allow->port = atoi(cep->value); 5415 else if (!strcmp(cep->name, "ipv6-clone-mask")) 5416 { 5417 /* 5418 * If this item isn't set explicitly by the 5419 * user, the value will temporarily be 5420 * zero. Defaults are applied in config_run_blocks(). 5421 */ 5422 allow->ipv6_clone_mask = atoi(cep->value); 5423 } 5424 else if (!strcmp(cep->name, "options")) 5425 { 5426 for (cepp = cep->items; cepp; cepp = cepp->next) 5427 { 5428 if (!strcmp(cepp->name, "noident")) 5429 allow->flags.noident = 1; 5430 else if (!strcmp(cepp->name, "useip")) 5431 allow->flags.useip = 1; 5432 else if (!strcmp(cepp->name, "ssl") || !strcmp(cepp->name, "tls")) 5433 allow->flags.tls = 1; 5434 else if (!strcmp(cepp->name, "reject-on-auth-failure")) 5435 allow->flags.reject_on_auth_failure = 1; 5436 } 5437 } 5438 } 5439 5440 /* Default: global-maxperip = maxperip+1 */ 5441 if (allow->global_maxperip == 0) 5442 allow->global_maxperip = allow->maxperip+1; 5443 5444 /* global-maxperip < maxperip makes no sense */ 5445 if (allow->global_maxperip < allow->maxperip) 5446 allow->global_maxperip = allow->maxperip; 5447 5448 AddListItem(allow, conf_allow); 5449 return 1; 5450 } 5451 5452 int _test_allow(ConfigFile *conf, ConfigEntry *ce) 5453 { 5454 ConfigEntry *cep, *cepp; 5455 int errors = 0; 5456 Hook *h; 5457 char has_ip = 0, has_hostname = 0, has_mask = 0, has_match = 0; 5458 char has_maxperip = 0, has_global_maxperip = 0, has_password = 0, has_class = 0; 5459 char has_redirectserver = 0, has_redirectport = 0, has_options = 0; 5460 int hostname_possible_silliness = 0; 5461 5462 if (ce->value) 5463 { 5464 if (!strcmp(ce->value, "channel")) 5465 return (_test_allow_channel(conf, ce)); 5466 else 5467 { 5468 int used = 0; 5469 for (h = Hooks[HOOKTYPE_CONFIGTEST]; h; h = h->next) 5470 { 5471 int value, errs = 0; 5472 if (h->owner && !(h->owner->flags & MODFLAG_TESTING) 5473 && !(h->owner->options & MOD_OPT_PERM)) 5474 continue; 5475 value = (*(h->func.intfunc))(conf,ce,CONFIG_ALLOW,&errs); 5476 if (value == 2) 5477 used = 1; 5478 if (value == 1) 5479 { 5480 used = 1; 5481 break; 5482 } 5483 if (value == -1) 5484 { 5485 used = 1; 5486 errors += errs; 5487 break; 5488 } 5489 if (value == -2) 5490 { 5491 used = 1; 5492 errors += errs; 5493 } 5494 } 5495 if (!used) { 5496 config_error("%s:%i: allow item with unknown type", 5497 ce->file->filename, ce->line_number); 5498 return 1; 5499 } 5500 return errors; 5501 } 5502 } 5503 5504 for (cep = ce->items; cep; cep = cep->next) 5505 { 5506 if (strcmp(cep->name, "options") && 5507 strcmp(cep->name, "match") && 5508 strcmp(cep->name, "mask") && 5509 config_is_blankorempty(cep, "allow")) 5510 { 5511 errors++; 5512 continue; 5513 } 5514 if (!strcmp(cep->name, "ip")) 5515 { 5516 if (has_ip) 5517 { 5518 config_warn_duplicate(cep->file->filename, 5519 cep->line_number, "allow::ip"); 5520 continue; 5521 } 5522 has_ip = 1; 5523 } 5524 else if (!strcmp(cep->name, "hostname")) 5525 { 5526 if (has_hostname) 5527 { 5528 config_warn_duplicate(cep->file->filename, 5529 cep->line_number, "allow::hostname"); 5530 continue; 5531 } 5532 has_hostname = 1; 5533 if (!strcmp(cep->value, "*@*") || !strcmp(cep->value, "*")) 5534 hostname_possible_silliness = 1; 5535 } 5536 else if (!strcmp(cep->name, "mask")) 5537 { 5538 has_mask = 1; 5539 test_match_block(conf, cep, &errors); 5540 } 5541 else if (!strcmp(cep->name, "match")) 5542 { 5543 has_match = 1; 5544 test_match_block(conf, cep, &errors); 5545 } 5546 else if (!strcmp(cep->name, "maxperip")) 5547 { 5548 int v = atoi(cep->value); 5549 if (has_maxperip) 5550 { 5551 config_warn_duplicate(cep->file->filename, 5552 cep->line_number, "allow::maxperip"); 5553 continue; 5554 } 5555 has_maxperip = 1; 5556 if ((v <= 0) || (v > 1000000)) 5557 { 5558 config_error("%s:%i: allow::maxperip with illegal value (must be 1-1000000)", 5559 cep->file->filename, cep->line_number); 5560 errors++; 5561 } 5562 } 5563 else if (!strcmp(cep->name, "global-maxperip")) 5564 { 5565 int v = atoi(cep->value); 5566 if (has_global_maxperip) 5567 { 5568 config_warn_duplicate(cep->file->filename, 5569 cep->line_number, "allow::global-maxperip"); 5570 continue; 5571 } 5572 has_global_maxperip = 1; 5573 if ((v <= 0) || (v > 1000000)) 5574 { 5575 config_error("%s:%i: allow::global-maxperip with illegal value (must be 1-1000000)", 5576 cep->file->filename, cep->line_number); 5577 errors++; 5578 } 5579 } 5580 else if (!strcmp(cep->name, "ipv6-clone-mask")) 5581 { 5582 /* keep this in sync with _test_set() */ 5583 int ipv6mask; 5584 ipv6mask = atoi(cep->value); 5585 if (ipv6mask == 0) 5586 { 5587 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.", 5588 cep->file->filename, cep->line_number); 5589 errors++; 5590 } 5591 if (ipv6mask > 128) 5592 { 5593 config_error("%s:%d: set::default-ipv6-clone-mask was set to %d. The maximum value is 128.", 5594 cep->file->filename, cep->line_number, 5595 ipv6mask); 5596 errors++; 5597 } 5598 if (ipv6mask <= 32) 5599 { 5600 config_warn("%s:%d: allow::ipv6-clone-mask was given a very small value.", 5601 cep->file->filename, cep->line_number); 5602 } 5603 } 5604 else if (!strcmp(cep->name, "password")) 5605 { 5606 if (has_password) 5607 { 5608 config_warn_duplicate(cep->file->filename, 5609 cep->line_number, "allow::password"); 5610 continue; 5611 } 5612 has_password = 1; 5613 /* some auth check stuff? */ 5614 if (Auth_CheckError(cep) < 0) 5615 errors++; 5616 } 5617 else if (!strcmp(cep->name, "class")) 5618 { 5619 if (has_class) 5620 { 5621 config_warn_duplicate(cep->file->filename, 5622 cep->line_number, "allow::class"); 5623 continue; 5624 } 5625 has_class = 1; 5626 } 5627 else if (!strcmp(cep->name, "redirect-server")) 5628 { 5629 if (has_redirectserver) 5630 { 5631 config_warn_duplicate(cep->file->filename, 5632 cep->line_number, "allow::redirect-server"); 5633 continue; 5634 } 5635 has_redirectserver = 1; 5636 } 5637 else if (!strcmp(cep->name, "redirect-port")) 5638 { 5639 if (has_redirectport) 5640 { 5641 config_warn_duplicate(cep->file->filename, 5642 cep->line_number, "allow::redirect-port"); 5643 continue; 5644 } 5645 has_redirectport = 1; 5646 } 5647 else if (!strcmp(cep->name, "options")) 5648 { 5649 if (has_options) 5650 { 5651 config_warn_duplicate(cep->file->filename, 5652 cep->line_number, "allow::options"); 5653 continue; 5654 } 5655 has_options = 1; 5656 for (cepp = cep->items; cepp; cepp = cepp->next) 5657 { 5658 if (!strcmp(cepp->name, "noident")) 5659 {} 5660 else if (!strcmp(cepp->name, "useip")) 5661 {} 5662 else if (!strcmp(cepp->name, "ssl") || !strcmp(cepp->name, "tls")) 5663 {} 5664 else if (!strcmp(cepp->name, "reject-on-auth-failure")) 5665 {} 5666 else if (!strcmp(cepp->name, "sasl")) 5667 { 5668 config_error("%s:%d: The option allow::options::sasl no longer exists. " 5669 "Please use a require authentication { } block instead, which " 5670 "is more flexible and provides the same functionality. See " 5671 "https://www.unrealircd.org/docs/Require_authentication_block", 5672 cepp->file->filename, cepp->line_number); 5673 errors++; 5674 } 5675 else 5676 { 5677 config_error_unknownopt(cepp->file->filename, 5678 cepp->line_number, "allow", cepp->name); 5679 errors++; 5680 } 5681 } 5682 } 5683 else 5684 { 5685 config_error_unknown(cep->file->filename, cep->line_number, 5686 "allow", cep->name); 5687 errors++; 5688 continue; 5689 } 5690 } 5691 5692 if ((has_mask || has_match) && (has_ip || has_hostname)) 5693 { 5694 config_error("%s:%d: The allow block uses allow::match, but you also have an allow::ip and allow::hostname.", 5695 ce->file->filename, ce->line_number); 5696 config_error("Please delete your allow::ip and allow::hostname entries and/or integrate them into allow::match"); 5697 } else 5698 if (has_ip) 5699 { 5700 config_warn("%s:%d: The allow block uses allow::match nowadays. Rename your allow::ip item to allow::match.", 5701 ce->file->filename, ce->line_number); 5702 config_warn("See https://www.unrealircd.org/docs/FAQ#allow-mask for more information"); 5703 } else 5704 if (has_hostname) 5705 { 5706 config_warn("%s:%d: The allow block uses allow::match nowadays. Rename your allow::hostname item to allow::match.", 5707 ce->file->filename, ce->line_number); 5708 config_warn("See https://www.unrealircd.org/docs/FAQ#allow-mask for more information"); 5709 } else 5710 if (has_mask && has_match) 5711 { 5712 config_error("%s:%d: You cannot have both ::mask and ::match. You should only use allow::match.", 5713 ce->file->filename, ce->line_number); 5714 errors++; 5715 } else 5716 if (!has_match && !has_mask) 5717 { 5718 config_error("%s:%d: allow block needs an allow::match", 5719 ce->file->filename, ce->line_number); 5720 errors++; 5721 } 5722 5723 if (has_ip && has_hostname) 5724 { 5725 config_error("%s:%d: allow block has both allow::ip and allow::hostname, this is no longer permitted.", 5726 ce->file->filename, ce->line_number); 5727 config_error("Please integrate your allow::ip and allow::hostname items into a single allow::mask block"); 5728 errors++; 5729 } else 5730 if (hostname_possible_silliness) 5731 { 5732 config_error("%s:%d: allow block contains 'hostname *;'. This means means that users " 5733 "without a valid hostname (unresolved IP's) will be unable to connect. " 5734 "You most likely want to use 'mask *;' instead.", 5735 ce->file->filename, ce->line_number); 5736 } 5737 5738 if (!has_class) 5739 { 5740 config_error_missing(ce->file->filename, ce->line_number, 5741 "allow::class"); 5742 errors++; 5743 } 5744 5745 if (!has_maxperip) 5746 { 5747 config_error_missing(ce->file->filename, ce->line_number, 5748 "allow::maxperip"); 5749 errors++; 5750 } 5751 return errors; 5752 } 5753 5754 int _conf_allow_channel(ConfigFile *conf, ConfigEntry *ce) 5755 { 5756 ConfigItem_allow_channel *allow = NULL; 5757 ConfigEntry *cep; 5758 char *class = NULL; 5759 ConfigEntry *match = NULL; 5760 5761 /* First, search for ::class, if any */ 5762 for (cep = ce->items; cep; cep = cep->next) 5763 { 5764 if (!strcmp(cep->name, "class")) 5765 class = cep->value; 5766 else if (!strcmp(cep->name, "match") || !strcmp(cep->name, "mask")) 5767 match = cep; 5768 } 5769 5770 for (cep = ce->items; cep; cep = cep->next) 5771 { 5772 if (!strcmp(cep->name, "channel")) 5773 { 5774 /* This way, we permit multiple ::channel items in one allow block */ 5775 allow = safe_alloc(sizeof(ConfigItem_allow_channel)); 5776 safe_strdup(allow->channel, cep->value); 5777 if (class) 5778 safe_strdup(allow->class, class); 5779 if (match) 5780 conf_match_block(conf, match, &allow->match); 5781 AddListItem(allow, conf_allow_channel); 5782 } 5783 } 5784 return 1; 5785 } 5786 5787 int _test_allow_channel(ConfigFile *conf, ConfigEntry *ce) 5788 { 5789 ConfigEntry *cep; 5790 int errors = 0; 5791 char has_match = 0, has_mask = 0, has_channel = 0, has_class = 0; 5792 5793 for (cep = ce->items; cep; cep = cep->next) 5794 { 5795 if (config_is_blankorempty(cep, "allow channel")) 5796 { 5797 errors++; 5798 continue; 5799 } 5800 5801 if (!strcmp(cep->name, "channel")) 5802 { 5803 has_channel = 1; 5804 } 5805 else if (!strcmp(cep->name, "class")) 5806 { 5807 5808 if (has_class) 5809 { 5810 config_warn_duplicate(cep->file->filename, 5811 cep->line_number, "allow channel::class"); 5812 continue; 5813 } 5814 has_class = 1; 5815 } 5816 else if (!strcmp(cep->name, "match")) 5817 { 5818 has_match = 1; 5819 test_match_block(conf, cep, &errors); 5820 } 5821 else if (!strcmp(cep->name, "mask")) 5822 { 5823 has_mask = 1; 5824 test_match_block(conf, cep, &errors); 5825 } 5826 else 5827 { 5828 config_error_unknown(cep->file->filename, cep->line_number, 5829 "allow channel", cep->name); 5830 errors++; 5831 } 5832 } 5833 if (has_mask && has_match) 5834 { 5835 config_error("%s:%d: You cannot have both ::mask and ::match. " 5836 "You should only use %s::match.", 5837 ce->file->filename, ce->line_number, ce->name); 5838 errors++; 5839 } 5840 if (!has_channel) 5841 { 5842 config_error_missing(ce->file->filename, ce->line_number, 5843 "allow channel::channel"); 5844 errors++; 5845 } 5846 return errors; 5847 } 5848 5849 int _conf_except(ConfigFile *conf, ConfigEntry *ce) 5850 { 5851 Hook *h; 5852 int value; 5853 5854 for (h = Hooks[HOOKTYPE_CONFIGRUN]; h; h = h->next) 5855 { 5856 value = (*(h->func.intfunc))(conf,ce,CONFIG_EXCEPT); 5857 if (value == 1) 5858 break; 5859 } 5860 5861 return 1; 5862 } 5863 5864 int _test_except(ConfigFile *conf, ConfigEntry *ce) 5865 { 5866 int errors = 0; 5867 Hook *h; 5868 int used = 0; 5869 5870 if (!ce->value) 5871 { 5872 config_error("%s:%i: except without type", 5873 ce->file->filename, ce->line_number); 5874 return 1; 5875 } 5876 5877 if (!strcmp(ce->value, "tkl")) 5878 { 5879 config_warn("%s:%i: except tkl { } is now called except ban { }. " 5880 "Simply rename the block from 'except tkl' to 'except ban' " 5881 "to get rid of this warning.", 5882 ce->file->filename, ce->line_number); 5883 safe_strdup(ce->value, "ban"); /* awww */ 5884 } 5885 5886 for (h = Hooks[HOOKTYPE_CONFIGTEST]; h; h = h->next) 5887 { 5888 int value, errs = 0; 5889 if (h->owner && !(h->owner->flags & MODFLAG_TESTING) 5890 && !(h->owner->options & MOD_OPT_PERM)) 5891 continue; 5892 value = (*(h->func.intfunc))(conf,ce,CONFIG_EXCEPT,&errs); 5893 if (value == 2) 5894 used = 1; 5895 if (value == 1) 5896 { 5897 used = 1; 5898 break; 5899 } 5900 if (value == -1) 5901 { 5902 used = 1; 5903 errors += errs; 5904 break; 5905 } 5906 if (value == -2) 5907 { 5908 used = 1; 5909 errors += errs; 5910 } 5911 } 5912 5913 if (!used) 5914 { 5915 config_error("%s:%i: unknown except type %s", 5916 ce->file->filename, ce->line_number, 5917 ce->value); 5918 return 1; 5919 } 5920 5921 return errors; 5922 } 5923 5924 /* 5925 * vhost {} block parser 5926 */ 5927 int _conf_vhost(ConfigFile *conf, ConfigEntry *ce) 5928 { 5929 ConfigItem_vhost *vhost; 5930 ConfigEntry *cep, *cepp; 5931 vhost = safe_alloc(sizeof(ConfigItem_vhost)); 5932 vhost->match = safe_alloc(sizeof(SecurityGroup)); 5933 5934 for (cep = ce->items; cep; cep = cep->next) 5935 { 5936 if (!strcmp(cep->name, "vhost")) 5937 { 5938 char *user, *host; 5939 user = strtok(cep->value, "@"); 5940 host = strtok(NULL, ""); 5941 if (!host) 5942 safe_strdup(vhost->virthost, user); 5943 else 5944 { 5945 safe_strdup(vhost->virtuser, user); 5946 safe_strdup(vhost->virthost, host); 5947 } 5948 } 5949 else if (!strcmp(cep->name, "login")) 5950 safe_strdup(vhost->login, cep->value); 5951 else if (!strcmp(cep->name, "password")) 5952 vhost->auth = AuthBlockToAuthConfig(cep); 5953 else if (!strcmp(cep->name, "match") || !strcmp(cep->name, "mask")) 5954 { 5955 conf_match_block(conf, cep, &vhost->match); 5956 } 5957 else if (!strcmp(cep->name, "swhois")) 5958 { 5959 SWhois *s; 5960 if (cep->items) 5961 { 5962 for (cepp = cep->items; cepp; cepp = cepp->next) 5963 { 5964 s = safe_alloc(sizeof(SWhois)); 5965 safe_strdup(s->line, cepp->name); 5966 safe_strdup(s->setby, "vhost"); 5967 AddListItem(s, vhost->swhois); 5968 } 5969 } else 5970 if (cep->value) 5971 { 5972 s = safe_alloc(sizeof(SWhois)); 5973 safe_strdup(s->line, cep->value); 5974 safe_strdup(s->setby, "vhost"); 5975 AddListItem(s, vhost->swhois); 5976 } 5977 } 5978 } 5979 AddListItem(vhost, conf_vhost); 5980 return 1; 5981 } 5982 5983 int _test_vhost(ConfigFile *conf, ConfigEntry *ce) 5984 { 5985 int errors = 0; 5986 ConfigEntry *cep; 5987 char has_vhost = 0, has_login = 0, has_password = 0, has_mask = 0, has_match = 0; 5988 5989 for (cep = ce->items; cep; cep = cep->next) 5990 { 5991 if (!strcmp(cep->name, "vhost")) 5992 { 5993 char *at, *tmp, *host; 5994 if (has_vhost) 5995 { 5996 config_warn_duplicate(cep->file->filename, 5997 cep->line_number, "vhost::vhost"); 5998 continue; 5999 } 6000 has_vhost = 1; 6001 if (!cep->value) 6002 { 6003 config_error_empty(cep->file->filename, 6004 cep->line_number, "vhost", "vhost"); 6005 errors++; 6006 continue; 6007 } 6008 if (!valid_vhost(cep->value)) 6009 { 6010 config_error("%s:%i: oper::vhost contains illegal characters or is too long: '%s'", 6011 cep->file->filename, cep->line_number, cep->value); 6012 errors++; 6013 } 6014 } 6015 else if (!strcmp(cep->name, "login")) 6016 { 6017 if (has_login) 6018 { 6019 config_warn_duplicate(cep->file->filename, 6020 cep->line_number, "vhost::login"); 6021 } 6022 has_login = 1; 6023 if (!cep->value) 6024 { 6025 config_error_empty(cep->file->filename, 6026 cep->line_number, "vhost", "login"); 6027 errors++; 6028 continue; 6029 } 6030 } 6031 else if (!strcmp(cep->name, "password")) 6032 { 6033 if (has_password) 6034 { 6035 config_warn_duplicate(cep->file->filename, 6036 cep->line_number, "vhost::password"); 6037 } 6038 has_password = 1; 6039 if (!cep->value) 6040 { 6041 config_error_empty(cep->file->filename, 6042 cep->line_number, "vhost", "password"); 6043 errors++; 6044 continue; 6045 } 6046 if (Auth_CheckError(cep) < 0) 6047 errors++; 6048 } 6049 else if (!strcmp(cep->name, "mask")) 6050 { 6051 has_mask = 1; 6052 test_match_block(conf, cep, &errors); 6053 } 6054 else if (!strcmp(cep->name, "match")) 6055 { 6056 has_match = 1; 6057 test_match_block(conf, cep, &errors); 6058 } 6059 else if (!strcmp(cep->name, "swhois")) 6060 { 6061 /* multiple is ok */ 6062 } 6063 else 6064 { 6065 config_error_unknown(cep->file->filename, cep->line_number, 6066 "vhost", cep->name); 6067 errors++; 6068 } 6069 } 6070 if (!has_vhost) 6071 { 6072 config_error_missing(ce->file->filename, ce->line_number, 6073 "vhost::vhost"); 6074 errors++; 6075 } 6076 if (!has_login) 6077 { 6078 config_error_missing(ce->file->filename, ce->line_number, 6079 "vhost::login"); 6080 errors++; 6081 6082 } 6083 if (!has_password) 6084 { 6085 config_error_missing(ce->file->filename, ce->line_number, 6086 "vhost::password"); 6087 errors++; 6088 } 6089 if (!has_mask && !has_match) 6090 { 6091 config_error_missing(ce->file->filename, ce->line_number, 6092 "vhost::match"); 6093 errors++; 6094 } 6095 if (has_mask && has_match) 6096 { 6097 config_error("%s:%d: You cannot have both ::mask and ::match. " 6098 "You should only use %s::match.", 6099 ce->file->filename, ce->line_number, ce->name); 6100 errors++; 6101 } 6102 return errors; 6103 } 6104 6105 int _test_sni(ConfigFile *conf, ConfigEntry *ce) 6106 { 6107 int errors = 0; 6108 ConfigEntry *cep; 6109 6110 if (!ce->value) 6111 { 6112 config_error("%s:%i: sni block needs a name, eg: sni irc.xyz.com {", 6113 ce->file->filename, ce->line_number); 6114 errors++; 6115 } 6116 6117 for (cep = ce->items; cep; cep = cep->next) 6118 { 6119 if (!strcmp(cep->name, "ssl-options") || !strcmp(cep->name, "tls-options")) 6120 { 6121 test_tlsblock(conf, cep, &errors); 6122 } else 6123 { 6124 config_error_unknown(cep->file->filename, cep->line_number, 6125 "sni", cep->name); 6126 errors++; 6127 continue; 6128 } 6129 } 6130 6131 return errors; 6132 } 6133 6134 int _conf_sni(ConfigFile *conf, ConfigEntry *ce) 6135 { 6136 ConfigEntry *cep; 6137 ConfigEntry *tlsconfig = NULL; 6138 char *name; 6139 ConfigItem_sni *sni = NULL; 6140 6141 name = ce->value; 6142 if (!name) 6143 return 0; 6144 6145 for (cep = ce->items; cep; cep = cep->next) 6146 { 6147 if (!strcmp(cep->name, "ssl-options") || !strcmp(cep->name, "tls-options")) 6148 { 6149 tlsconfig = cep; 6150 } 6151 } 6152 6153 if (!tlsconfig) 6154 return 0; 6155 6156 sni = safe_alloc(sizeof(ConfigItem_listen)); 6157 safe_strdup(sni->name, name); 6158 sni->tls_options = safe_alloc(sizeof(TLSOptions)); 6159 conf_tlsblock(conf, tlsconfig, sni->tls_options); 6160 sni->ssl_ctx = init_ctx(sni->tls_options, 1); 6161 AddListItem(sni, conf_sni); 6162 6163 return 1; 6164 } 6165 6166 int _conf_help(ConfigFile *conf, ConfigEntry *ce) 6167 { 6168 ConfigEntry *cep; 6169 ConfigItem_help *ca; 6170 MOTDLine *last = NULL, *temp; 6171 ca = safe_alloc(sizeof(ConfigItem_help)); 6172 6173 if (!ce->value) 6174 ca->command = NULL; 6175 else 6176 safe_strdup(ca->command, ce->value); 6177 6178 for (cep = ce->items; cep; cep = cep->next) 6179 { 6180 temp = safe_alloc(sizeof(MOTDLine)); 6181 safe_strdup(temp->line, cep->name); 6182 temp->next = NULL; 6183 if (!last) 6184 ca->text = temp; 6185 else 6186 last->next = temp; 6187 last = temp; 6188 } 6189 AddListItem(ca, conf_help); 6190 return 1; 6191 6192 } 6193 6194 int _test_help(ConfigFile *conf, ConfigEntry *ce) { 6195 int errors = 0; 6196 ConfigEntry *cep; 6197 if (!ce->items) 6198 { 6199 config_error("%s:%i: empty help block", 6200 ce->file->filename, ce->line_number); 6201 return 1; 6202 } 6203 for (cep = ce->items; cep; cep = cep->next) 6204 { 6205 if (strlen(cep->name) > 500) 6206 { 6207 config_error("%s:%i: oversized help item", 6208 ce->file->filename, ce->line_number); 6209 errors++; 6210 continue; 6211 } 6212 } 6213 return errors; 6214 } 6215 6216 int _conf_link(ConfigFile *conf, ConfigEntry *ce) 6217 { 6218 ConfigEntry *cep, *cepp, *ceppp; 6219 ConfigItem_link *link = NULL; 6220 6221 link = safe_alloc(sizeof(ConfigItem_link)); 6222 safe_strdup(link->servername, ce->value); 6223 6224 for (cep = ce->items; cep; cep = cep->next) 6225 { 6226 if (!strcmp(cep->name, "incoming")) 6227 { 6228 for (cepp = cep->items; cepp; cepp = cepp->next) 6229 { 6230 if (!strcmp(cepp->name, "match") || !strcmp(cepp->name, "mask")) 6231 { 6232 conf_match_block(conf, cepp, &link->incoming.match); 6233 } 6234 } 6235 } 6236 else if (!strcmp(cep->name, "outgoing")) 6237 { 6238 for (cepp = cep->items; cepp; cepp = cepp->next) 6239 { 6240 if (!strcmp(cepp->name, "bind-ip")) 6241 safe_strdup(link->outgoing.bind_ip, cepp->value); 6242 else if (!strcmp(cepp->name, "file")) 6243 safe_strdup(link->outgoing.file, cepp->value); 6244 else if (!strcmp(cepp->name, "hostname")) 6245 safe_strdup(link->outgoing.hostname, cepp->value); 6246 else if (!strcmp(cepp->name, "port")) 6247 link->outgoing.port = atoi(cepp->value); 6248 else if (!strcmp(cepp->name, "options")) 6249 { 6250 link->outgoing.options = 0; 6251 for (ceppp = cepp->items; ceppp; ceppp = ceppp->next) 6252 { 6253 long v; 6254 if ((v = nv_find_by_name(_LinkFlags, ceppp->name))) 6255 link->outgoing.options |= v; 6256 } 6257 } 6258 else if (!strcmp(cepp->name, "ssl-options") || !strcmp(cepp->name, "tls-options")) 6259 { 6260 link->tls_options = safe_alloc(sizeof(TLSOptions)); 6261 conf_tlsblock(conf, cepp, link->tls_options); 6262 link->ssl_ctx = init_ctx(link->tls_options, 0); 6263 } 6264 } 6265 } 6266 else if (!strcmp(cep->name, "password")) 6267 link->auth = AuthBlockToAuthConfig(cep); 6268 else if (!strcmp(cep->name, "hub")) 6269 safe_strdup(link->hub, cep->value); 6270 else if (!strcmp(cep->name, "leaf")) 6271 safe_strdup(link->leaf, cep->value); 6272 else if (!strcmp(cep->name, "leaf-depth") || !strcmp(cep->name, "leafdepth")) 6273 link->leaf_depth = atoi(cep->value); 6274 else if (!strcmp(cep->name, "class")) 6275 { 6276 link->class = find_class(cep->value); 6277 if (!link->class || (link->class->flag.temporary == 1)) 6278 { 6279 config_status("%s:%i: illegal link::class, unknown class '%s' using default of class 'default'", 6280 cep->file->filename, 6281 cep->line_number, 6282 cep->value); 6283 link->class = default_class; 6284 } 6285 link->class->xrefcount++; 6286 } 6287 else if (!strcmp(cep->name, "verify-certificate")) 6288 { 6289 link->verify_certificate = config_checkval(cep->value, CFG_YESNO); 6290 } 6291 else if (!strcmp(cep->name, "options")) 6292 { 6293 link->options = 0; 6294 for (cepp = cep->items; cepp; cepp = cepp->next) 6295 { 6296 long v; 6297 if ((v = nv_find_by_name(_LinkFlags, cepp->name))) 6298 link->options |= v; 6299 } 6300 } 6301 } 6302 6303 /* The default is 'hub *', unless you specify leaf or hub manually. */ 6304 if (!link->hub && !link->leaf) 6305 safe_strdup(link->hub, "*"); 6306 6307 AppendListItem(link, conf_link); 6308 return 0; 6309 } 6310 6311 /** Helper function for erroring on duplicate items. 6312 */ 6313 int config_detect_duplicate(int *var, ConfigEntry *ce, int *errors) 6314 { 6315 if (*var) 6316 { 6317 config_error("%s:%d: Duplicate %s directive", 6318 ce->file->filename, ce->line_number, 6319 ce->name); 6320 (*errors)++; 6321 return 1; 6322 } else { 6323 *var = 1; 6324 } 6325 return 0; 6326 } 6327 6328 int _test_link(ConfigFile *conf, ConfigEntry *ce) 6329 { 6330 ConfigEntry *cep, *cepp, *ceppp; 6331 int errors = 0; 6332 6333 int has_incoming = 0, has_incoming_mask = 0, has_incoming_match = 0, has_outgoing = 0, has_outgoing_file = 0; 6334 int has_outgoing_bind_ip = 0, has_outgoing_hostname = 0, has_outgoing_port = 0; 6335 int has_outgoing_options = 0, has_hub = 0, has_leaf = 0, has_leaf_depth = 0; 6336 int has_password = 0, has_class = 0, has_options = 0; 6337 6338 if (!ce->value) 6339 { 6340 config_error("%s:%i: link without servername. Expected: link servername { ... }", 6341 ce->file->filename, ce->line_number); 6342 return 1; 6343 6344 } 6345 6346 if (!strchr(ce->value, '.')) 6347 { 6348 config_error("%s:%i: link: bogus server name. Expected: link servername { ... }", 6349 ce->file->filename, ce->line_number); 6350 return 1; 6351 } 6352 6353 for (cep = ce->items; cep; cep = cep->next) 6354 { 6355 if (!strcmp(cep->name, "incoming")) 6356 { 6357 config_detect_duplicate(&has_incoming, cep, &errors); 6358 for (cepp = cep->items; cepp; cepp = cepp->next) 6359 { 6360 if (!strcmp(cepp->name, "match")) 6361 { 6362 if (cepp->value || cepp->items) 6363 { 6364 has_incoming_match = 1; 6365 test_match_block(conf, cepp, &errors); 6366 } else 6367 if (config_is_blankorempty(cepp, "link::incoming")) 6368 { 6369 errors++; 6370 continue; 6371 } 6372 } else 6373 if (!strcmp(cepp->name, "mask")) 6374 { 6375 if (cepp->value || cepp->items) 6376 { 6377 has_incoming_mask = 1; 6378 test_match_block(conf, cepp, &errors); 6379 } else 6380 if (config_is_blankorempty(cepp, "link::incoming")) 6381 { 6382 errors++; 6383 continue; 6384 } 6385 } 6386 } 6387 } 6388 else if (!strcmp(cep->name, "outgoing")) 6389 { 6390 config_detect_duplicate(&has_outgoing, cep, &errors); 6391 for (cepp = cep->items; cepp; cepp = cepp->next) 6392 { 6393 if (!strcmp(cepp->name, "bind-ip")) 6394 { 6395 if (config_is_blankorempty(cepp, "link::outgoing")) 6396 { 6397 errors++; 6398 continue; 6399 } 6400 config_detect_duplicate(&has_outgoing_bind_ip, cepp, &errors); 6401 // todo: ipv4 vs ipv6 6402 } 6403 else if (!strcmp(cepp->name, "file")) 6404 { 6405 if (config_is_blankorempty(cepp, "link::outgoing")) 6406 { 6407 errors++; 6408 continue; 6409 } 6410 config_detect_duplicate(&has_outgoing_file, cepp, &errors); 6411 } 6412 else if (!strcmp(cepp->name, "hostname")) 6413 { 6414 if (config_is_blankorempty(cepp, "link::outgoing")) 6415 { 6416 errors++; 6417 continue; 6418 } 6419 config_detect_duplicate(&has_outgoing_hostname, cepp, &errors); 6420 if (strchr(cepp->value, '*') || strchr(cepp->value, '?')) 6421 { 6422 config_error("%s:%i: hostname in link::outgoing(!) cannot contain wildcards", 6423 cepp->file->filename, cepp->line_number); 6424 errors++; 6425 } 6426 } 6427 else if (!strcmp(cepp->name, "port")) 6428 { 6429 if (config_is_blankorempty(cepp, "link::outgoing")) 6430 { 6431 errors++; 6432 continue; 6433 } 6434 config_detect_duplicate(&has_outgoing_port, cepp, &errors); 6435 } 6436 else if (!strcmp(cepp->name, "options")) 6437 { 6438 config_detect_duplicate(&has_outgoing_options, cepp, &errors); 6439 for (ceppp = cepp->items; ceppp; ceppp = ceppp->next) 6440 { 6441 if (!strcmp(ceppp->name, "autoconnect")) 6442 ; 6443 else if (!strcmp(ceppp->name, "ssl") || !strcmp(ceppp->name, "tls")) 6444 ; 6445 else if (!strcmp(ceppp->name, "insecure")) 6446 ; 6447 else 6448 { 6449 config_error_unknownopt(ceppp->file->filename, 6450 ceppp->line_number, "link::outgoing", ceppp->name); 6451 errors++; 6452 } 6453 } 6454 } 6455 else if (!strcmp(cepp->name, "ssl-options") || !strcmp(cepp->name, "tls-options")) 6456 { 6457 test_tlsblock(conf, cepp, &errors); 6458 } 6459 else 6460 { 6461 config_error("%s:%d: Unknown directive '%s'", 6462 cepp->file->filename, cepp->line_number, 6463 config_var(cepp)); 6464 errors++; 6465 } 6466 } 6467 } 6468 else if (!strcmp(cep->name, "password")) 6469 { 6470 config_detect_duplicate(&has_password, cep, &errors); 6471 if (Auth_CheckError(cep) < 0) 6472 { 6473 errors++; 6474 } else { 6475 AuthConfig *auth = AuthBlockToAuthConfig(cep); 6476 /* hm. would be nicer if handled @auth-system I think. ah well.. */ 6477 if ((auth->type != AUTHTYPE_PLAINTEXT) && (auth->type != AUTHTYPE_TLS_CLIENTCERT) && 6478 (auth->type != AUTHTYPE_TLS_CLIENTCERTFP) && (auth->type != AUTHTYPE_SPKIFP)) 6479 { 6480 config_error("%s:%i: password in link block should be plaintext OR should be the " 6481 "certificate or SPKI fingerprint of the remote link (=better)", 6482 cep->file->filename, cep->line_number); 6483 errors++; 6484 } 6485 Auth_FreeAuthConfig(auth); 6486 } 6487 } 6488 else if (!strcmp(cep->name, "hub")) 6489 { 6490 if (config_is_blankorempty(cep, "link")) 6491 { 6492 errors++; 6493 continue; 6494 } 6495 config_detect_duplicate(&has_hub, cep, &errors); 6496 } 6497 else if (!strcmp(cep->name, "leaf")) 6498 { 6499 if (config_is_blankorempty(cep, "link")) 6500 { 6501 errors++; 6502 continue; 6503 } 6504 config_detect_duplicate(&has_leaf, cep, &errors); 6505 } 6506 else if (!strcmp(cep->name, "leaf-depth") || !strcmp(cep->name, "leafdepth")) 6507 { 6508 if (config_is_blankorempty(cep, "link")) 6509 { 6510 errors++; 6511 continue; 6512 } 6513 config_detect_duplicate(&has_leaf_depth, cep, &errors); 6514 } 6515 else if (!strcmp(cep->name, "class")) 6516 { 6517 if (config_is_blankorempty(cep, "link")) 6518 { 6519 errors++; 6520 continue; 6521 } 6522 config_detect_duplicate(&has_class, cep, &errors); 6523 } 6524 else if (!strcmp(cep->name, "ciphers")) 6525 { 6526 config_error("%s:%d: link::ciphers has been moved to link::outgoing::ssl-options::ciphers, " 6527 "see https://www.unrealircd.org/docs/FAQ#link::ciphers_no_longer_works", 6528 cep->file->filename, cep->line_number); 6529 errors++; 6530 } 6531 else if (!strcmp(cep->name, "verify-certificate")) 6532 { 6533 if (config_is_blankorempty(cep, "link")) 6534 { 6535 errors++; 6536 continue; 6537 } 6538 } 6539 else if (!strcmp(cep->name, "options")) 6540 { 6541 config_detect_duplicate(&has_options, cep, &errors); 6542 for (cepp = cep->items; cepp; cepp = cepp->next) 6543 { 6544 if (!strcmp(cepp->name, "quarantine")) 6545 ; 6546 else 6547 { 6548 config_error("%s:%d: link::options only has one possible option ('quarantine', rarely used). " 6549 "Option '%s' is unrecognized. " 6550 "Perhaps you meant to set an outgoing option in link::outgoing::options instead?", 6551 cepp->file->filename, cepp->line_number, cepp->name); 6552 errors++; 6553 } 6554 } 6555 } 6556 else 6557 { 6558 config_error_unknown(cep->file->filename, 6559 cep->line_number, "link", cep->name); 6560 errors++; 6561 continue; 6562 } 6563 } 6564 6565 if (!has_incoming && !has_outgoing) 6566 { 6567 config_error("%s:%d: link block needs at least an incoming or outgoing section.", 6568 ce->file->filename, ce->line_number); 6569 errors++; 6570 } 6571 6572 if (has_incoming) 6573 { 6574 /* If we have an incoming sub-block then we need at least 'mask' and 'password' */ 6575 if (!has_incoming_mask && !has_incoming_match) 6576 { 6577 config_error_missing(ce->file->filename, ce->line_number, "link::incoming::match"); 6578 errors++; 6579 } 6580 if (has_incoming_mask && has_incoming_match) 6581 { 6582 config_error("%s:%d: You cannot have both link::incoming::mask and link::incoming::match. " 6583 "You should only use link::incoming::match.", 6584 ce->file->filename, ce->line_number); 6585 errors++; 6586 } 6587 } 6588 6589 if (has_outgoing) 6590 { 6591 /* If we have an outgoing sub-block then we need at least a hostname and port or a file */ 6592 if (!has_outgoing_file) 6593 { 6594 if (!has_outgoing_hostname) 6595 { 6596 config_error_missing(ce->file->filename, ce->line_number, "link::outgoing::hostname"); 6597 errors++; 6598 } 6599 if (!has_outgoing_port) 6600 { 6601 config_error_missing(ce->file->filename, ce->line_number, "link::outgoing::port"); 6602 errors++; 6603 } 6604 } 6605 else if (has_outgoing_file && (has_outgoing_hostname || has_outgoing_port)) 6606 { 6607 config_error("%s:%d: link block should either have a 'file' (for *NIX domain socket), " 6608 "OR have a 'hostname' and 'port' (for IPv4/IPv6). You cannot combine both in one link block.", 6609 ce->file->filename, ce->line_number); 6610 errors++; 6611 } 6612 } 6613 6614 /* The only other generic options that are required are 'class' and 'password' */ 6615 if (!has_password) 6616 { 6617 config_error_missing(ce->file->filename, ce->line_number, "link::password"); 6618 errors++; 6619 } 6620 if (!has_class) 6621 { 6622 config_error_missing(ce->file->filename, ce->line_number, 6623 "link::class"); 6624 errors++; 6625 } 6626 6627 return errors; 6628 } 6629 6630 int _conf_ban(ConfigFile *conf, ConfigEntry *ce) 6631 { 6632 ConfigEntry *cep; 6633 ConfigItem_ban *ca; 6634 Hook *h; 6635 6636 ca = safe_alloc(sizeof(ConfigItem_ban)); 6637 if (!strcmp(ce->value, "realname")) 6638 ca->flag.type = CONF_BAN_REALNAME; 6639 else if (!strcmp(ce->value, "server")) 6640 ca->flag.type = CONF_BAN_SERVER; 6641 else if (!strcmp(ce->value, "version")) 6642 { 6643 ca->flag.type = CONF_BAN_VERSION; 6644 tempiConf.use_ban_version = 1; /* enable CTCP VERSION on connect */ 6645 } 6646 else { 6647 int value; 6648 safe_free(ca); /* ca isn't used, modules have their own list. */ 6649 for (h = Hooks[HOOKTYPE_CONFIGRUN]; h; h = h->next) 6650 { 6651 value = (*(h->func.intfunc))(conf,ce,CONFIG_BAN); 6652 if (value == 1) 6653 break; 6654 } 6655 return 0; 6656 } 6657 for (cep = ce->items; cep; cep = cep->next) 6658 { 6659 if (!strcmp(cep->name, "mask")) 6660 { 6661 safe_strdup(ca->mask, cep->value); 6662 } 6663 else if (!strcmp(cep->name, "reason")) 6664 safe_strdup(ca->reason, cep->value); 6665 else if (!strcmp(cep->name, "action")) 6666 ca->action = banact_stringtoval(cep->value); 6667 } 6668 AddListItem(ca, conf_ban); 6669 return 0; 6670 } 6671 6672 int _test_ban(ConfigFile *conf, ConfigEntry *ce) 6673 { 6674 ConfigEntry *cep; 6675 int errors = 0; 6676 Hook *h; 6677 char type = 0; 6678 char has_mask = 0, has_action = 0, has_reason = 0; 6679 6680 if (!ce->value) 6681 { 6682 config_error("%s:%i: ban without type", 6683 ce->file->filename, ce->line_number); 6684 return 1; 6685 } 6686 else if (!strcmp(ce->value, "server")) 6687 {} 6688 else if (!strcmp(ce->value, "realname")) 6689 {} 6690 else if (!strcmp(ce->value, "version")) 6691 type = 'v'; 6692 else 6693 { 6694 int used = 0; 6695 for (h = Hooks[HOOKTYPE_CONFIGTEST]; h; h = h->next) 6696 { 6697 int value, errs = 0; 6698 if (h->owner && !(h->owner->flags & MODFLAG_TESTING) 6699 && !(h->owner->options & MOD_OPT_PERM)) 6700 continue; 6701 value = (*(h->func.intfunc))(conf,ce,CONFIG_BAN, &errs); 6702 if (value == 2) 6703 used = 1; 6704 if (value == 1) 6705 { 6706 used = 1; 6707 break; 6708 } 6709 if (value == -1) 6710 { 6711 used = 1; 6712 errors += errs; 6713 break; 6714 } 6715 if (value == -2) 6716 { 6717 used = 1; 6718 errors += errs; 6719 } 6720 } 6721 if (!used) { 6722 config_error("%s:%i: unknown ban type %s", 6723 ce->file->filename, ce->line_number, 6724 ce->value); 6725 return 1; 6726 } 6727 return errors; 6728 } 6729 6730 for (cep = ce->items; cep; cep = cep->next) 6731 { 6732 if (config_is_blankorempty(cep, "ban")) 6733 { 6734 errors++; 6735 continue; 6736 } 6737 if (!strcmp(cep->name, "mask")) 6738 { 6739 if (has_mask) 6740 { 6741 config_warn_duplicate(cep->file->filename, 6742 cep->line_number, "ban::mask"); 6743 continue; 6744 } 6745 has_mask = 1; 6746 } 6747 else if (!strcmp(cep->name, "reason")) 6748 { 6749 if (has_reason) 6750 { 6751 config_warn_duplicate(cep->file->filename, 6752 cep->line_number, "ban::reason"); 6753 continue; 6754 } 6755 has_reason = 1; 6756 } 6757 else if (!strcmp(cep->name, "action")) 6758 { 6759 if (has_action) 6760 { 6761 config_warn_duplicate(cep->file->filename, 6762 cep->line_number, "ban::action"); 6763 } 6764 has_action = 1; 6765 if (!banact_stringtoval(cep->value)) 6766 { 6767 config_error("%s:%i: ban::action has unknown action type '%s'", 6768 cep->file->filename, cep->line_number, 6769 cep->value); 6770 errors++; 6771 } 6772 } 6773 } 6774 6775 if (!has_mask) 6776 { 6777 config_error_missing(ce->file->filename, ce->line_number, 6778 "ban::mask"); 6779 errors++; 6780 } 6781 if (!has_reason) 6782 { 6783 config_error_missing(ce->file->filename, ce->line_number, 6784 "ban::reason"); 6785 errors++; 6786 } 6787 if (has_action && type != 'v') 6788 { 6789 config_error("%s:%d: ban::action specified even though type is not 'version'", 6790 ce->file->filename, ce->line_number); 6791 errors++; 6792 } 6793 return errors; 6794 } 6795 6796 int _conf_require(ConfigFile *conf, ConfigEntry *ce) 6797 { 6798 ConfigEntry *cep; 6799 Hook *h; 6800 char *usermask = NULL; 6801 char *hostmask = NULL; 6802 char *reason = NULL; 6803 6804 if (strcmp(ce->value, "authentication") && strcmp(ce->value, "sasl")) 6805 { 6806 /* Some other block... run modules... */ 6807 int value; 6808 for (h = Hooks[HOOKTYPE_CONFIGRUN]; h; h = h->next) 6809 { 6810 value = (*(h->func.intfunc))(conf,ce,CONFIG_REQUIRE); 6811 if (value == 1) 6812 break; 6813 } 6814 return 0; 6815 } 6816 6817 for (cep = ce->items; cep; cep = cep->next) 6818 { 6819 if (!strcmp(cep->name, "mask")) 6820 { 6821 char buf[512], *p; 6822 strlcpy(buf, cep->value, sizeof(buf)); 6823 p = strchr(buf, '@'); 6824 if (p) 6825 { 6826 *p++ = '\0'; 6827 safe_strdup(usermask, buf); 6828 safe_strdup(hostmask, p); 6829 } else { 6830 safe_strdup(hostmask, cep->value); 6831 } 6832 } 6833 else if (!strcmp(cep->name, "reason")) 6834 safe_strdup(reason, cep->value); 6835 } 6836 6837 if (!usermask) 6838 safe_strdup(usermask, "*"); 6839 6840 if (!reason) 6841 safe_strdup(reason, "-"); 6842 6843 tkl_add_serverban(TKL_KILL, usermask, hostmask, reason, "-config-", 0, TStime(), 1, TKL_FLAG_CONFIG); 6844 safe_free(usermask); 6845 safe_free(hostmask); 6846 safe_free(reason); 6847 return 0; 6848 } 6849 6850 int _test_require(ConfigFile *conf, ConfigEntry *ce) 6851 { 6852 ConfigEntry *cep; 6853 int errors = 0; 6854 Hook *h; 6855 char has_mask = 0, has_reason = 0; 6856 6857 if (!ce->value) 6858 { 6859 config_error("%s:%i: require without type, did you mean 'require authentication'?", 6860 ce->file->filename, ce->line_number); 6861 return 1; 6862 } 6863 if (!strcmp(ce->value, "authentication")) 6864 {} 6865 else if (!strcmp(ce->value, "sasl")) 6866 { 6867 config_warn("%s:%i: the 'require sasl' block is now called 'require authentication'", 6868 ce->file->filename, ce->line_number); 6869 } 6870 else 6871 { 6872 int used = 0; 6873 for (h = Hooks[HOOKTYPE_CONFIGTEST]; h; h = h->next) 6874 { 6875 int value, errs = 0; 6876 if (h->owner && !(h->owner->flags & MODFLAG_TESTING) 6877 && !(h->owner->options & MOD_OPT_PERM)) 6878 continue; 6879 value = (*(h->func.intfunc))(conf,ce,CONFIG_REQUIRE, &errs); 6880 if (value == 2) 6881 used = 1; 6882 if (value == 1) 6883 { 6884 used = 1; 6885 break; 6886 } 6887 if (value == -1) 6888 { 6889 used = 1; 6890 errors += errs; 6891 break; 6892 } 6893 if (value == -2) 6894 { 6895 used = 1; 6896 errors += errs; 6897 } 6898 } 6899 if (!used) { 6900 config_error("%s:%i: unknown require type '%s'", 6901 ce->file->filename, ce->line_number, 6902 ce->value); 6903 return 1; 6904 } 6905 return errors; 6906 } 6907 6908 for (cep = ce->items; cep; cep = cep->next) 6909 { 6910 if (config_is_blankorempty(cep, "require")) 6911 { 6912 errors++; 6913 continue; 6914 } 6915 if (!strcmp(cep->name, "mask")) 6916 { 6917 if (has_mask) 6918 { 6919 config_warn_duplicate(cep->file->filename, 6920 cep->line_number, "require::mask"); 6921 continue; 6922 } 6923 has_mask = 1; 6924 } 6925 else if (!strcmp(cep->name, "reason")) 6926 { 6927 if (has_reason) 6928 { 6929 config_warn_duplicate(cep->file->filename, 6930 cep->line_number, "require::reason"); 6931 continue; 6932 } 6933 has_reason = 1; 6934 } 6935 } 6936 6937 if (!has_mask) 6938 { 6939 config_error_missing(ce->file->filename, ce->line_number, 6940 "require::mask"); 6941 errors++; 6942 } 6943 if (!has_reason) 6944 { 6945 config_error_missing(ce->file->filename, ce->line_number, 6946 "require::reason"); 6947 errors++; 6948 } 6949 return errors; 6950 } 6951 6952 #define CheckDuplicate(cep, name, display) if (settings.has_##name) { config_warn_duplicate((cep)->file->filename, cep->line_number, "set::" display); continue; } else settings.has_##name = 1 6953 6954 void test_tlsblock(ConfigFile *conf, ConfigEntry *cep, int *totalerrors) 6955 { 6956 ConfigEntry *cepp, *ceppp; 6957 int errors = 0; 6958 6959 for (cepp = cep->items; cepp; cepp = cepp->next) 6960 { 6961 if (!strcmp(cepp->name, "renegotiate-timeout")) 6962 { 6963 } 6964 else if (!strcmp(cepp->name, "renegotiate-bytes")) 6965 { 6966 } 6967 else if (!strcmp(cepp->name, "ciphers") || !strcmp(cepp->name, "server-cipher-list")) 6968 { 6969 CheckNull(cepp); 6970 } 6971 else if (!strcmp(cepp->name, "ciphersuites")) 6972 { 6973 CheckNull(cepp); 6974 } 6975 else if (!strcmp(cepp->name, "ecdh-curves")) 6976 { 6977 CheckNull(cepp); 6978 #ifndef HAS_SSL_CTX_SET1_CURVES_LIST 6979 config_error("ecdh-curves specified but your OpenSSL/LibreSSL library does not " 6980 "support setting curves manually by name. Either upgrade to a " 6981 "newer library version or remove the 'ecdh-curves' directive " 6982 "from your configuration file"); 6983 errors++; 6984 #endif 6985 } 6986 else if (!strcmp(cepp->name, "protocols")) 6987 { 6988 char copy[512], *p, *name; 6989 int v = 0; 6990 int option; 6991 char modifier; 6992 6993 CheckNull(cepp); 6994 strlcpy(copy, cepp->value, sizeof(copy)); 6995 for (name = strtoken(&p, copy, ","); name; name = strtoken(&p, NULL, ",")) 6996 { 6997 modifier = '\0'; 6998 option = 0; 6999 7000 if ((*name == '+') || (*name == '-')) 7001 { 7002 modifier = *name; 7003 name++; 7004 } 7005 7006 if (!strcasecmp(name, "All")) 7007 option = TLS_PROTOCOL_ALL; 7008 else if (!strcasecmp(name, "TLSv1")) 7009 option = TLS_PROTOCOL_TLSV1; 7010 else if (!strcasecmp(name, "TLSv1.1")) 7011 option = TLS_PROTOCOL_TLSV1_1; 7012 else if (!strcasecmp(name, "TLSv1.2")) 7013 option = TLS_PROTOCOL_TLSV1_2; 7014 else if (!strcasecmp(name, "TLSv1.3")) 7015 option = TLS_PROTOCOL_TLSV1_3; 7016 else 7017 { 7018 #ifdef SSL_OP_NO_TLSv1_3 7019 config_warn("%s:%i: %s: unknown protocol '%s'. " 7020 "Valid protocols are: TLSv1,TLSv1.1,TLSv1.2,TLSv1.3", 7021 cepp->file->filename, cepp->line_number, config_var(cepp), name); 7022 #else 7023 config_warn("%s:%i: %s: unknown protocol '%s'. " 7024 "Valid protocols are: TLSv1,TLSv1.1,TLSv1.2", 7025 cepp->file->filename, cepp->line_number, config_var(cepp), name); 7026 #endif 7027 } 7028 7029 if (option) 7030 { 7031 if (modifier == '\0') 7032 v = option; 7033 else if (modifier == '+') 7034 v |= option; 7035 else if (modifier == '-') 7036 v &= ~option; 7037 } 7038 } 7039 if (v == 0) 7040 { 7041 config_error("%s:%i: %s: no protocols enabled. Hint: set at least TLSv1.2", 7042 cepp->file->filename, cepp->line_number, config_var(cepp)); 7043 errors++; 7044 } 7045 } 7046 else if (!strcmp(cepp->name, "certificate") || 7047 !strcmp(cepp->name, "key") || 7048 !strcmp(cepp->name, "trusted-ca-file")) 7049 { 7050 char *path; 7051 CheckNull(cepp); 7052 path = convert_to_absolute_path_duplicate(cepp->value, CONFDIR); 7053 if (!file_exists(path)) 7054 { 7055 config_error("%s:%i: %s: could not open '%s': %s", 7056 cepp->file->filename, cepp->line_number, config_var(cepp), 7057 path, strerror(errno)); 7058 safe_free(path); 7059 errors++; 7060 } 7061 safe_free(path); 7062 } 7063 else if (!strcmp(cepp->name, "dh")) 7064 { 7065 /* Support for this undocumented option was silently dropped in 5.0.0. 7066 * Since 5.0.7 we print a warning about it, since you never know 7067 * someone may still have it configured. -- Syzop 7068 */ 7069 config_warn("%s:%d: Not reading DH file '%s'. UnrealIRCd does not support old DH(E), we use modern ECDHE/EECDH. " 7070 "Just remove the 'dh' directive from your config file to get rid of this warning.", 7071 cepp->file->filename, cepp->line_number, 7072 cepp->value ? cepp->value : ""); 7073 } 7074 else if (!strcmp(cepp->name, "outdated-protocols")) 7075 { 7076 char copy[512], *p, *name; 7077 int v = 0; 7078 int option; 7079 char modifier; 7080 7081 CheckNull(cepp); 7082 strlcpy(copy, cepp->value, sizeof(copy)); 7083 for (name = strtoken(&p, copy, ","); name; name = strtoken(&p, NULL, ",")) 7084 { 7085 if (!strcasecmp(name, "All")) 7086 ; 7087 else if (!strcasecmp(name, "TLSv1")) 7088 ; 7089 else if (!strcasecmp(name, "TLSv1.1")) 7090 ; 7091 else if (!strcasecmp(name, "TLSv1.2")) 7092 ; 7093 else if (!strcasecmp(name, "TLSv1.3")) 7094 ; 7095 else 7096 { 7097 #ifdef SSL_OP_NO_TLSv1_3 7098 config_warn("%s:%i: %s: unknown protocol '%s'. " 7099 "Valid protocols are: TLSv1,TLSv1.1,TLSv1.2,TLSv1.3", 7100 cepp->file->filename, cepp->line_number, config_var(cepp), name); 7101 #else 7102 config_warn("%s:%i: %s: unknown protocol '%s'. " 7103 "Valid protocols are: TLSv1,TLSv1.1,TLSv1.2", 7104 cepp->file->filename, cepp->line_number, config_var(cepp), name); 7105 #endif 7106 } 7107 } 7108 } 7109 else if (!strcmp(cepp->name, "outdated-ciphers")) 7110 { 7111 CheckNull(cepp); 7112 } 7113 else if (!strcmp(cepp->name, "options")) 7114 { 7115 for (ceppp = cepp->items; ceppp; ceppp = ceppp->next) 7116 { 7117 if (!nv_find_by_name(_TLSFlags, ceppp->name)) 7118 { 7119 config_error("%s:%i: unknown TLS option '%s'", 7120 ceppp->file->filename, 7121 ceppp->line_number, ceppp->name); 7122 errors ++; 7123 } 7124 } 7125 } 7126 else if (!strcmp(cepp->name, "sts-policy")) 7127 { 7128 int has_port = 0; 7129 int has_duration = 0; 7130 7131 for (ceppp = cepp->items; ceppp; ceppp = ceppp->next) 7132 { 7133 if (!strcmp(ceppp->name, "port")) 7134 { 7135 int port; 7136 CheckNull(ceppp); 7137 port = atoi(ceppp->value); 7138 if ((port < 1) || (port > 65535)) 7139 { 7140 config_error("%s:%i: invalid port number specified in sts-policy::port (%d)", 7141 ceppp->file->filename, ceppp->line_number, port); 7142 errors++; 7143 } 7144 has_port = 1; 7145 } 7146 else if (!strcmp(ceppp->name, "duration")) 7147 { 7148 long duration; 7149 CheckNull(ceppp); 7150 duration = config_checkval(ceppp->value, CFG_TIME); 7151 if (duration < 1) 7152 { 7153 config_error("%s:%i: invalid duration specified in sts-policy::duration (%ld seconds)", 7154 ceppp->file->filename, ceppp->line_number, duration); 7155 errors++; 7156 } 7157 has_duration = 1; 7158 } 7159 else if (!strcmp(ceppp->name, "preload")) 7160 { 7161 CheckNull(ceppp); 7162 } 7163 } 7164 if (!has_port) 7165 { 7166 config_error("%s:%i: sts-policy block without port", 7167 cepp->file->filename, cepp->line_number); 7168 errors++; 7169 } 7170 if (!has_duration) 7171 { 7172 config_error("%s:%i: sts-policy block without duration", 7173 cepp->file->filename, cepp->line_number); 7174 errors++; 7175 } 7176 } 7177 else 7178 { 7179 config_error("%s:%i: unknown directive %s", 7180 cepp->file->filename, cepp->line_number, 7181 config_var(cepp)); 7182 errors++; 7183 } 7184 } 7185 7186 *totalerrors += errors; 7187 } 7188 7189 void free_tls_options(TLSOptions *tlsoptions) 7190 { 7191 if (!tlsoptions) 7192 return; 7193 7194 safe_free(tlsoptions->certificate_file); 7195 safe_free(tlsoptions->key_file); 7196 safe_free(tlsoptions->trusted_ca_file); 7197 safe_free(tlsoptions->ciphers); 7198 safe_free(tlsoptions->ciphersuites); 7199 safe_free(tlsoptions->ecdh_curves); 7200 safe_free(tlsoptions->outdated_protocols); 7201 safe_free(tlsoptions->outdated_ciphers); 7202 memset(tlsoptions, 0, sizeof(TLSOptions)); 7203 safe_free(tlsoptions); 7204 } 7205 7206 void conf_tlsblock(ConfigFile *conf, ConfigEntry *cep, TLSOptions *tlsoptions) 7207 { 7208 ConfigEntry *cepp, *ceppp; 7209 NameValue *ofl; 7210 7211 /* First, inherit settings from set::options::tls */ 7212 if (tlsoptions != tempiConf.tls_options) 7213 { 7214 safe_strdup(tlsoptions->certificate_file, tempiConf.tls_options->certificate_file); 7215 safe_strdup(tlsoptions->key_file, tempiConf.tls_options->key_file); 7216 safe_strdup(tlsoptions->trusted_ca_file, tempiConf.tls_options->trusted_ca_file); 7217 tlsoptions->protocols = tempiConf.tls_options->protocols; 7218 safe_strdup(tlsoptions->ciphers, tempiConf.tls_options->ciphers); 7219 safe_strdup(tlsoptions->ciphersuites, tempiConf.tls_options->ciphersuites); 7220 safe_strdup(tlsoptions->ecdh_curves, tempiConf.tls_options->ecdh_curves); 7221 safe_strdup(tlsoptions->outdated_protocols, tempiConf.tls_options->outdated_protocols); 7222 safe_strdup(tlsoptions->outdated_ciphers, tempiConf.tls_options->outdated_ciphers); 7223 tlsoptions->options = tempiConf.tls_options->options; 7224 tlsoptions->renegotiate_bytes = tempiConf.tls_options->renegotiate_bytes; 7225 tlsoptions->renegotiate_timeout = tempiConf.tls_options->renegotiate_timeout; 7226 tlsoptions->sts_port = tempiConf.tls_options->sts_port; 7227 tlsoptions->sts_duration = tempiConf.tls_options->sts_duration; 7228 tlsoptions->sts_preload = tempiConf.tls_options->sts_preload; 7229 } 7230 7231 /* Now process the options */ 7232 for (cepp = cep->items; cepp; cepp = cepp->next) 7233 { 7234 if (!strcmp(cepp->name, "ciphers") || !strcmp(cepp->name, "server-cipher-list")) 7235 { 7236 safe_strdup(tlsoptions->ciphers, cepp->value); 7237 } 7238 else if (!strcmp(cepp->name, "ciphersuites")) 7239 { 7240 safe_strdup(tlsoptions->ciphersuites, cepp->value); 7241 } 7242 else if (!strcmp(cepp->name, "ecdh-curves")) 7243 { 7244 safe_strdup(tlsoptions->ecdh_curves, cepp->value); 7245 } 7246 else if (!strcmp(cepp->name, "protocols")) 7247 { 7248 char copy[512], *p, *name; 7249 int option; 7250 char modifier; 7251 7252 strlcpy(copy, cepp->value, sizeof(copy)); 7253 tlsoptions->protocols = 0; 7254 for (name = strtoken(&p, copy, ","); name; name = strtoken(&p, NULL, ",")) 7255 { 7256 modifier = '\0'; 7257 option = 0; 7258 7259 if ((*name == '+') || (*name == '-')) 7260 { 7261 modifier = *name; 7262 name++; 7263 } 7264 7265 if (!strcasecmp(name, "All")) 7266 option = TLS_PROTOCOL_ALL; 7267 else if (!strcasecmp(name, "TLSv1")) 7268 option = TLS_PROTOCOL_TLSV1; 7269 else if (!strcasecmp(name, "TLSv1.1")) 7270 option = TLS_PROTOCOL_TLSV1_1; 7271 else if (!strcasecmp(name, "TLSv1.2")) 7272 option = TLS_PROTOCOL_TLSV1_2; 7273 else if (!strcasecmp(name, "TLSv1.3")) 7274 option = TLS_PROTOCOL_TLSV1_3; 7275 7276 if (option) 7277 { 7278 if (modifier == '\0') 7279 tlsoptions->protocols = option; 7280 else if (modifier == '+') 7281 tlsoptions->protocols |= option; 7282 else if (modifier == '-') 7283 tlsoptions->protocols &= ~option; 7284 } 7285 } 7286 } 7287 else if (!strcmp(cepp->name, "certificate")) 7288 { 7289 convert_to_absolute_path(&cepp->value, CONFDIR); 7290 safe_strdup(tlsoptions->certificate_file, cepp->value); 7291 } 7292 else if (!strcmp(cepp->name, "key")) 7293 { 7294 convert_to_absolute_path(&cepp->value, CONFDIR); 7295 safe_strdup(tlsoptions->key_file, cepp->value); 7296 } 7297 else if (!strcmp(cepp->name, "trusted-ca-file")) 7298 { 7299 convert_to_absolute_path(&cepp->value, CONFDIR); 7300 safe_strdup(tlsoptions->trusted_ca_file, cepp->value); 7301 } 7302 else if (!strcmp(cepp->name, "outdated-protocols")) 7303 { 7304 safe_strdup(tlsoptions->outdated_protocols, cepp->value); 7305 } 7306 else if (!strcmp(cepp->name, "outdated-ciphers")) 7307 { 7308 safe_strdup(tlsoptions->outdated_ciphers, cepp->value); 7309 } 7310 else if (!strcmp(cepp->name, "renegotiate-bytes")) 7311 { 7312 tlsoptions->renegotiate_bytes = config_checkval(cepp->value, CFG_SIZE); 7313 } 7314 else if (!strcmp(cepp->name, "renegotiate-timeout")) 7315 { 7316 tlsoptions->renegotiate_timeout = config_checkval(cepp->value, CFG_TIME); 7317 } 7318 else if (!strcmp(cepp->name, "options")) 7319 { 7320 tlsoptions->options = 0; 7321 for (ceppp = cepp->items; ceppp; ceppp = ceppp->next) 7322 { 7323 long v = nv_find_by_name(_TLSFlags, ceppp->name); 7324 tlsoptions->options |= v; 7325 } 7326 } 7327 else if (!strcmp(cepp->name, "sts-policy")) 7328 { 7329 /* We do not inherit ::sts-policy if there is a specific block for this one... */ 7330 tlsoptions->sts_port = 0; 7331 tlsoptions->sts_duration = 0; 7332 tlsoptions->sts_preload = 0; 7333 for (ceppp = cepp->items; ceppp; ceppp = ceppp->next) 7334 { 7335 if (!strcmp(ceppp->name, "port")) 7336 tlsoptions->sts_port = atoi(ceppp->value); 7337 else if (!strcmp(ceppp->name, "duration")) 7338 tlsoptions->sts_duration = config_checkval(ceppp->value, CFG_TIME); 7339 else if (!strcmp(ceppp->name, "preload")) 7340 tlsoptions->sts_preload = config_checkval(ceppp->value, CFG_YESNO); 7341 } 7342 } 7343 } 7344 } 7345 7346 int _conf_set(ConfigFile *conf, ConfigEntry *ce) 7347 { 7348 ConfigEntry *cep, *cepp, *ceppp, *cep4; 7349 Hook *h; 7350 7351 for (cep = ce->items; cep; cep = cep->next) 7352 { 7353 if (!strcmp(cep->name, "kline-address")) { 7354 safe_strdup(tempiConf.kline_address, cep->value); 7355 } 7356 if (!strcmp(cep->name, "gline-address")) { 7357 safe_strdup(tempiConf.gline_address, cep->value); 7358 } 7359 else if (!strcmp(cep->name, "modes-on-connect")) { 7360 tempiConf.conn_modes = (long) set_usermode(cep->value); 7361 } 7362 else if (!strcmp(cep->name, "modes-on-oper")) { 7363 tempiConf.oper_modes = (long) set_usermode(cep->value); 7364 } 7365 else if (!strcmp(cep->name, "modes-on-join")) { 7366 conf_channelmodes(cep->value, &tempiConf.modes_on_join); 7367 tempiConf.modes_on_join_set = 1; 7368 } 7369 else if (!strcmp(cep->name, "snomask-on-oper")) { 7370 safe_strdup(tempiConf.oper_snomask, cep->value); 7371 } 7372 else if (!strcmp(cep->name, "server-notice-colors")) { 7373 tempiConf.server_notice_colors = config_checkval(cep->value, CFG_YESNO); 7374 } 7375 else if (!strcmp(cep->name, "server-notice-show-event")) { 7376 tempiConf.server_notice_show_event = config_checkval(cep->value, CFG_YESNO); 7377 } 7378 else if (!strcmp(cep->name, "level-on-join")) { 7379 const char *res = channellevel_to_string(cep->value); /* 'halfop', etc */ 7380 if (!res) 7381 { 7382 /* This check needs to be here, in config run, because 7383 * now the channel modules are initialized and we know 7384 * which ones are available. This same information is 7385 * not available during config test, so we can't test 7386 * for it there like we normally do. 7387 */ 7388 if (!valid_channel_access_mode_letter(*cep->value)) 7389 { 7390 config_warn("%s:%d: set::level-on-join: Unknown mode (access level) '%c'. " 7391 "That mode does not exist or is not a valid access mode " 7392 "like vhoaq.", 7393 cep->file->filename, cep->line_number, 7394 *cep->value); 7395 config_warn("Falling back to to set::level-on-join none; now. " 7396 "This is probably not what you want!!!"); 7397 } 7398 res = cep->value; /* if we reach this.. then it is a single letter */ 7399 } 7400 safe_strdup(tempiConf.level_on_join, res); 7401 } 7402 else if (!strcmp(cep->name, "static-quit")) { 7403 safe_strdup(tempiConf.static_quit, cep->value); 7404 } 7405 else if (!strcmp(cep->name, "static-part")) { 7406 safe_strdup(tempiConf.static_part, cep->value); 7407 } 7408 else if (!strcmp(cep->name, "who-limit")) { 7409 tempiConf.who_limit = atol(cep->value); 7410 } 7411 else if (!strcmp(cep->name, "maxbans")) { 7412 tempiConf.maxbans = atol(cep->value); 7413 } 7414 else if (!strcmp(cep->name, "maxbanlength")) { 7415 tempiConf.maxbanlength = atol(cep->value); 7416 } 7417 else if (!strcmp(cep->name, "silence-limit")) { 7418 tempiConf.silence_limit = atol(cep->value); 7419 } 7420 else if (!strcmp(cep->name, "auto-join")) { 7421 safe_strdup(tempiConf.auto_join_chans, cep->value); 7422 } 7423 else if (!strcmp(cep->name, "oper-auto-join")) { 7424 safe_strdup(tempiConf.oper_auto_join_chans, cep->value); 7425 } 7426 else if (!strcmp(cep->name, "check-target-nick-bans")) { 7427 tempiConf.check_target_nick_bans = config_checkval(cep->value, CFG_YESNO); 7428 } 7429 else if (!strcmp(cep->name, "ping-cookie")) { 7430 tempiConf.ping_cookie = config_checkval(cep->value, CFG_YESNO); 7431 } 7432 else if (!strcmp(cep->name, "watch-away-notification")) { 7433 tempiConf.watch_away_notification = config_checkval(cep->value, CFG_YESNO); 7434 } 7435 else if (!strcmp(cep->name, "uhnames")) { 7436 tempiConf.uhnames = config_checkval(cep->value, CFG_YESNO); 7437 } 7438 else if (!strcmp(cep->name, "allow-userhost-change")) { 7439 if (!strcasecmp(cep->value, "always")) 7440 tempiConf.userhost_allowed = UHALLOW_ALWAYS; 7441 else if (!strcasecmp(cep->value, "never")) 7442 tempiConf.userhost_allowed = UHALLOW_NEVER; 7443 else if (!strcasecmp(cep->value, "not-on-channels")) 7444 tempiConf.userhost_allowed = UHALLOW_NOCHANS; 7445 else 7446 tempiConf.userhost_allowed = UHALLOW_REJOIN; 7447 } 7448 else if (!strcmp(cep->name, "channel-command-prefix")) { 7449 safe_strdup(tempiConf.channel_command_prefix, cep->value); 7450 } 7451 else if (!strcmp(cep->name, "restrict-usermodes")) { 7452 int i; 7453 char *p = safe_alloc(strlen(cep->value) + 1), *x = p; 7454 /* The data should be something like 'Gw' or something, 7455 * but just in case users use '+Gw' then ignore the + (and -). 7456 */ 7457 for (i=0; i < strlen(cep->value); i++) 7458 if ((cep->value[i] != '+') && (cep->value[i] != '-')) 7459 *x++ = cep->value[i]; 7460 *x = '\0'; 7461 tempiConf.restrict_usermodes = p; 7462 } 7463 else if (!strcmp(cep->name, "restrict-channelmodes")) { 7464 int i; 7465 char *p = safe_alloc(strlen(cep->value) + 1), *x = p; 7466 /* The data should be something like 'GL' or something, 7467 * but just in case users use '+GL' then ignore the + (and -). 7468 */ 7469 for (i=0; i < strlen(cep->value); i++) 7470 if ((cep->value[i] != '+') && (cep->value[i] != '-')) 7471 *x++ = cep->value[i]; 7472 *x = '\0'; 7473 tempiConf.restrict_channelmodes = p; 7474 } 7475 else if (!strcmp(cep->name, "restrict-extendedbans")) { 7476 safe_strdup(tempiConf.restrict_extendedbans, cep->value); 7477 } 7478 else if (!strcmp(cep->name, "named-extended-bans")) { 7479 tempiConf.named_extended_bans = config_checkval(cep->value, CFG_YESNO); 7480 } 7481 else if (!strcmp(cep->name, "anti-spam-quit-message-time")) { 7482 tempiConf.anti_spam_quit_message_time = config_checkval(cep->value,CFG_TIME); 7483 } 7484 else if (!strcmp(cep->name, "allow-user-stats")) { 7485 if (!cep->items) 7486 { 7487 safe_strdup(tempiConf.allow_user_stats, cep->value); 7488 } 7489 else 7490 { 7491 for (cepp = cep->items; cepp; cepp = cepp->next) 7492 { 7493 OperStat *os = safe_alloc(sizeof(OperStat)); 7494 safe_strdup(os->flag, cepp->name); 7495 AddListItem(os, tempiConf.allow_user_stats_ext); 7496 } 7497 } 7498 } 7499 else if (!strcmp(cep->name, "maxchannelsperuser")) { 7500 tempiConf.maxchannelsperuser = atoi(cep->value); 7501 } 7502 else if (!strcmp(cep->name, "ping-warning")) { 7503 tempiConf.ping_warning = atoi(cep->value); 7504 } 7505 else if (!strcmp(cep->name, "maxdccallow")) { 7506 tempiConf.maxdccallow = atoi(cep->value); 7507 } 7508 else if (!strcmp(cep->name, "max-targets-per-command")) 7509 { 7510 for (cepp = cep->items; cepp; cepp = cepp->next) 7511 { 7512 int v; 7513 if (!strcmp(cepp->value, "max")) 7514 v = MAXTARGETS_MAX; 7515 else 7516 v = atoi(cepp->value); 7517 setmaxtargets(cepp->name, v); 7518 } 7519 } 7520 else if (!strcmp(cep->name, "network-name")) { 7521 char *tmp; 7522 safe_strdup(tempiConf.network_name, cep->value); 7523 for (tmp = cep->value; *cep->value; cep->value++) { 7524 if (*cep->value == ' ') 7525 *cep->value='-'; 7526 } 7527 safe_strdup(tempiConf.network_name_005, tmp); 7528 cep->value = tmp; 7529 } 7530 else if (!strcmp(cep->name, "default-server")) { 7531 safe_strdup(tempiConf.default_server, cep->value); 7532 } 7533 else if (!strcmp(cep->name, "services-server")) { 7534 safe_strdup(tempiConf.services_name, cep->value); 7535 } 7536 else if (!strcmp(cep->name, "sasl-server")) { 7537 safe_strdup(tempiConf.sasl_server, cep->value); 7538 } 7539 else if (!strcmp(cep->name, "stats-server")) { 7540 safe_strdup(tempiConf.stats_server, cep->value); 7541 } 7542 else if (!strcmp(cep->name, "help-channel")) { 7543 safe_strdup(tempiConf.helpchan, cep->value); 7544 } 7545 else if (!strcmp(cep->name, "cloak-prefix") || !strcmp(cep->name, "hiddenhost-prefix")) { 7546 safe_strdup(tempiConf.cloak_prefix, cep->value); 7547 } 7548 else if (!strcmp(cep->name, "hide-ban-reason")) { 7549 tempiConf.hide_ban_reason = config_checkval(cep->value, CFG_YESNO); 7550 } 7551 else if (!strcmp(cep->name, "prefix-quit")) { 7552 if (!strcmp(cep->value, "0") || !strcmp(cep->value, "no")) 7553 safe_free(tempiConf.prefix_quit); 7554 else 7555 safe_strdup(tempiConf.prefix_quit, cep->value); 7556 } 7557 else if (!strcmp(cep->name, "link")) { 7558 for (cepp = cep->items; cepp; cepp = cepp->next) { 7559 if (!strcmp(cepp->name, "bind-ip")) { 7560 safe_strdup(tempiConf.link_bindip, cepp->value); 7561 } 7562 } 7563 } 7564 else if (!strcmp(cep->name, "anti-flood")) { 7565 for (cepp = cep->items; cepp; cepp = cepp->next) 7566 { 7567 int lag_penalty = -1; 7568 int lag_penalty_bytes = -1; 7569 for (ceppp = cepp->items; ceppp; ceppp = ceppp->next) 7570 { 7571 /* Check hooks first */ 7572 int used = 0; 7573 for (h = Hooks[HOOKTYPE_CONFIGRUN]; h; h = h->next) 7574 { 7575 used = (*(h->func.intfunc))(conf,ceppp,CONFIG_SET_ANTI_FLOOD); 7576 if (used == 1) 7577 break; 7578 } 7579 if (used == 1) 7580 continue; /* module handled it */ 7581 if (used == 2) 7582 break; /* module handled it and we must stop entire block processing */ 7583 if (!strcmp(ceppp->name, "handshake-data-flood")) 7584 { 7585 for (cep4 = ceppp->items; cep4; cep4 = cep4->next) 7586 { 7587 if (!strcmp(cep4->name, "amount")) 7588 tempiConf.handshake_data_flood_amount = config_checkval(cep4->value, CFG_SIZE); 7589 else if (!strcmp(cep4->name, "ban-time")) 7590 tempiConf.handshake_data_flood_ban_time = config_checkval(cep4->value, CFG_TIME); 7591 else if (!strcmp(cep4->name, "ban-action")) 7592 tempiConf.handshake_data_flood_ban_action = banact_stringtoval(cep4->value); 7593 } 7594 } 7595 else if (!strcmp(ceppp->name, "away-flood")) 7596 { 7597 config_parse_flood_generic(ceppp->value, &tempiConf, cepp->name, FLD_AWAY); 7598 } 7599 else if (!strcmp(ceppp->name, "nick-flood")) 7600 { 7601 config_parse_flood_generic(ceppp->value, &tempiConf, cepp->name, FLD_NICK); 7602 } 7603 else if (!strcmp(ceppp->name, "vhost-flood")) 7604 { 7605 config_parse_flood_generic(ceppp->value, &tempiConf, cepp->name, FLD_VHOST); 7606 } 7607 else if (!strcmp(ceppp->name, "join-flood")) 7608 { 7609 config_parse_flood_generic(ceppp->value, &tempiConf, cepp->name, FLD_JOIN); 7610 } 7611 else if (!strcmp(ceppp->name, "invite-flood")) 7612 { 7613 config_parse_flood_generic(ceppp->value, &tempiConf, cepp->name, FLD_INVITE); 7614 } 7615 else if (!strcmp(ceppp->name, "knock-flood")) 7616 { 7617 config_parse_flood_generic(ceppp->value, &tempiConf, cepp->name, FLD_KNOCK); 7618 } 7619 else if (!strcmp(ceppp->name, "lag-penalty")) 7620 { 7621 lag_penalty = atoi(ceppp->value); 7622 } 7623 else if (!strcmp(ceppp->name, "lag-penalty-bytes")) 7624 { 7625 lag_penalty_bytes = config_checkval(ceppp->value, CFG_SIZE); 7626 if (lag_penalty_bytes <= 0) 7627 lag_penalty_bytes = INT_MAX; 7628 } 7629 else if (!strcmp(ceppp->name, "connect-flood")) 7630 { 7631 int cnt, period; 7632 config_parse_flood(ceppp->value, &cnt, &period); 7633 tempiConf.throttle_count = cnt; 7634 tempiConf.throttle_period = period; 7635 } 7636 else if (!strcmp(ceppp->name, "max-concurrent-conversations")) 7637 { 7638 /* We use a hack here to make it fit our storage format */ 7639 char buf[64]; 7640 int users=0; 7641 long every=0; 7642 for (cep4 = ceppp->items; cep4; cep4 = cep4->next) 7643 { 7644 if (!strcmp(cep4->name, "users")) 7645 { 7646 users = atoi(cep4->value); 7647 } else 7648 if (!strcmp(cep4->name, "new-user-every")) 7649 { 7650 every = config_checkval(cep4->value, CFG_TIME); 7651 } 7652 } 7653 snprintf(buf, sizeof(buf), "%d:%ld", users, every); 7654 config_parse_flood_generic(buf, &tempiConf, cepp->name, FLD_CONVERSATIONS); 7655 } 7656 } 7657 if ((lag_penalty != -1) && (lag_penalty_bytes != -1)) 7658 { 7659 /* We use a hack here to make it fit our storage format */ 7660 char buf[64]; 7661 snprintf(buf, sizeof(buf), "%d:%d", lag_penalty_bytes, lag_penalty); 7662 config_parse_flood_generic(buf, &tempiConf, cepp->name, FLD_LAG_PENALTY); 7663 } 7664 } 7665 } 7666 else if (!strcmp(cep->name, "options")) { 7667 for (cepp = cep->items; cepp; cepp = cepp->next) { 7668 if (!strcmp(cepp->name, "hide-ulines")) { 7669 tempiConf.hide_ulines = 1; 7670 } 7671 else if (!strcmp(cepp->name, "flat-map")) { 7672 tempiConf.flat_map = 1; 7673 } 7674 else if (!strcmp(cepp->name, "show-opermotd")) { 7675 tempiConf.show_opermotd = 1; 7676 } 7677 else if (!strcmp(cepp->name, "identd-check")) { 7678 tempiConf.ident_check = 1; 7679 } 7680 else if (!strcmp(cepp->name, "fail-oper-warn")) { 7681 tempiConf.fail_oper_warn = 1; 7682 } 7683 else if (!strcmp(cepp->name, "show-connect-info")) { 7684 tempiConf.show_connect_info = 1; 7685 } 7686 else if (!strcmp(cepp->name, "no-connect-tls-info")) { 7687 tempiConf.no_connect_tls_info = 1; 7688 } 7689 else if (!strcmp(cepp->name, "dont-resolve")) { 7690 tempiConf.dont_resolve = 1; 7691 } 7692 else if (!strcmp(cepp->name, "mkpasswd-for-everyone")) { 7693 tempiConf.mkpasswd_for_everyone = 1; 7694 } 7695 else if (!strcmp(cepp->name, "allow-insane-bans")) { 7696 tempiConf.allow_insane_bans = 1; 7697 } 7698 else if (!strcmp(cepp->name, "allow-part-if-shunned")) { 7699 tempiConf.allow_part_if_shunned = 1; 7700 } 7701 else if (!strcmp(cepp->name, "disable-cap")) { 7702 tempiConf.disable_cap = 1; 7703 } 7704 else if (!strcmp(cepp->name, "disable-ipv6")) { 7705 /* other code handles this */ 7706 } 7707 } 7708 } 7709 else if (!strcmp(cep->name, "cloak-keys")) 7710 { 7711 for (h = Hooks[HOOKTYPE_CONFIGRUN]; h; h = h->next) 7712 { 7713 int value; 7714 value = (*(h->func.intfunc))(conf, cep, CONFIG_CLOAKKEYS); 7715 if (value == 1) 7716 break; 7717 } 7718 } 7719 else if (!strcmp(cep->name, "ident")) 7720 { 7721 for (cepp = cep->items; cepp; cepp = cepp->next) 7722 { 7723 if (!strcmp(cepp->name, "connect-timeout")) 7724 tempiConf.ident_connect_timeout = config_checkval(cepp->value,CFG_TIME); 7725 if (!strcmp(cepp->name, "read-timeout")) 7726 tempiConf.ident_read_timeout = config_checkval(cepp->value,CFG_TIME); 7727 } 7728 } 7729 else if (!strcmp(cep->name, "spamfilter")) 7730 { 7731 for (cepp = cep->items; cepp; cepp = cepp->next) 7732 { 7733 if (!strcmp(cepp->name, "ban-time")) 7734 tempiConf.spamfilter_ban_time = config_checkval(cepp->value,CFG_TIME); 7735 else if (!strcmp(cepp->name, "ban-reason")) 7736 safe_strdup(tempiConf.spamfilter_ban_reason, cepp->value); 7737 else if (!strcmp(cepp->name, "virus-help-channel")) 7738 safe_strdup(tempiConf.spamfilter_virus_help_channel, cepp->value); 7739 else if (!strcmp(cepp->name, "virus-help-channel-deny")) 7740 tempiConf.spamfilter_vchan_deny = config_checkval(cepp->value,CFG_YESNO); 7741 else if (!strcmp(cepp->name, "except")) 7742 { 7743 char *name, *p; 7744 SpamExcept *e; 7745 safe_strdup(tempiConf.spamexcept_line, cepp->value); 7746 for (name = strtoken(&p, cepp->value, ","); name; name = strtoken(&p, NULL, ",")) 7747 { 7748 if (*name == ' ') 7749 name++; 7750 if (*name) 7751 { 7752 e = safe_alloc(sizeof(SpamExcept) + strlen(name)); 7753 strcpy(e->name, name); 7754 AddListItem(e, tempiConf.spamexcept); 7755 } 7756 } 7757 } 7758 else if (!strcmp(cepp->name, "detect-slow-warn")) 7759 { 7760 tempiConf.spamfilter_detectslow_warn = atol(cepp->value); 7761 } 7762 else if (!strcmp(cepp->name, "detect-slow-fatal")) 7763 { 7764 tempiConf.spamfilter_detectslow_fatal = atol(cepp->value); 7765 } 7766 else if (!strcmp(cepp->name, "stop-on-first-match")) 7767 { 7768 tempiConf.spamfilter_stop_on_first_match = config_checkval(cepp->value, CFG_YESNO); 7769 } 7770 else if (!strcmp(cepp->name, "utf8")) 7771 { 7772 tempiConf.spamfilter_utf8 = config_checkval(cepp->value, CFG_YESNO); 7773 } 7774 } 7775 } 7776 else if (!strcmp(cep->name, "default-bantime")) 7777 { 7778 tempiConf.default_bantime = config_checkval(cep->value,CFG_TIME); 7779 } 7780 else if (!strcmp(cep->name, "ban-version-tkl-time")) 7781 { 7782 tempiConf.ban_version_tkl_time = config_checkval(cep->value,CFG_TIME); 7783 } 7784 else if (!strcmp(cep->name, "min-nick-length")) { 7785 int v = atoi(cep->value); 7786 tempiConf.min_nick_length = v; 7787 } 7788 else if (!strcmp(cep->name, "nick-length")) { 7789 int v = atoi(cep->value); 7790 tempiConf.nick_length = v; 7791 } 7792 else if (!strcmp(cep->name, "topic-length")) { 7793 int v = atoi(cep->value); 7794 tempiConf.topic_length = v; 7795 } 7796 else if (!strcmp(cep->name, "away-length")) { 7797 int v = atoi(cep->value); 7798 tempiConf.away_length = v; 7799 } 7800 else if (!strcmp(cep->name, "kick-length")) { 7801 int v = atoi(cep->value); 7802 tempiConf.kick_length = v; 7803 } 7804 else if (!strcmp(cep->name, "quit-length")) { 7805 int v = atoi(cep->value); 7806 tempiConf.quit_length = v; 7807 } 7808 else if (!strcmp(cep->name, "ssl") || !strcmp(cep->name, "tls")) { 7809 /* no need to alloc tempiConf.tls_options since config_defaults() already ensures it exists */ 7810 conf_tlsblock(conf, cep, tempiConf.tls_options); 7811 } 7812 else if (!strcmp(cep->name, "plaintext-policy")) 7813 { 7814 for (cepp = cep->items; cepp; cepp = cepp->next) 7815 { 7816 if (!strcmp(cepp->name, "user")) 7817 tempiConf.plaintext_policy_user = policy_strtoval(cepp->value); 7818 else if (!strcmp(cepp->name, "oper")) 7819 tempiConf.plaintext_policy_oper = policy_strtoval(cepp->value); 7820 else if (!strcmp(cepp->name, "server")) 7821 tempiConf.plaintext_policy_server = policy_strtoval(cepp->value); 7822 else if (!strcmp(cepp->name, "user-message")) 7823 addmultiline(&tempiConf.plaintext_policy_user_message, cepp->value); 7824 else if (!strcmp(cepp->name, "oper-message")) 7825 addmultiline(&tempiConf.plaintext_policy_oper_message, cepp->value); 7826 } 7827 } 7828 else if (!strcmp(cep->name, "outdated-tls-policy")) 7829 { 7830 for (cepp = cep->items; cepp; cepp = cepp->next) 7831 { 7832 if (!strcmp(cepp->name, "user")) 7833 tempiConf.outdated_tls_policy_user = policy_strtoval(cepp->value); 7834 else if (!strcmp(cepp->name, "oper")) 7835 tempiConf.outdated_tls_policy_oper = policy_strtoval(cepp->value); 7836 else if (!strcmp(cepp->name, "server")) 7837 tempiConf.outdated_tls_policy_server = policy_strtoval(cepp->value); 7838 else if (!strcmp(cepp->name, "user-message")) 7839 safe_strdup(tempiConf.outdated_tls_policy_user_message, cepp->value); 7840 else if (!strcmp(cepp->name, "oper-message")) 7841 safe_strdup(tempiConf.outdated_tls_policy_oper_message, cepp->value); 7842 } 7843 } 7844 else if (!strcmp(cep->name, "default-ipv6-clone-mask")) 7845 { 7846 tempiConf.default_ipv6_clone_mask = atoi(cep->value); 7847 } 7848 else if (!strcmp(cep->name, "hide-list")) { 7849 for (cepp = cep->items; cepp; cepp = cepp->next) 7850 { 7851 if (!strcmp(cepp->name, "deny-channel")) 7852 { 7853 tempiConf.hide_list = 1; 7854 /* if we would expand this later then change this to a bitmask or struct or whatever */ 7855 } 7856 } 7857 } 7858 else if (!strcmp(cep->name, "max-unknown-connections-per-ip")) 7859 { 7860 tempiConf.max_unknown_connections_per_ip = atoi(cep->value); 7861 } 7862 else if (!strcmp(cep->name, "handshake-timeout")) 7863 { 7864 tempiConf.handshake_timeout = config_checkval(cep->value, CFG_TIME); 7865 } 7866 else if (!strcmp(cep->name, "sasl-timeout")) 7867 { 7868 tempiConf.sasl_timeout = config_checkval(cep->value, CFG_TIME); 7869 } 7870 else if (!strcmp(cep->name, "handshake-delay")) 7871 { 7872 tempiConf.handshake_delay = config_checkval(cep->value, CFG_TIME); 7873 } 7874 else if (!strcmp(cep->name, "automatic-ban-target")) 7875 { 7876 tempiConf.automatic_ban_target = ban_target_strtoval(cep->value); 7877 } 7878 else if (!strcmp(cep->name, "manual-ban-target")) 7879 { 7880 tempiConf.manual_ban_target = ban_target_strtoval(cep->value); 7881 } 7882 else if (!strcmp(cep->name, "reject-message")) 7883 { 7884 for (cepp = cep->items; cepp; cepp = cepp->next) 7885 { 7886 if (!strcmp(cepp->name, "too-many-connections")) 7887 safe_strdup(tempiConf.reject_message_too_many_connections, cepp->value); 7888 else if (!strcmp(cepp->name, "server-full")) 7889 safe_strdup(tempiConf.reject_message_server_full, cepp->value); 7890 else if (!strcmp(cepp->name, "unauthorized")) 7891 safe_strdup(tempiConf.reject_message_unauthorized, cepp->value); 7892 else if (!strcmp(cepp->name, "kline")) 7893 safe_strdup(tempiConf.reject_message_kline, cepp->value); 7894 else if (!strcmp(cepp->name, "gline")) 7895 safe_strdup(tempiConf.reject_message_gline, cepp->value); 7896 } 7897 } 7898 else if (!strcmp(cep->name, "topic-setter")) 7899 { 7900 if (!strcmp(cep->value, "nick")) 7901 tempiConf.topic_setter = SETTER_NICK; 7902 else if (!strcmp(cep->value, "nick-user-host")) 7903 tempiConf.topic_setter = SETTER_NICK_USER_HOST; 7904 } 7905 else if (!strcmp(cep->name, "ban-setter")) 7906 { 7907 if (!strcmp(cep->value, "nick")) 7908 tempiConf.ban_setter = SETTER_NICK; 7909 else if (!strcmp(cep->value, "nick-user-host")) 7910 tempiConf.ban_setter = SETTER_NICK_USER_HOST; 7911 } 7912 else if (!strcmp(cep->name, "ban-setter-sync") || !strcmp(cep->name, "ban-setter-synch")) 7913 { 7914 tempiConf.ban_setter_sync = config_checkval(cep->value, CFG_YESNO); 7915 } 7916 else if (!strcmp(cep->name, "part-instead-of-quit-on-comment-change")) 7917 { 7918 tempiConf.part_instead_of_quit_on_comment_change = config_checkval(cep->value, CFG_YESNO); 7919 } 7920 else if (!strcmp(cep->name, "broadcast-channel-messages")) 7921 { 7922 if (!strcmp(cep->value, "auto")) 7923 tempiConf.broadcast_channel_messages = BROADCAST_CHANNEL_MESSAGES_AUTO; 7924 else if (!strcmp(cep->value, "always")) 7925 tempiConf.broadcast_channel_messages = BROADCAST_CHANNEL_MESSAGES_ALWAYS; 7926 else if (!strcmp(cep->value, "never")) 7927 tempiConf.broadcast_channel_messages = BROADCAST_CHANNEL_MESSAGES_NEVER; 7928 } 7929 else if (!strcmp(cep->name, "allowed-channelchars")) 7930 { 7931 tempiConf.allowed_channelchars = allowed_channelchars_strtoval(cep->value); 7932 } 7933 else if (!strcmp(cep->name, "hide-idle-time")) 7934 { 7935 for (cepp = cep->items; cepp; cepp = cepp->next) 7936 { 7937 if (!strcmp(cepp->name, "policy")) 7938 tempiConf.hide_idle_time = hideidletime_strtoval(cepp->value); 7939 } 7940 } else if (!strcmp(cep->name, "limit-svscmds")) 7941 { 7942 if (!strcmp(cep->value, "ulines")) 7943 tempiConf.limit_svscmds = LIMIT_SVSCMDS_ULINES; 7944 else 7945 tempiConf.limit_svscmds = LIMIT_SVSCMDS_SERVERS; 7946 } else 7947 { 7948 int value; 7949 for (h = Hooks[HOOKTYPE_CONFIGRUN]; h; h = h->next) 7950 { 7951 value = (*(h->func.intfunc))(conf,cep,CONFIG_SET); 7952 if (value == 1) 7953 break; 7954 } 7955 } 7956 } 7957 return 0; 7958 } 7959 7960 int _test_set(ConfigFile *conf, ConfigEntry *ce) 7961 { 7962 ConfigEntry *cep, *cepp, *ceppp, *cep4; 7963 int tempi; 7964 int errors = 0; 7965 Hook *h; 7966 7967 for (cep = ce->items; cep; cep = cep->next) 7968 { 7969 if (!strcmp(cep->name, "kline-address")) { 7970 CheckNull(cep); 7971 CheckDuplicate(cep, kline_address, "kline-address"); 7972 if (!strchr(cep->value, '@') && !strchr(cep->value, ':')) 7973 { 7974 config_error("%s:%i: set::kline-address must be an e-mail or an URL", 7975 cep->file->filename, cep->line_number); 7976 errors++; 7977 continue; 7978 } 7979 else if (match_simple("*@unrealircd.com", cep->value) || match_simple("*@unrealircd.org",cep->value) || match_simple("unreal-*@lists.sourceforge.net",cep->value)) 7980 { 7981 config_error("%s:%i: set::kline-address may not be an UnrealIRCd Team address", 7982 cep->file->filename, cep->line_number); 7983 errors++; continue; 7984 } 7985 } 7986 else if (!strcmp(cep->name, "gline-address")) { 7987 CheckNull(cep); 7988 CheckDuplicate(cep, gline_address, "gline-address"); 7989 if (!strchr(cep->value, '@') && !strchr(cep->value, ':')) 7990 { 7991 config_error("%s:%i: set::gline-address must be an e-mail or an URL", 7992 cep->file->filename, cep->line_number); 7993 errors++; 7994 continue; 7995 } 7996 else if (match_simple("*@unrealircd.com", cep->value) || match_simple("*@unrealircd.org",cep->value) || match_simple("unreal-*@lists.sourceforge.net",cep->value)) 7997 { 7998 config_error("%s:%i: set::gline-address may not be an UnrealIRCd Team address", 7999 cep->file->filename, cep->line_number); 8000 errors++; continue; 8001 } 8002 } 8003 else if (!strcmp(cep->name, "modes-on-connect")) { 8004 char *p; 8005 CheckNull(cep); 8006 CheckDuplicate(cep, modes_on_connect, "modes-on-connect"); 8007 for (p = cep->value; *p; p++) 8008 if (strchr("orzSHqtW", *p)) 8009 { 8010 config_error("%s:%i: set::modes-on-connect may not include mode '%c'", 8011 cep->file->filename, cep->line_number, *p); 8012 errors++; 8013 } 8014 } 8015 else if (!strcmp(cep->name, "modes-on-join")) { 8016 char *c; 8017 struct ChMode temp; 8018 memset(&temp, 0, sizeof(temp)); 8019 CheckNull(cep); 8020 CheckDuplicate(cep, modes_on_join, "modes-on-join"); 8021 for (c = cep->value; *c; c++) 8022 { 8023 if (*c == ' ') 8024 break; /* don't check the parameter ;p */ 8025 switch (*c) 8026 { 8027 case 'q': 8028 case 'a': 8029 case 'o': 8030 case 'h': 8031 case 'v': 8032 case 'b': 8033 case 'e': 8034 case 'I': 8035 config_error("%s:%i: set::modes-on-join may not contain +%c", 8036 cep->file->filename, cep->line_number, *c); 8037 errors++; 8038 break; 8039 } 8040 } 8041 /* We can't really verify much here. 8042 * The channel mode modules have not been initialized 8043 * yet at this point, so we can't really verify much 8044 * here. 8045 */ 8046 } 8047 else if (!strcmp(cep->name, "modes-on-oper")) { 8048 char *p; 8049 CheckNull(cep); 8050 CheckDuplicate(cep, modes_on_oper, "modes-on-oper"); 8051 for (p = cep->value; *p; p++) 8052 if (strchr("orzS", *p)) 8053 { 8054 config_error("%s:%i: set::modes-on-oper may not include mode '%c'", 8055 cep->file->filename, cep->line_number, *p); 8056 errors++; 8057 } 8058 set_usermode(cep->value); 8059 } 8060 else if (!strcmp(cep->name, "snomask-on-oper")) { 8061 char *wrong_snomask; 8062 CheckNull(cep); 8063 CheckDuplicate(cep, snomask_on_oper, "snomask-on-oper"); 8064 if (!is_valid_snomask_string_testing(cep->value, &wrong_snomask)) 8065 { 8066 config_error("%s:%i: set::snomask-on-oper contains unknown snomask letter(s) '%s'", 8067 cep->file->filename, cep->line_number, wrong_snomask); 8068 errors++; 8069 invalid_snomasks_encountered++; 8070 } 8071 } 8072 else if (!strcmp(cep->name, "server-notice-colors")) { 8073 CheckNull(cep); 8074 } 8075 else if (!strcmp(cep->name, "server-notice-show-event")) { 8076 CheckNull(cep); 8077 } 8078 else if (!strcmp(cep->name, "level-on-join")) { 8079 CheckNull(cep); 8080 CheckDuplicate(cep, level_on_join, "level-on-join"); 8081 if (!channellevel_to_string(cep->value) && (strlen(cep->value) != 1)) 8082 { 8083 config_error("%s:%i: set::level-on-join: unknown value '%s', should be one of: " 8084 "'none', 'voice', 'halfop', 'op', 'admin', 'owner', or a single letter (eg 'o')", 8085 cep->file->filename, cep->line_number, cep->value); 8086 errors++; 8087 } 8088 } 8089 else if (!strcmp(cep->name, "static-quit")) { 8090 CheckNull(cep); 8091 CheckDuplicate(cep, static_quit, "static-quit"); 8092 } 8093 else if (!strcmp(cep->name, "static-part")) { 8094 CheckNull(cep); 8095 CheckDuplicate(cep, static_part, "static-part"); 8096 } 8097 else if (!strcmp(cep->name, "who-limit")) { 8098 CheckNull(cep); 8099 CheckDuplicate(cep, who_limit, "who-limit"); 8100 if (!config_checkval(cep->value,CFG_SIZE)) 8101 { 8102 config_error("%s:%i: set::who-limit: value must be at least 1", 8103 cep->file->filename, cep->line_number); 8104 errors++; 8105 } 8106 } 8107 else if (!strcmp(cep->name, "maxbans")) { 8108 CheckNull(cep); 8109 CheckDuplicate(cep, maxbans, "maxbans"); 8110 } 8111 else if (!strcmp(cep->name, "maxbanlength")) { 8112 CheckNull(cep); 8113 CheckDuplicate(cep, maxbanlength, "maxbanlength"); 8114 } 8115 else if (!strcmp(cep->name, "silence-limit")) { 8116 CheckNull(cep); 8117 CheckDuplicate(cep, silence_limit, "silence-limit"); 8118 } 8119 else if (!strcmp(cep->name, "auto-join")) { 8120 CheckNull(cep); 8121 CheckDuplicate(cep, auto_join, "auto-join"); 8122 } 8123 else if (!strcmp(cep->name, "oper-auto-join")) { 8124 CheckNull(cep); 8125 CheckDuplicate(cep, oper_auto_join, "oper-auto-join"); 8126 } 8127 else if (!strcmp(cep->name, "check-target-nick-bans")) { 8128 CheckNull(cep); 8129 CheckDuplicate(cep, check_target_nick_bans, "check-target-nick-bans"); 8130 } 8131 else if (!strcmp(cep->name, "pingpong-warning")) { 8132 config_error("%s:%i: set::pingpong-warning no longer exists (the warning is always off)", 8133 cep->file->filename, cep->line_number); 8134 errors++; 8135 } 8136 else if (!strcmp(cep->name, "ping-cookie")) { 8137 CheckNull(cep); 8138 CheckDuplicate(cep, ping_cookie, "ping-cookie"); 8139 } 8140 else if (!strcmp(cep->name, "watch-away-notification")) { 8141 CheckNull(cep); 8142 CheckDuplicate(cep, watch_away_notification, "watch-away-notification"); 8143 } 8144 else if (!strcmp(cep->name, "uhnames")) { 8145 CheckNull(cep); 8146 CheckDuplicate(cep, uhnames, "uhnames"); 8147 } 8148 else if (!strcmp(cep->name, "channel-command-prefix")) { 8149 CheckNullAllowEmpty(cep); 8150 CheckDuplicate(cep, channel_command_prefix, "channel-command-prefix"); 8151 } 8152 else if (!strcmp(cep->name, "allow-userhost-change")) { 8153 CheckNull(cep); 8154 CheckDuplicate(cep, allow_userhost_change, "allow-userhost-change"); 8155 if (strcasecmp(cep->value, "always") && 8156 strcasecmp(cep->value, "never") && 8157 strcasecmp(cep->value, "not-on-channels") && 8158 strcasecmp(cep->value, "force-rejoin")) 8159 { 8160 config_error("%s:%i: set::allow-userhost-change is invalid", 8161 cep->file->filename, 8162 cep->line_number); 8163 errors++; 8164 continue; 8165 } 8166 } 8167 else if (!strcmp(cep->name, "anti-spam-quit-message-time")) { 8168 CheckNull(cep); 8169 CheckDuplicate(cep, anti_spam_quit_message_time, "anti-spam-quit-message-time"); 8170 } 8171 else if (!strcmp(cep->name, "oper-only-stats")) 8172 { 8173 config_warn("%s:%d: We no longer use a blacklist for stats (set::oper-only-stats) but " 8174 "have a whitelist now instead (set::allow-user-stats). ", 8175 cep->file->filename, cep->line_number); 8176 config_warn("Simply delete the oper-only-stats line from your configuration file %s around line %d to get rid of this warning", 8177 cep->file->filename, cep->line_number); 8178 continue; 8179 } 8180 else if (!strcmp(cep->name, "allow-user-stats")) 8181 { 8182 CheckDuplicate(cep, allow_user_stats, "allow-user-stats"); 8183 CheckNull(cep); 8184 } 8185 else if (!strcmp(cep->name, "maxchannelsperuser")) { 8186 CheckNull(cep); 8187 CheckDuplicate(cep, maxchannelsperuser, "maxchannelsperuser"); 8188 tempi = atoi(cep->value); 8189 if (tempi < 1) 8190 { 8191 config_error("%s:%i: set::maxchannelsperuser must be > 0", 8192 cep->file->filename, 8193 cep->line_number); 8194 errors++; 8195 continue; 8196 } 8197 } 8198 else if (!strcmp(cep->name, "ping-warning")) { 8199 CheckNull(cep); 8200 CheckDuplicate(cep, ping_warning, "ping-warning"); 8201 tempi = atoi(cep->value); 8202 /* it is pointless to allow setting higher than 170 */ 8203 if (tempi > 170) 8204 { 8205 config_error("%s:%i: set::ping-warning must be < 170", 8206 cep->file->filename, 8207 cep->line_number); 8208 errors++; 8209 continue; 8210 } 8211 } 8212 else if (!strcmp(cep->name, "maxdccallow")) { 8213 CheckNull(cep); 8214 CheckDuplicate(cep, maxdccallow, "maxdccallow"); 8215 } 8216 else if (!strcmp(cep->name, "max-targets-per-command")) 8217 { 8218 for (cepp = cep->items; cepp; cepp = cepp->next) 8219 { 8220 CheckNull(cepp); 8221 if (!strcasecmp(cepp->name, "NAMES") || !strcasecmp(cepp->name, "WHOWAS")) 8222 { 8223 if (atoi(cepp->value) != 1) 8224 { 8225 config_error("%s:%i: set::max-targets-per-command::%s: " 8226 "this command is hardcoded at a maximum of 1 " 8227 "and cannot be configured to accept more.", 8228 cepp->file->filename, 8229 cepp->line_number, 8230 cepp->name); 8231 errors++; 8232 } 8233 } else 8234 if (!strcasecmp(cepp->name, "USERHOST") || 8235 !strcasecmp(cepp->name, "USERIP") || 8236 !strcasecmp(cepp->name, "ISON") || 8237 !strcasecmp(cepp->name, "WATCH")) 8238 { 8239 if (strcmp(cepp->value, "max")) 8240 { 8241 config_error("%s:%i: set::max-targets-per-command::%s: " 8242 "this command is hardcoded at a maximum of 'max' " 8243 "and cannot be changed. This because it is " 8244 "highly discouraged to change it.", 8245 cepp->file->filename, 8246 cepp->line_number, 8247 cepp->name); 8248 errors++; 8249 } 8250 } 8251 /* Now check the value syntax in general: */ 8252 if (strcmp(cepp->value, "max")) /* anything other than 'max'.. */ 8253 { 8254 int v = atoi(cepp->value); 8255 if ((v < 1) || (v > 20)) 8256 { 8257 config_error("%s:%i: set::max-targets-per-command::%s: " 8258 "value should be 1-20 or 'max'", 8259 cepp->file->filename, 8260 cepp->line_number, 8261 cepp->name); 8262 errors++; 8263 } 8264 } 8265 } 8266 } 8267 else if (!strcmp(cep->name, "network-name")) { 8268 char *p; 8269 CheckNull(cep); 8270 CheckDuplicate(cep, network_name, "network-name"); 8271 for (p = cep->value; *p; p++) 8272 if ((*p < ' ') || (*p > '~')) 8273 { 8274 config_error("%s:%i: set::network-name can only contain ASCII characters 33-126. Invalid character = '%c'", 8275 cep->file->filename, cep->line_number, *p); 8276 errors++; 8277 break; 8278 } 8279 } 8280 else if (!strcmp(cep->name, "default-server")) { 8281 CheckNull(cep); 8282 CheckDuplicate(cep, default_server, "default-server"); 8283 } 8284 else if (!strcmp(cep->name, "services-server")) { 8285 CheckNull(cep); 8286 CheckDuplicate(cep, services_server, "services-server"); 8287 } 8288 else if (!strcmp(cep->name, "sasl-server")) { 8289 CheckNull(cep); 8290 CheckDuplicate(cep, sasl_server, "sasl-server"); 8291 } 8292 else if (!strcmp(cep->name, "stats-server")) { 8293 CheckNull(cep); 8294 CheckDuplicate(cep, stats_server, "stats-server"); 8295 } 8296 else if (!strcmp(cep->name, "help-channel")) { 8297 CheckNull(cep); 8298 CheckDuplicate(cep, help_channel, "help-channel"); 8299 } 8300 else if (!strcmp(cep->name, "cloak-prefix") || !strcmp(cep->name, "hiddenhost-prefix")) { 8301 CheckNull(cep); 8302 CheckDuplicate(cep, hiddenhost_prefix, "cloak-prefix"); 8303 if (strchr(cep->value, ' ') || (*cep->value == ':')) 8304 { 8305 config_error("%s:%i: set::cloak-prefix must not contain spaces or be prefixed with ':'", 8306 cep->file->filename, cep->line_number); 8307 errors++; 8308 continue; 8309 } 8310 } 8311 else if (!strcmp(cep->name, "prefix-quit")) { 8312 CheckNull(cep); 8313 CheckDuplicate(cep, prefix_quit, "prefix-quit"); 8314 } 8315 else if (!strcmp(cep->name, "hide-ban-reason")) { 8316 CheckNull(cep); 8317 CheckDuplicate(cep, hide_ban_reason, "hide-ban-reason"); 8318 } 8319 else if (!strcmp(cep->name, "restrict-usermodes")) 8320 { 8321 CheckNull(cep); 8322 CheckDuplicate(cep, restrict_usermodes, "restrict-usermodes"); 8323 if (cep->name) { 8324 int warn = 0; 8325 char *p; 8326 for (p = cep->value; *p; p++) 8327 if ((*p == '+') || (*p == '-')) 8328 warn = 1; 8329 if (warn) { 8330 config_status("%s:%i: warning: set::restrict-usermodes: should only contain modechars, no + or -.\n", 8331 cep->file->filename, cep->line_number); 8332 } 8333 } 8334 } 8335 else if (!strcmp(cep->name, "restrict-channelmodes")) 8336 { 8337 CheckNull(cep); 8338 CheckDuplicate(cep, restrict_channelmodes, "restrict-channelmodes"); 8339 if (cep->name) { 8340 int warn = 0; 8341 char *p; 8342 for (p = cep->value; *p; p++) 8343 if ((*p == '+') || (*p == '-')) 8344 warn = 1; 8345 if (warn) { 8346 config_status("%s:%i: warning: set::restrict-channelmodes: should only contain modechars, no + or -.\n", 8347 cep->file->filename, cep->line_number); 8348 } 8349 } 8350 } 8351 else if (!strcmp(cep->name, "restrict-extendedbans")) 8352 { 8353 CheckDuplicate(cep, restrict_extendedbans, "restrict-extendedbans"); 8354 CheckNull(cep); 8355 } 8356 else if (!strcmp(cep->name, "named-extended-bans")) 8357 { 8358 CheckNull(cep); 8359 } 8360 else if (!strcmp(cep->name, "link")) { 8361 for (cepp = cep->items; cepp; cepp = cepp->next) { 8362 CheckNull(cepp); 8363 if (!strcmp(cepp->name, "bind-ip")) { 8364 CheckDuplicate(cepp, link_bind_ip, "link::bind-ip"); 8365 if (strcmp(cepp->value, "*")) 8366 { 8367 if (!is_valid_ip(cepp->value)) 8368 { 8369 config_error("%s:%i: set::link::bind-ip (%s) is not a valid IP", 8370 cepp->file->filename, cepp->line_number, 8371 cepp->value); 8372 errors++; 8373 continue; 8374 } 8375 } 8376 } 8377 } 8378 } 8379 else if (!strcmp(cep->name, "throttle")) { 8380 config_error("%s:%i: set::throttle has been renamed. you now use " 8381 "set::anti-flood::connect-flood <connections>:<period>. " 8382 "Or just remove the throttle block and you get the default " 8383 "of 3 per 60 seconds.", 8384 cep->file->filename, cep->line_number); 8385 errors++; 8386 continue; 8387 } 8388 else if (!strcmp(cep->name, "anti-flood")) 8389 { 8390 int anti_flood_old = 0; 8391 int anti_flood_old_and_default = 0; 8392 8393 for (cepp = cep->items; cepp; cepp = cepp->next) 8394 { 8395 int has_lag_penalty = 0; 8396 int has_lag_penalty_bytes = 0; 8397 8398 /* Test for old options: */ 8399 if (flood_option_is_old(cepp->name)) 8400 { 8401 /* Special code if the user is using 100% of the defaults */ 8402 if (cepp->value && 8403 ((!strcmp(cepp->name, "nick-flood") && !strcmp(cepp->value, "3:60")) || 8404 (!strcmp(cepp->name, "connect-flood") && cepp->value && !strcmp(cepp->value, "3:60")) || 8405 (!strcmp(cepp->name, "away-flood") && cepp->value && !strcmp(cepp->value, "4:120")))) 8406 { 8407 anti_flood_old_and_default = 1; 8408 } else 8409 { 8410 anti_flood_old = 1; 8411 } 8412 continue; 8413 } 8414 8415 for (ceppp = cepp->items; ceppp; ceppp = ceppp->next) 8416 { 8417 int everyone; 8418 int for_everyone; 8419 int used = 0; 8420 Hook *h; 8421 8422 /* First, check hooks... */ 8423 for (h = Hooks[HOOKTYPE_CONFIGTEST]; h; h = h->next) 8424 { 8425 int value, errs = 0; 8426 if (h->owner && !(h->owner->flags & MODFLAG_TESTING) 8427 && !(h->owner->options & MOD_OPT_PERM)) 8428 continue; 8429 value = (*(h->func.intfunc))(conf,ceppp,CONFIG_SET_ANTI_FLOOD,&errs); 8430 if (value == 2) 8431 { 8432 used = 2; 8433 break; 8434 } else 8435 if (value == 1) 8436 { 8437 used = 1; 8438 break; 8439 } else 8440 if (value == -1) 8441 { 8442 used = 1; 8443 errors += errs; 8444 break; 8445 } else 8446 if (value == -2) 8447 { 8448 used = 2; 8449 errors += errs; 8450 break; 8451 } 8452 } 8453 if (used == 1) 8454 continue; /* module handled it */ 8455 if (used == 2) 8456 break; /* module handled it and we must stop entire block processing */ 8457 8458 /* Prevent users from using options that belong in "everyone" 8459 * at other places, and vice-versa. 8460 */ 8461 everyone = !strcmp(cepp->name, "everyone") ? 1 : 0; 8462 for_everyone = flood_option_is_for_everyone(ceppp->name); 8463 if (everyone && !for_everyone) 8464 { 8465 config_error("%s:%i: %s cannot be in the set::anti-flood::everyone block. " 8466 "You can put it in 'known-users' or 'unknown-users' instead.", 8467 ceppp->file->filename, ceppp->line_number, 8468 ceppp->name); 8469 errors++; 8470 continue; 8471 } else 8472 if (!everyone && for_everyone) 8473 { 8474 config_error("%s:%i: %s must be in the set::anti-flood::everyone block, not anywhere else.", 8475 ceppp->file->filename, ceppp->line_number, 8476 ceppp->name); 8477 errors++; 8478 continue; 8479 } 8480 8481 /* Now comes the actual config check for each element... */ 8482 if (!strcmp(ceppp->name, "max-concurrent-conversations")) 8483 { 8484 for (cep4 = ceppp->items; cep4; cep4 = cep4->next) 8485 { 8486 CheckNull(cep4); 8487 if (!strcmp(cep4->name, "users")) 8488 { 8489 int v = atoi(cep4->value); 8490 if ((v < 1) || (v > MAXCCUSERS)) 8491 { 8492 config_error("%s:%i: set::anti-flood::max-concurrent-conversations::users: " 8493 "value should be between 1 and %d", 8494 cep4->file->filename, cep4->line_number, MAXCCUSERS); 8495 errors++; 8496 } 8497 } else 8498 if (!strcmp(cep4->name, "new-user-every")) 8499 { 8500 long v = config_checkval(cep4->value, CFG_TIME); 8501 if ((v < 1) || (v > 120)) 8502 { 8503 config_error("%s:%i: set::anti-flood::max-concurrent-conversations::new-user-every: " 8504 "value should be between 1 and 120 seconds", 8505 cep4->file->filename, cep4->line_number); 8506 errors++; 8507 } 8508 } else 8509 { 8510 config_error_unknownopt(cep4->file->filename, 8511 cep4->line_number, "set::anti-flood", 8512 cep4->name); 8513 errors++; 8514 } 8515 } 8516 continue; /* required here, due to checknull directly below */ 8517 } 8518 else if (!strcmp(ceppp->name, "unknown-flood-amount") || 8519 !strcmp(ceppp->name, "unknown-flood-bantime")) 8520 { 8521 config_error("%s:%i: set::anti-flood::%s: this setting has been moved. " 8522 "See https://www.unrealircd.org/docs/Anti-flood_settings#handshake-data-flood", 8523 ceppp->file->filename, ceppp->line_number, ceppp->name); 8524 errors++; 8525 continue; 8526 } 8527 else if (!strcmp(ceppp->name, "handshake-data-flood")) 8528 { 8529 for (cep4 = ceppp->items; cep4; cep4 = cep4->next) 8530 { 8531 if (!strcmp(cep4->name, "amount")) 8532 { 8533 long v; 8534 CheckNull(cep4); 8535 v = config_checkval(cep4->value, CFG_SIZE); 8536 if (v < 1024) 8537 { 8538 config_error("%s:%i: set::anti-flood::handshake-data-flood::amount must be at least 1024 bytes", 8539 cep4->file->filename, cep4->line_number); 8540 errors++; 8541 } 8542 } else 8543 if (!strcmp(cep4->name, "ban-action")) 8544 { 8545 CheckNull(cep4); 8546 if (!banact_stringtoval(cep4->value)) 8547 { 8548 config_error("%s:%i: set::anti-flood::handshake-data-flood::ban-action has unknown action type '%s'", 8549 cep4->file->filename, cep4->line_number, 8550 cep4->value); 8551 errors++; 8552 } 8553 } else 8554 if (!strcmp(cep4->name, "ban-time")) 8555 { 8556 CheckNull(cep4); 8557 } else 8558 { 8559 config_error_unknownopt(cep4->file->filename, 8560 cep4->line_number, "set::anti-flood::handshake-data-flood", 8561 cep4->name); 8562 errors++; 8563 } 8564 } 8565 } 8566 else if (!strcmp(ceppp->name, "away-count")) 8567 { 8568 int temp = atol(ceppp->value); 8569 CheckNull(ceppp); 8570 if (temp < 1 || temp > 255) 8571 { 8572 config_error("%s:%i: set::anti-flood::away-count must be between 1 and 255", 8573 ceppp->file->filename, ceppp->line_number); 8574 errors++; 8575 } 8576 } 8577 else if (!strcmp(ceppp->name, "away-period")) 8578 { 8579 CheckNull(ceppp); 8580 int temp = config_checkval(ceppp->value, CFG_TIME); 8581 if (temp < 10) 8582 { 8583 config_error("%s:%i: set::anti-flood::away-period must be greater than 9", 8584 ceppp->file->filename, ceppp->line_number); 8585 errors++; 8586 } 8587 } 8588 else if (!strcmp(ceppp->name, "away-flood")) 8589 { 8590 int cnt, period; 8591 CheckNull(ceppp); 8592 if (!config_parse_flood(ceppp->value, &cnt, &period) || 8593 (cnt < 1) || (cnt > 255) || (period < 10)) 8594 { 8595 config_error("%s:%i: set::anti-flood::away-flood error. Syntax is '<count>:<period>' (eg 5:60), " 8596 "count should be 1-255, period should be greater than 9", 8597 ceppp->file->filename, ceppp->line_number); 8598 errors++; 8599 } 8600 } 8601 else if (!strcmp(ceppp->name, "nick-flood")) 8602 { 8603 int cnt, period; 8604 CheckNull(ceppp); 8605 if (!config_parse_flood(ceppp->value, &cnt, &period) || 8606 (cnt < 1) || (cnt > 255) || (period < 5)) 8607 { 8608 config_error("%s:%i: set::anti-flood::nick-flood error. Syntax is '<count>:<period>' (eg 5:60), " 8609 "count should be 1-255, period should be greater than 4", 8610 ceppp->file->filename, ceppp->line_number); 8611 errors++; 8612 } 8613 } 8614 else if (!strcmp(ceppp->name, "vhost-flood")) 8615 { 8616 int cnt, period; 8617 CheckNull(ceppp); 8618 if (!config_parse_flood(ceppp->value, &cnt, &period) || 8619 (cnt < 1) || (cnt > 255) || (period < 5)) 8620 { 8621 config_error("%s:%i: set::anti-flood::vhost-flood error. Syntax is '<count>:<period>' (eg 5:60), " 8622 "count should be 1-255, period should be greater than 4", 8623 ceppp->file->filename, ceppp->line_number); 8624 errors++; 8625 } 8626 } 8627 else if (!strcmp(ceppp->name, "join-flood")) 8628 { 8629 int cnt, period; 8630 CheckNull(ceppp); 8631 8632 if (!config_parse_flood(ceppp->value, &cnt, &period) || 8633 (cnt < 1) || (cnt > 255) || (period < 5)) 8634 { 8635 config_error("%s:%i: join-flood error. Syntax is '<count>:<period>' (eg 5:60), " 8636 "count should be 1-255, period should be greater than 4", 8637 ceppp->file->filename, ceppp->line_number); 8638 errors++; 8639 } 8640 } 8641 else if (!strcmp(ceppp->name, "invite-flood")) 8642 { 8643 int cnt, period; 8644 CheckNull(ceppp); 8645 if (!config_parse_flood(ceppp->value, &cnt, &period) || 8646 (cnt < 1) || (cnt > 255) || (period < 5)) 8647 { 8648 config_error("%s:%i: set::anti-flood::invite-flood error. Syntax is '<count>:<period>' (eg 5:60), " 8649 "count should be 1-255, period should be greater than 4", 8650 ceppp->file->filename, ceppp->line_number); 8651 errors++; 8652 } 8653 } 8654 else if (!strcmp(ceppp->name, "knock-flood")) 8655 { 8656 int cnt, period; 8657 CheckNull(ceppp); 8658 if (!config_parse_flood(ceppp->value, &cnt, &period) || 8659 (cnt < 1) || (cnt > 255) || (period < 5)) 8660 { 8661 config_error("%s:%i: set::anti-flood::knock-flood error. Syntax is '<count>:<period>' (eg 5:60), " 8662 "count should be 1-255, period should be greater than 4", 8663 ceppp->file->filename, ceppp->line_number); 8664 errors++; 8665 } 8666 } 8667 else if (!strcmp(ceppp->name, "lag-penalty")) 8668 { 8669 int v; 8670 CheckNull(ceppp); 8671 v = atoi(ceppp->value); 8672 has_lag_penalty = 1; 8673 if ((v < 0) || (v > 10000)) 8674 { 8675 config_error("%s:%i: set::anti-flood::%s::lag-penalty: value is in milliseconds and should be between 0 and 10000", 8676 ceppp->file->filename, ceppp->line_number, cepp->name); 8677 errors++; 8678 } 8679 } 8680 else if (!strcmp(ceppp->name, "lag-penalty-bytes")) 8681 { 8682 has_lag_penalty_bytes = 1; 8683 CheckNull(ceppp); 8684 } 8685 else if (!strcmp(ceppp->name, "connect-flood")) 8686 { 8687 int cnt, period; 8688 CheckNull(ceppp); 8689 if (strcmp(cepp->name, "everyone")) 8690 { 8691 config_error("%s:%i: connect-flood must be in the set::anti-flood::everyone block, not anywhere else.", 8692 ceppp->file->filename, ceppp->line_number); 8693 errors++; 8694 continue; 8695 } 8696 if (!config_parse_flood(ceppp->value, &cnt, &period) || 8697 (cnt < 1) || (cnt > 255) || (period < 1) || (period > 3600)) 8698 { 8699 config_error("%s:%i: set::anti-flood::connect-flood: Syntax is '<count>:<period>' (eg 5:60), " 8700 "count should be 1-255, period should be 1-3600", 8701 ceppp->file->filename, ceppp->line_number); 8702 errors++; 8703 } 8704 } 8705 else 8706 { 8707 config_error_unknownopt(ceppp->file->filename, 8708 ceppp->line_number, "set::anti-flood", 8709 ceppp->name); 8710 errors++; 8711 } 8712 } 8713 if (has_lag_penalty+has_lag_penalty_bytes == 1) 8714 { 8715 config_error("%s:%i: set::anti-flood::%s: if you use lag-penalty then you must also add an lag-penalty-bytes item (and vice-versa)", 8716 cepp->file->filename, cepp->line_number, cepp->name); 8717 errors++; 8718 } 8719 } 8720 /* Now the warnings: */ 8721 if (anti_flood_old == 1) 8722 { 8723 config_warn("%s:%d: the set::anti-flood block has been reorganized to be more flexible. " 8724 "Your custom anti-flood settings have NOT been read.", 8725 cep->file->filename, cep->line_number); 8726 config_warn("See https://www.unrealircd.org/docs/Anti-flood_settings for the new block style,"); 8727 config_warn("OR: simply remove all the anti-flood options from the conf to get rid of this " 8728 "warning and use the built-in defaults."); 8729 } else 8730 if (anti_flood_old_and_default == 1) 8731 { 8732 config_warn("%s:%d: the set::anti-flood block has been reorganized to be more flexible.", 8733 cep->file->filename, cep->line_number); 8734 config_warn("To fix this warning, delete the anti-flood block from your configuration file " 8735 "(file %s around line %d), this will make UnrealIRCd use the built-in defaults.", 8736 cep->file->filename, cep->line_number); 8737 config_warn("If you want to learn more about the new functionality you can visit " 8738 "https://www.unrealircd.org/docs/Anti-flood_settings"); 8739 } 8740 } 8741 else if (!strcmp(cep->name, "options")) { 8742 for (cepp = cep->items; cepp; cepp = cepp->next) { 8743 if (!strcmp(cepp->name, "hide-ulines")) 8744 { 8745 CheckDuplicate(cepp, options_hide_ulines, "options::hide-ulines"); 8746 } 8747 else if (!strcmp(cepp->name, "flat-map")) { 8748 CheckDuplicate(cepp, options_flat_map, "options::flat-map"); 8749 } 8750 else if (!strcmp(cepp->name, "show-opermotd")) { 8751 CheckDuplicate(cepp, options_show_opermotd, "options::show-opermotd"); 8752 } 8753 else if (!strcmp(cepp->name, "identd-check")) { 8754 CheckDuplicate(cepp, options_identd_check, "options::identd-check"); 8755 } 8756 else if (!strcmp(cepp->name, "fail-oper-warn")) { 8757 CheckDuplicate(cepp, options_fail_oper_warn, "options::fail-oper-warn"); 8758 } 8759 else if (!strcmp(cepp->name, "show-connect-info")) { 8760 CheckDuplicate(cepp, options_show_connect_info, "options::show-connect-info"); 8761 } 8762 else if (!strcmp(cepp->name, "no-connect-tls-info")) { 8763 CheckDuplicate(cepp, options_no_connect_tls_info, "options::no-connect-tls-info"); 8764 } 8765 else if (!strcmp(cepp->name, "dont-resolve")) { 8766 CheckDuplicate(cepp, options_dont_resolve, "options::dont-resolve"); 8767 } 8768 else if (!strcmp(cepp->name, "mkpasswd-for-everyone")) { 8769 CheckDuplicate(cepp, options_mkpasswd_for_everyone, "options::mkpasswd-for-everyone"); 8770 } 8771 else if (!strcmp(cepp->name, "allow-insane-bans")) { 8772 CheckDuplicate(cepp, options_allow_insane_bans, "options::allow-insane-bans"); 8773 } 8774 else if (!strcmp(cepp->name, "allow-part-if-shunned")) { 8775 CheckDuplicate(cepp, options_allow_part_if_shunned, "options::allow-part-if-shunned"); 8776 } 8777 else if (!strcmp(cepp->name, "disable-cap")) { 8778 CheckDuplicate(cepp, options_disable_cap, "options::disable-cap"); 8779 } 8780 else if (!strcmp(cepp->name, "disable-ipv6")) { 8781 CheckDuplicate(cepp, options_disable_ipv6, "options::disable-ipv6"); 8782 DISABLE_IPV6 = 1; /* ugly ugly. needs to be done here because at conf runtime is too late. */ 8783 } 8784 else 8785 { 8786 config_error_unknownopt(cepp->file->filename, 8787 cepp->line_number, "set::options", 8788 cepp->name); 8789 errors++; 8790 continue; 8791 } 8792 } 8793 } 8794 else if (!strcmp(cep->name, "hosts")) { 8795 config_error("%s:%i: set::hosts has been removed. You can use oper::vhost now.", 8796 cep->file->filename, cep->line_number); 8797 errors++; 8798 } 8799 else if (!strcmp(cep->name, "cloak-keys")) 8800 { 8801 CheckDuplicate(cep, cloak_keys, "cloak-keys"); 8802 for (h = Hooks[HOOKTYPE_CONFIGTEST]; h; h = h->next) 8803 { 8804 int value, errs = 0; 8805 if (h->owner && !(h->owner->flags & MODFLAG_TESTING) 8806 && !(h->owner->options & MOD_OPT_PERM)) 8807 continue; 8808 value = (*(h->func.intfunc))(conf, cep, CONFIG_CLOAKKEYS, &errs); 8809 8810 if (value == 1) 8811 break; 8812 if (value == -1) 8813 { 8814 errors += errs; 8815 break; 8816 } 8817 if (value == -2) 8818 errors += errs; 8819 } 8820 } 8821 else if (!strcmp(cep->name, "ident")) { 8822 for (cepp = cep->items; cepp; cepp = cepp->next) 8823 { 8824 int is_ok = 0; 8825 CheckNull(cepp); 8826 if (!strcmp(cepp->name, "connect-timeout")) 8827 { 8828 is_ok = 1; 8829 CheckDuplicate(cepp, ident_connect_timeout, "ident::connect-timeout"); 8830 } 8831 else if (!strcmp(cepp->name, "read-timeout")) 8832 { 8833 is_ok = 1; 8834 CheckDuplicate(cepp, ident_read_timeout, "ident::read-timeout"); 8835 } 8836 if (is_ok) 8837 { 8838 int v = config_checkval(cepp->value,CFG_TIME); 8839 if ((v > 60) || (v < 1)) 8840 { 8841 config_error("%s:%i: set::ident::%s value out of range (%d), should be between 1 and 60.", 8842 cepp->file->filename, cepp->line_number, cepp->name, v); 8843 errors++; 8844 continue; 8845 } 8846 } else { 8847 config_error_unknown(cepp->file->filename, 8848 cepp->line_number, "set::ident", 8849 cepp->name); 8850 errors++; 8851 continue; 8852 } 8853 } 8854 } 8855 else if (!strcmp(cep->name, "timesync") || !strcmp(cep->name, "timesynch")) 8856 { 8857 config_warn("%s:%i: Timesync support has been removed from UnrealIRCd. " 8858 "Please remove any set::timesync blocks you may have.", 8859 cep->file->filename, cep->line_number); 8860 config_warn("Use the time synchronization feature of your OS/distro instead!"); 8861 } 8862 else if (!strcmp(cep->name, "spamfilter")) { 8863 for (cepp = cep->items; cepp; cepp = cepp->next) 8864 { 8865 CheckNull(cepp); 8866 if (!strcmp(cepp->name, "ban-time")) 8867 { 8868 long x; 8869 CheckDuplicate(cepp, spamfilter_ban_time, "spamfilter::ban-time"); 8870 x = config_checkval(cepp->value,CFG_TIME); 8871 if ((x < 0) > (x > 2000000000)) 8872 { 8873 config_error("%s:%i: set::spamfilter:ban-time: value '%ld' out of range", 8874 cep->file->filename, cep->line_number, x); 8875 errors++; 8876 continue; 8877 } 8878 } else 8879 if (!strcmp(cepp->name, "ban-reason")) 8880 { 8881 CheckDuplicate(cepp, spamfilter_ban_reason, "spamfilter::ban-reason"); 8882 8883 } 8884 else if (!strcmp(cepp->name, "virus-help-channel")) 8885 { 8886 CheckDuplicate(cepp, spamfilter_virus_help_channel, "spamfilter::virus-help-channel"); 8887 if ((cepp->value[0] != '#') || (strlen(cepp->value) > CHANNELLEN)) 8888 { 8889 config_error("%s:%i: set::spamfilter:virus-help-channel: " 8890 "specified channelname is too long or contains invalid characters (%s)", 8891 cep->file->filename, cep->line_number, 8892 cepp->value); 8893 errors++; 8894 continue; 8895 } 8896 } else 8897 if (!strcmp(cepp->name, "virus-help-channel-deny")) 8898 { 8899 CheckDuplicate(cepp, spamfilter_virus_help_channel_deny, "spamfilter::virus-help-channel-deny"); 8900 } else 8901 if (!strcmp(cepp->name, "except")) 8902 { 8903 CheckDuplicate(cepp, spamfilter_except, "spamfilter::except"); 8904 } else 8905 #ifdef SPAMFILTER_DETECTSLOW 8906 if (!strcmp(cepp->name, "detect-slow-warn")) 8907 { 8908 } else 8909 if (!strcmp(cepp->name, "detect-slow-fatal")) 8910 { 8911 } else 8912 #endif 8913 if (!strcmp(cepp->name, "stop-on-first-match")) 8914 { 8915 } else 8916 if (!strcmp(cepp->name, "utf8")) 8917 { 8918 } else 8919 { 8920 config_error_unknown(cepp->file->filename, 8921 cepp->line_number, "set::spamfilter", 8922 cepp->name); 8923 errors++; 8924 continue; 8925 } 8926 } 8927 } 8928 else if (!strcmp(cep->name, "default-bantime")) 8929 { 8930 long x; 8931 CheckDuplicate(cep, default_bantime, "default-bantime"); 8932 CheckNull(cep); 8933 x = config_checkval(cep->value,CFG_TIME); 8934 if ((x < 0) > (x > 2000000000)) 8935 { 8936 config_error("%s:%i: set::default-bantime: value '%ld' out of range", 8937 cep->file->filename, cep->line_number, x); 8938 errors++; 8939 } 8940 } 8941 else if (!strcmp(cep->name, "ban-version-tkl-time")) { 8942 long x; 8943 CheckDuplicate(cep, ban_version_tkl_time, "ban-version-tkl-time"); 8944 CheckNull(cep); 8945 x = config_checkval(cep->value,CFG_TIME); 8946 if ((x < 0) > (x > 2000000000)) 8947 { 8948 config_error("%s:%i: set::ban-version-tkl-time: value '%ld' out of range", 8949 cep->file->filename, cep->line_number, x); 8950 errors++; 8951 } 8952 } 8953 else if (!strcmp(cep->name, "min-nick-length")) { 8954 int v; 8955 CheckDuplicate(cep, min_nick_length, "min-nick-length"); 8956 CheckNull(cep); 8957 v = atoi(cep->value); 8958 if ((v <= 0) || (v > NICKLEN)) 8959 { 8960 config_error("%s:%i: set::min-nick-length: value '%d' out of range (should be 1-%d)", 8961 cep->file->filename, cep->line_number, v, NICKLEN); 8962 errors++; 8963 } 8964 else 8965 nicklengths.min = v; 8966 } 8967 else if (!strcmp(cep->name, "nick-length")) { 8968 int v; 8969 CheckDuplicate(cep, nick_length, "nick-length"); 8970 CheckNull(cep); 8971 v = atoi(cep->value); 8972 if ((v <= 0) || (v > NICKLEN)) 8973 { 8974 config_error("%s:%i: set::nick-length: value '%d' out of range (should be 1-%d)", 8975 cep->file->filename, cep->line_number, v, NICKLEN); 8976 errors++; 8977 } 8978 else 8979 nicklengths.max = v; 8980 } 8981 else if (!strcmp(cep->name, "topic-length")) { 8982 int v; 8983 CheckNull(cep); 8984 v = atoi(cep->value); 8985 if ((v <= 0) || (v > MAXTOPICLEN)) 8986 { 8987 config_error("%s:%i: set::topic-length: value '%d' out of range (should be 1-%d)", 8988 cep->file->filename, cep->line_number, v, MAXTOPICLEN); 8989 errors++; 8990 } 8991 } 8992 else if (!strcmp(cep->name, "away-length")) { 8993 int v; 8994 CheckNull(cep); 8995 v = atoi(cep->value); 8996 if ((v <= 0) || (v > MAXAWAYLEN)) 8997 { 8998 config_error("%s:%i: set::away-length: value '%d' out of range (should be 1-%d)", 8999 cep->file->filename, cep->line_number, v, MAXAWAYLEN); 9000 errors++; 9001 } 9002 } 9003 else if (!strcmp(cep->name, "kick-length")) { 9004 int v; 9005 CheckNull(cep); 9006 v = atoi(cep->value); 9007 if ((v <= 0) || (v > MAXKICKLEN)) 9008 { 9009 config_error("%s:%i: set::kick-length: value '%d' out of range (should be 1-%d)", 9010 cep->file->filename, cep->line_number, v, MAXKICKLEN); 9011 errors++; 9012 } 9013 } 9014 else if (!strcmp(cep->name, "quit-length")) { 9015 int v; 9016 CheckNull(cep); 9017 v = atoi(cep->value); 9018 if ((v <= 0) || (v > MAXQUITLEN)) 9019 { 9020 config_error("%s:%i: set::quit-length: value '%d' out of range (should be 1-%d)", 9021 cep->file->filename, cep->line_number, v, MAXQUITLEN); 9022 errors++; 9023 } 9024 } 9025 else if (!strcmp(cep->name, "ssl") || !strcmp(cep->name, "tls")) { 9026 test_tlsblock(conf, cep, &errors); 9027 } 9028 else if (!strcmp(cep->name, "plaintext-policy")) 9029 { 9030 for (cepp = cep->items; cepp; cepp = cepp->next) 9031 { 9032 if (!strcmp(cepp->name, "user") || 9033 !strcmp(cepp->name, "oper") || 9034 !strcmp(cepp->name, "server")) 9035 { 9036 Policy policy; 9037 CheckNull(cepp); 9038 policy = policy_strtoval(cepp->value); 9039 if (!policy) 9040 { 9041 config_error("%s:%i: set::plaintext-policy::%s: needs to be one of: 'allow', 'warn' or 'reject'", 9042 cepp->file->filename, cepp->line_number, cepp->name); 9043 errors++; 9044 } 9045 } else if (!strcmp(cepp->name, "user-message") || 9046 !strcmp(cepp->name, "oper-message")) 9047 { 9048 CheckNull(cepp); 9049 } else { 9050 config_error_unknown(cepp->file->filename, 9051 cepp->line_number, "set::plaintext-policy", 9052 cepp->name); 9053 errors++; 9054 continue; 9055 } 9056 } 9057 } 9058 else if (!strcmp(cep->name, "outdated-tls-policy")) 9059 { 9060 for (cepp = cep->items; cepp; cepp = cepp->next) 9061 { 9062 if (!strcmp(cepp->name, "user") || 9063 !strcmp(cepp->name, "oper") || 9064 !strcmp(cepp->name, "server")) 9065 { 9066 Policy policy; 9067 CheckNull(cepp); 9068 policy = policy_strtoval(cepp->value); 9069 if (!policy) 9070 { 9071 config_error("%s:%i: set::outdated-tls-policy::%s: needs to be one of: 'allow', 'warn' or 'reject'", 9072 cepp->file->filename, cepp->line_number, cepp->name); 9073 errors++; 9074 } 9075 } else if (!strcmp(cepp->name, "user-message") || 9076 !strcmp(cepp->name, "oper-message")) 9077 { 9078 CheckNull(cepp); 9079 } else { 9080 config_error_unknown(cepp->file->filename, 9081 cepp->line_number, "set::outdated-tls-policy", 9082 cepp->name); 9083 errors++; 9084 continue; 9085 } 9086 } 9087 } 9088 else if (!strcmp(cep->name, "default-ipv6-clone-mask")) 9089 { 9090 /* keep this in sync with _test_allow() */ 9091 int ipv6mask; 9092 CheckNull(cep); 9093 ipv6mask = atoi(cep->value); 9094 if (ipv6mask == 0) 9095 { 9096 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.", 9097 cep->file->filename, cep->line_number); 9098 errors++; 9099 } 9100 if (ipv6mask > 128) 9101 { 9102 config_error("%s:%d: set::default-ipv6-clone-mask was set to %d. The maximum value is 128.", 9103 cep->file->filename, cep->line_number, 9104 ipv6mask); 9105 errors++; 9106 } 9107 if (ipv6mask <= 32) 9108 { 9109 config_warn("%s:%d: set::default-ipv6-clone-mask was given a very small value.", 9110 cep->file->filename, cep->line_number); 9111 } 9112 } 9113 else if (!strcmp(cep->name, "hide-list")) { 9114 for (cepp = cep->items; cepp; cepp = cepp->next) 9115 { 9116 if (!strcmp(cepp->name, "deny-channel")) 9117 { 9118 } else 9119 { 9120 config_error_unknown(cepp->file->filename, 9121 cepp->line_number, "set::hide-list", 9122 cepp->name); 9123 errors++; 9124 continue; 9125 } 9126 } 9127 } 9128 else if (!strcmp(cep->name, "max-unknown-connections-per-ip")) { 9129 int v; 9130 CheckNull(cep); 9131 v = atoi(cep->value); 9132 if (v < 1) 9133 { 9134 config_error("%s:%i: set::max-unknown-connections-per-ip: value should be at least 1.", 9135 cep->file->filename, cep->line_number); 9136 errors++; 9137 } 9138 } 9139 else if (!strcmp(cep->name, "handshake-timeout")) { 9140 int v; 9141 CheckNull(cep); 9142 v = config_checkval(cep->value, CFG_TIME); 9143 if (v < 5) 9144 { 9145 config_error("%s:%i: set::handshake-timeout: value should be at least 5 seconds.", 9146 cep->file->filename, cep->line_number); 9147 errors++; 9148 } 9149 } 9150 else if (!strcmp(cep->name, "sasl-timeout")) { 9151 int v; 9152 CheckNull(cep); 9153 v = config_checkval(cep->value, CFG_TIME); 9154 if (v < 5) 9155 { 9156 config_error("%s:%i: set::sasl-timeout: value should be at least 5 seconds.", 9157 cep->file->filename, cep->line_number); 9158 errors++; 9159 } 9160 } 9161 else if (!strcmp(cep->name, "handshake-delay")) 9162 { 9163 int v; 9164 CheckNull(cep); 9165 v = config_checkval(cep->value, CFG_TIME); 9166 if (v >= 10) 9167 { 9168 config_error("%s:%i: set::handshake-delay: value should be less than 10 seconds.", 9169 cep->file->filename, cep->line_number); 9170 errors++; 9171 } 9172 } 9173 else if (!strcmp(cep->name, "ban-include-username")) 9174 { 9175 config_error("%s:%i: set::ban-include-username is no longer supported. " 9176 "Use set { automatic-ban-target userip; }; instead.", 9177 cep->file->filename, cep->line_number); 9178 config_error("See https://www.unrealircd.org/docs/Set_block#set::automatic-ban-target " 9179 "for more information and options."); 9180 errors++; 9181 } 9182 else if (!strcmp(cep->name, "automatic-ban-target")) 9183 { 9184 CheckNull(cep); 9185 if (!ban_target_strtoval(cep->value)) 9186 { 9187 config_error("%s:%i: set::automatic-ban-target: value '%s' is not recognized. " 9188 "See https://www.unrealircd.org/docs/Set_block#set::automatic-ban-target", 9189 cep->file->filename, cep->line_number, cep->value); 9190 errors++; 9191 } 9192 } 9193 else if (!strcmp(cep->name, "manual-ban-target")) 9194 { 9195 CheckNull(cep); 9196 if (!ban_target_strtoval(cep->value)) 9197 { 9198 config_error("%s:%i: set::manual-ban-target: value '%s' is not recognized. " 9199 "See https://www.unrealircd.org/docs/Set_block#set::manual-ban-target", 9200 cep->file->filename, cep->line_number, cep->value); 9201 errors++; 9202 } 9203 } 9204 else if (!strcmp(cep->name, "reject-message")) 9205 { 9206 for (cepp = cep->items; cepp; cepp = cepp->next) 9207 { 9208 CheckNull(cepp); 9209 if (!strcmp(cepp->name, "password-mismatch")) 9210 ; 9211 else if (!strcmp(cepp->name, "too-many-connections")) 9212 ; 9213 else if (!strcmp(cepp->name, "server-full")) 9214 ; 9215 else if (!strcmp(cepp->name, "unauthorized")) 9216 ; 9217 else if (!strcmp(cepp->name, "kline")) 9218 ; 9219 else if (!strcmp(cepp->name, "gline")) 9220 ; 9221 else 9222 { 9223 config_error_unknown(cepp->file->filename, 9224 cepp->line_number, "set::reject-message", 9225 cepp->name); 9226 errors++; 9227 continue; 9228 } 9229 } 9230 } 9231 else if (!strcmp(cep->name, "topic-setter")) 9232 { 9233 CheckNull(cep); 9234 if (strcmp(cep->value, "nick") && strcmp(cep->value, "nick-user-host")) 9235 { 9236 config_error("%s:%i: set::topic-setter: value should be 'nick' or 'nick-user-host'", 9237 cep->file->filename, cep->line_number); 9238 errors++; 9239 } 9240 } 9241 else if (!strcmp(cep->name, "ban-setter")) 9242 { 9243 CheckNull(cep); 9244 if (strcmp(cep->value, "nick") && strcmp(cep->value, "nick-user-host")) 9245 { 9246 config_error("%s:%i: set::ban-setter: value should be 'nick' or 'nick-user-host'", 9247 cep->file->filename, cep->line_number); 9248 errors++; 9249 } 9250 } 9251 else if (!strcmp(cep->name, "ban-setter-sync") || !strcmp(cep->name, "ban-setter-synch")) 9252 { 9253 CheckNull(cep); 9254 } 9255 else if (!strcmp(cep->name, "part-instead-of-quit-on-comment-change")) 9256 { 9257 CheckNull(cep); 9258 } 9259 else if (!strcmp(cep->name, "broadcast-channel-messages")) 9260 { 9261 CheckNull(cep); 9262 if (strcmp(cep->value, "auto") && 9263 strcmp(cep->value, "always") && 9264 strcmp(cep->value, "never")) 9265 { 9266 config_error("%s:%i: set::broadcast-channel-messages: value should be 'auto', 'always' or 'never'", 9267 cep->file->filename, cep->line_number); 9268 errors++; 9269 } 9270 } 9271 else if (!strcmp(cep->name, "allowed-channelchars")) 9272 { 9273 CheckNull(cep); 9274 if (!allowed_channelchars_strtoval(cep->value)) 9275 { 9276 config_error("%s:%i: set::allowed-channelchars: value should be one of: 'ascii', 'utf8' or 'any'", 9277 cep->file->filename, cep->line_number); 9278 errors++; 9279 } 9280 } 9281 else if (!strcmp(cep->name, "hide-idle-time")) 9282 { 9283 for (cepp = cep->items; cepp; cepp = cepp->next) 9284 { 9285 CheckNull(cepp); 9286 if (!strcmp(cepp->name, "policy")) 9287 { 9288 if (!hideidletime_strtoval(cepp->value)) 9289 { 9290 config_error("%s:%i: set::hide-idle-time::policy: value should be one of: 'never', 'always', 'usermode' or 'oper-usermode'", 9291 cepp->file->filename, cepp->line_number); 9292 errors++; 9293 } 9294 } 9295 else 9296 { 9297 config_error_unknown(cepp->file->filename, 9298 cepp->line_number, "set::hide-idle-time", 9299 cepp->name); 9300 errors++; 9301 continue; 9302 } 9303 } 9304 } else if (!strcmp(cep->name, "limit-svscmds")) 9305 { 9306 CheckNull(cep); 9307 if (strcmp(cep->value, "servers") && strcmp(cep->value, "ulines")) 9308 { 9309 config_error("%s:%i: set::limit-svscmds: value must be one of: 'servers' or 'ulines'", 9310 cep->file->filename, cep->line_number); 9311 errors++; 9312 } 9313 } else 9314 { 9315 int used = 0; 9316 for (h = Hooks[HOOKTYPE_CONFIGTEST]; h; h = h->next) 9317 { 9318 int value, errs = 0; 9319 if (h->owner && !(h->owner->flags & MODFLAG_TESTING) && 9320 !(h->owner->options & MOD_OPT_PERM)) 9321 continue; 9322 value = (*(h->func.intfunc))(conf,cep,CONFIG_SET, &errs); 9323 if (value == 2) 9324 used = 1; 9325 if (value == 1) 9326 { 9327 used = 1; 9328 break; 9329 } 9330 if (value == -1) 9331 { 9332 used = 1; 9333 errors += errs; 9334 break; 9335 } 9336 if (value == -2) 9337 { 9338 used = 1; 9339 errors += errs; 9340 } 9341 } 9342 if (!used) { 9343 config_error("%s:%i: unknown directive set::%s", 9344 cep->file->filename, cep->line_number, 9345 cep->name); 9346 errors++; 9347 } 9348 } 9349 } 9350 return errors; 9351 } 9352 9353 int _conf_loadmodule(ConfigFile *conf, ConfigEntry *ce) 9354 { 9355 const char *ret; 9356 if (!ce->value) 9357 { 9358 config_status("%s:%i: loadmodule without filename", 9359 ce->file->filename, ce->line_number); 9360 return -1; 9361 } 9362 9363 if (is_blacklisted_module(ce->value)) 9364 { 9365 /* config_warn("%s:%i: Module '%s' is blacklisted, not loading", 9366 ce->file->filename, ce->line_number, ce->value); */ 9367 return 1; 9368 } 9369 9370 if ((ret = Module_Create(ce->value))) { 9371 config_error("%s:%i: loadmodule %s: failed to load: %s", 9372 ce->file->filename, ce->line_number, 9373 ce->value, ret); 9374 return -1; 9375 } 9376 return 1; 9377 } 9378 9379 int _test_loadmodule(ConfigFile *conf, ConfigEntry *ce) 9380 { 9381 return 0; 9382 } 9383 9384 int _test_blacklist_module(ConfigFile *conf, ConfigEntry *ce) 9385 { 9386 const char *path; 9387 ConfigItem_blacklist_module *m; 9388 9389 if (!ce->value) 9390 { 9391 config_status("%s:%i: blacklist-module: no module name given to blacklist", 9392 ce->file->filename, ce->line_number); 9393 return -1; 9394 } 9395 9396 path = Module_TransformPath(ce->value); 9397 9398 m = safe_alloc(sizeof(ConfigItem_blacklist_module)); 9399 safe_strdup(m->name, ce->value); 9400 AddListItem(m, conf_blacklist_module); 9401 9402 return 0; 9403 } 9404 9405 int is_blacklisted_module(const char *name) 9406 { 9407 const char *path = Module_TransformPath(name); 9408 ConfigItem_blacklist_module *m; 9409 9410 for (m = conf_blacklist_module; m; m = m->next) 9411 if (match_simple(m->name, name) || match_simple(m->name, path)) 9412 return 1; 9413 9414 return 0; 9415 } 9416 9417 void start_listeners(void) 9418 { 9419 ConfigItem_listen *listener; 9420 int failed = 0, ports_bound = 0; 9421 char boundmsg_ipv4[512], boundmsg_ipv6[512]; 9422 int last_errno = 0; 9423 9424 *boundmsg_ipv4 = *boundmsg_ipv6 = '\0'; 9425 9426 for (listener = conf_listen; listener; listener = listener->next) 9427 { 9428 /* Try to bind to any ports that are not yet bound and not marked as temporary */ 9429 if (!(listener->options & LISTENER_BOUND) && !listener->flag.temporary) 9430 { 9431 if (add_listener(listener) == -1) 9432 { 9433 /* Error already printed upstream */ 9434 failed = 1; 9435 last_errno = ERRNO; 9436 } else { 9437 if (loop.booted) 9438 { 9439 unreal_log(ULOG_INFO, "listen", "LISTEN_ADDED", NULL, 9440 "UnrealIRCd is now also listening on $listen_ip:$listen_port", 9441 log_data_string("listen_ip", listener->ip), 9442 log_data_integer("listen_port", listener->port)); 9443 } else { 9444 switch (listener->socket_type) 9445 { 9446 case SOCKET_TYPE_IPV4: 9447 snprintf(boundmsg_ipv4+strlen(boundmsg_ipv4), sizeof(boundmsg_ipv4)-strlen(boundmsg_ipv4), 9448 "%s:%d%s, ", listener->ip, listener->port, 9449 listener->options & LISTENER_TLS ? "(TLS)" : ""); 9450 break; 9451 case SOCKET_TYPE_IPV6: 9452 snprintf(boundmsg_ipv6+strlen(boundmsg_ipv6), sizeof(boundmsg_ipv6)-strlen(boundmsg_ipv6), 9453 "%s:%d%s, ", listener->ip, listener->port, 9454 listener->options & LISTENER_TLS ? "(TLS)" : ""); 9455 break; 9456 // TODO: show unix domain sockets ;) 9457 default: 9458 break; 9459 } 9460 } 9461 } 9462 } 9463 9464 /* NOTE: do not merge this with code above (nor in an else block), 9465 * as add_listener() affects this flag. 9466 */ 9467 if (listener->options & LISTENER_BOUND) 9468 ports_bound++; 9469 } 9470 9471 if (ports_bound == 0) 9472 { 9473 #ifdef _WIN32 9474 if (last_errno == WSAEADDRINUSE) 9475 #else 9476 if (last_errno == EADDRINUSE) 9477 #endif 9478 { 9479 /* We can be specific */ 9480 unreal_log(ULOG_FATAL, "listen", "ALL_LISTEN_PORTS_FAILED", NULL, 9481 "Unable to listen on any ports. " 9482 "Most likely UnrealIRCd is already running."); 9483 } else { 9484 unreal_log(ULOG_FATAL, "listen", "ALL_LISTEN_PORTS_FAILED", NULL, 9485 "Unable to listen on any ports. " 9486 "Please verify that no other process is using the ports. " 9487 "Also, on some IRCd shells you may have to use listen::bind-ip " 9488 "with a specific IP assigned to you (rather than \"*\")."); 9489 } 9490 exit(-1); 9491 } 9492 9493 if (failed && !loop.booted) 9494 { 9495 unreal_log(ULOG_FATAL, "listen", "SOME_LISTEN_PORTS_FAILED", NULL, 9496 "Unable to listen on all ports (some of them succeeded, some of them failed). " 9497 "Please verify that no other process is using the port(s). " 9498 "Also, on some IRCd shells you may have to use listen::bind-ip " 9499 "with a specific IP assigned to you (rather than \"*\")."); 9500 exit(-1); 9501 } 9502 9503 if (!loop.booted) 9504 { 9505 if (strlen(boundmsg_ipv4) > 2) 9506 boundmsg_ipv4[strlen(boundmsg_ipv4)-2] = '\0'; 9507 if (strlen(boundmsg_ipv6) > 2) 9508 boundmsg_ipv6[strlen(boundmsg_ipv6)-2] = '\0'; 9509 9510 if (!*boundmsg_ipv4) 9511 strlcpy(boundmsg_ipv4, "<none>", sizeof(boundmsg_ipv4)); 9512 if (!*boundmsg_ipv6) 9513 strlcpy(boundmsg_ipv6, "<none>", sizeof(boundmsg_ipv6)); 9514 9515 unreal_log(ULOG_INFO, "listen", "LISTENING", NULL, 9516 "UnrealIRCd is now listening on the following addresses/ports:\n" 9517 "IPv4: $ipv4_port_list\n" 9518 "IPv6: $ipv6_port_list\n", 9519 log_data_string("ipv4_port_list", boundmsg_ipv4), 9520 log_data_string("ipv6_port_list", boundmsg_ipv6)); 9521 } 9522 } 9523 9524 /* Actually use configuration */ 9525 void config_run(void) 9526 { 9527 loop.config_status = CONFIG_STATUS_POSTLOAD; 9528 extcmodes_check_for_changes(); 9529 start_listeners(); 9530 if (!loop.booted) 9531 add_proc_io_server(); 9532 free_all_config_resources(); 9533 } 9534 9535 int _conf_offchans(ConfigFile *conf, ConfigEntry *ce) 9536 { 9537 ConfigEntry *cep, *cepp; 9538 9539 for (cep = ce->items; cep; cep = cep->next) 9540 { 9541 ConfigItem_offchans *of = safe_alloc(sizeof(ConfigItem_offchans)); 9542 strlcpy(of->name, cep->name, CHANNELLEN+1); 9543 for (cepp = cep->items; cepp; cepp = cepp->next) 9544 { 9545 if (!strcmp(cepp->name, "topic")) 9546 safe_strdup(of->topic, cepp->value); 9547 } 9548 AddListItem(of, conf_offchans); 9549 } 9550 return 0; 9551 } 9552 9553 int _test_offchans(ConfigFile *conf, ConfigEntry *ce) 9554 { 9555 int errors = 0; 9556 ConfigEntry *cep, *cep2; 9557 9558 if (!ce->items) 9559 { 9560 config_error("%s:%i: empty official-channels block", 9561 ce->file->filename, ce->line_number); 9562 return 1; 9563 } 9564 9565 config_warn("set::official-channels is deprecated. It often does not do what you want. " 9566 "You're better of creating a channel, setting all modes, topic, etc. to your liking " 9567 "and then making the channel permanent (MODE #channel +P). " 9568 "The channel will then be stored in a database to preserve it between restarts."); 9569 9570 for (cep = ce->items; cep; cep = cep->next) 9571 { 9572 if (strlen(cep->name) > CHANNELLEN) 9573 { 9574 config_error("%s:%i: official-channels: '%s' name too long (max %d characters).", 9575 cep->file->filename, cep->line_number, cep->name, CHANNELLEN); 9576 errors++; 9577 continue; 9578 } 9579 if (!valid_channelname(cep->name)) 9580 { 9581 config_error("%s:%i: official-channels: '%s' is not a valid channel name.", 9582 cep->file->filename, cep->line_number, cep->name); 9583 errors++; 9584 continue; 9585 } 9586 for (cep2 = cep->items; cep2; cep2 = cep2->next) 9587 { 9588 if (!cep2->value) 9589 { 9590 config_error_empty(cep2->file->filename, 9591 cep2->line_number, "official-channels", 9592 cep2->name); 9593 errors++; 9594 continue; 9595 } 9596 if (!strcmp(cep2->name, "topic")) 9597 { 9598 if (strlen(cep2->value) > MAXTOPICLEN) 9599 { 9600 config_error("%s:%i: official-channels::%s: topic too long (max %d characters).", 9601 cep2->file->filename, cep2->line_number, cep->name, MAXTOPICLEN); 9602 errors++; 9603 continue; 9604 } 9605 } else { 9606 config_error_unknown(cep2->file->filename, 9607 cep2->line_number, "official-channels", 9608 cep2->name); 9609 errors++; 9610 continue; 9611 } 9612 } 9613 } 9614 return errors; 9615 } 9616 9617 int _conf_alias(ConfigFile *conf, ConfigEntry *ce) 9618 { 9619 ConfigItem_alias *alias = NULL; 9620 ConfigItem_alias_format *format; 9621 ConfigEntry *cep, *cepp; 9622 RealCommand *cmptr; 9623 9624 if ((cmptr = find_command(ce->value, CMD_ALIAS))) 9625 CommandDelX(NULL, cmptr); 9626 if (find_command_simple(ce->value)) 9627 { 9628 config_warn("%s:%i: Alias '%s' would conflict with command (or server token) '%s', alias not added.", 9629 ce->file->filename, ce->line_number, 9630 ce->value, ce->value); 9631 return 0; 9632 } 9633 if ((alias = find_alias(ce->value))) 9634 DelListItem(alias, conf_alias); 9635 alias = safe_alloc(sizeof(ConfigItem_alias)); 9636 safe_strdup(alias->alias, ce->value); 9637 for (cep = ce->items; cep; cep = cep->next) 9638 { 9639 if (!strcmp(cep->name, "format")) { 9640 format = safe_alloc(sizeof(ConfigItem_alias_format)); 9641 safe_strdup(format->format, cep->value); 9642 format->expr = unreal_create_match(MATCH_PCRE_REGEX, cep->value, NULL); 9643 if (!format->expr) 9644 abort(); /* Impossible due to _test_alias earlier */ 9645 for (cepp = cep->items; cepp; cepp = cepp->next) { 9646 if (!strcmp(cepp->name, "nick") || 9647 !strcmp(cepp->name, "target") || 9648 !strcmp(cepp->name, "command")) { 9649 safe_strdup(format->nick, cepp->value); 9650 } 9651 else if (!strcmp(cepp->name, "parameters")) { 9652 safe_strdup(format->parameters, cepp->value); 9653 } 9654 else if (!strcmp(cepp->name, "type")) { 9655 if (!strcmp(cepp->value, "services")) 9656 format->type = ALIAS_SERVICES; 9657 else if (!strcmp(cepp->value, "stats")) 9658 format->type = ALIAS_STATS; 9659 else if (!strcmp(cepp->value, "normal")) 9660 format->type = ALIAS_NORMAL; 9661 else if (!strcmp(cepp->value, "channel")) 9662 format->type = ALIAS_CHANNEL; 9663 else if (!strcmp(cepp->value, "real")) 9664 format->type = ALIAS_REAL; 9665 } 9666 } 9667 AddListItem(format, alias->format); 9668 } 9669 9670 else if (!strcmp(cep->name, "nick") || !strcmp(cep->name, "target")) 9671 { 9672 safe_strdup(alias->nick, cep->value); 9673 } 9674 else if (!strcmp(cep->name, "type")) { 9675 if (!strcmp(cep->value, "services")) 9676 alias->type = ALIAS_SERVICES; 9677 else if (!strcmp(cep->value, "stats")) 9678 alias->type = ALIAS_STATS; 9679 else if (!strcmp(cep->value, "normal")) 9680 alias->type = ALIAS_NORMAL; 9681 else if (!strcmp(cep->value, "channel")) 9682 alias->type = ALIAS_CHANNEL; 9683 else if (!strcmp(cep->value, "command")) 9684 alias->type = ALIAS_COMMAND; 9685 } 9686 else if (!strcmp(cep->name, "spamfilter")) 9687 alias->spamfilter = config_checkval(cep->value, CFG_YESNO); 9688 } 9689 if (BadPtr(alias->nick) && alias->type != ALIAS_COMMAND) { 9690 safe_strdup(alias->nick, alias->alias); 9691 } 9692 AliasAdd(NULL, alias->alias, cmd_alias, 1, CMD_USER|CMD_ALIAS); 9693 9694 AddListItem(alias, conf_alias); 9695 return 0; 9696 } 9697 9698 9699 int _test_alias(ConfigFile *conf, ConfigEntry *ce) { 9700 int errors = 0; 9701 ConfigEntry *cep, *cepp; 9702 char has_type = 0, has_target = 0, has_format = 0; 9703 char type = 0; 9704 9705 if (!ce->items) 9706 { 9707 config_error("%s:%i: empty alias block", 9708 ce->file->filename, ce->line_number); 9709 return 1; 9710 } 9711 if (!ce->value) 9712 { 9713 config_error("%s:%i: alias without name", 9714 ce->file->filename, ce->line_number); 9715 errors++; 9716 } 9717 else if (!find_command(ce->value, CMD_ALIAS) && find_command(ce->value, 0)) { 9718 config_status("%s:%i: %s is an existing command, can not add alias", 9719 ce->file->filename, ce->line_number, ce->value); 9720 errors++; 9721 } 9722 for (cep = ce->items; cep; cep = cep->next) 9723 { 9724 if (config_is_blankorempty(cep, "alias")) 9725 { 9726 errors++; 9727 continue; 9728 } 9729 if (!strcmp(cep->name, "format")) { 9730 char *err = NULL; 9731 Match *expr; 9732 char has_type = 0, has_target = 0, has_parameters = 0; 9733 9734 has_format = 1; 9735 expr = unreal_create_match(MATCH_PCRE_REGEX, cep->value, &err); 9736 if (!expr) 9737 { 9738 config_error("%s:%i: alias::format contains an invalid regex: %s", 9739 cep->file->filename, cep->line_number, err); 9740 } else { 9741 unreal_delete_match(expr); 9742 } 9743 9744 for (cepp = cep->items; cepp; cepp = cepp->next) { 9745 if (config_is_blankorempty(cepp, "alias::format")) 9746 { 9747 errors++; 9748 continue; 9749 } 9750 if (!strcmp(cepp->name, "nick") || 9751 !strcmp(cepp->name, "command") || 9752 !strcmp(cepp->name, "target")) 9753 { 9754 if (has_target) 9755 { 9756 config_warn_duplicate(cepp->file->filename, 9757 cepp->line_number, 9758 "alias::format::target"); 9759 continue; 9760 } 9761 has_target = 1; 9762 } 9763 else if (!strcmp(cepp->name, "type")) 9764 { 9765 if (has_type) 9766 { 9767 config_warn_duplicate(cepp->file->filename, 9768 cepp->line_number, 9769 "alias::format::type"); 9770 continue; 9771 } 9772 has_type = 1; 9773 if (!strcmp(cepp->value, "services")) 9774 ; 9775 else if (!strcmp(cepp->value, "stats")) 9776 ; 9777 else if (!strcmp(cepp->value, "normal")) 9778 ; 9779 else if (!strcmp(cepp->value, "channel")) 9780 ; 9781 else if (!strcmp(cepp->value, "real")) 9782 ; 9783 else 9784 { 9785 config_error("%s:%i: unknown alias type", 9786 cepp->file->filename, cepp->line_number); 9787 errors++; 9788 } 9789 } 9790 else if (!strcmp(cepp->name, "parameters")) 9791 { 9792 if (has_parameters) 9793 { 9794 config_warn_duplicate(cepp->file->filename, 9795 cepp->line_number, 9796 "alias::format::parameters"); 9797 continue; 9798 } 9799 has_parameters = 1; 9800 } 9801 else 9802 { 9803 config_error_unknown(cepp->file->filename, 9804 cepp->line_number, "alias::format", 9805 cepp->name); 9806 errors++; 9807 } 9808 } 9809 if (!has_target) 9810 { 9811 config_error_missing(cep->file->filename, 9812 cep->line_number, "alias::format::target"); 9813 errors++; 9814 } 9815 if (!has_type) 9816 { 9817 config_error_missing(cep->file->filename, 9818 cep->line_number, "alias::format::type"); 9819 errors++; 9820 } 9821 if (!has_parameters) 9822 { 9823 config_error_missing(cep->file->filename, 9824 cep->line_number, "alias::format::parameters"); 9825 errors++; 9826 } 9827 } 9828 else if (!strcmp(cep->name, "nick") || !strcmp(cep->name, "target")) 9829 { 9830 if (has_target) 9831 { 9832 config_warn_duplicate(cep->file->filename, 9833 cep->line_number, "alias::target"); 9834 continue; 9835 } 9836 has_target = 1; 9837 } 9838 else if (!strcmp(cep->name, "type")) { 9839 if (has_type) 9840 { 9841 config_warn_duplicate(cep->file->filename, 9842 cep->line_number, "alias::type"); 9843 continue; 9844 } 9845 has_type = 1; 9846 if (!strcmp(cep->value, "services")) 9847 ; 9848 else if (!strcmp(cep->value, "stats")) 9849 ; 9850 else if (!strcmp(cep->value, "normal")) 9851 ; 9852 else if (!strcmp(cep->value, "channel")) 9853 ; 9854 else if (!strcmp(cep->value, "command")) 9855 type = 'c'; 9856 else { 9857 config_error("%s:%i: unknown alias type", 9858 cep->file->filename, cep->line_number); 9859 errors++; 9860 } 9861 } 9862 else if (!strcmp(cep->name, "spamfilter")) 9863 ; 9864 else { 9865 config_error_unknown(cep->file->filename, cep->line_number, 9866 "alias", cep->name); 9867 errors++; 9868 } 9869 } 9870 if (!has_type) 9871 { 9872 config_error_missing(ce->file->filename, ce->line_number, 9873 "alias::type"); 9874 errors++; 9875 } 9876 if (!has_format && type == 'c') 9877 { 9878 config_error("%s:%d: alias::type is 'command' but no alias::format was specified", 9879 ce->file->filename, ce->line_number); 9880 errors++; 9881 } 9882 else if (has_format && type != 'c') 9883 { 9884 config_error("%s:%d: alias::format specified when type is not 'command'", 9885 ce->file->filename, ce->line_number); 9886 errors++; 9887 } 9888 return errors; 9889 } 9890 9891 int _conf_deny(ConfigFile *conf, ConfigEntry *ce) 9892 { 9893 Hook *h; 9894 9895 if (!strcmp(ce->value, "channel")) 9896 _conf_deny_channel(conf, ce); 9897 else if (!strcmp(ce->value, "version")) 9898 _conf_deny_version(conf, ce); 9899 else 9900 { 9901 int value; 9902 for (h = Hooks[HOOKTYPE_CONFIGRUN]; h; h = h->next) 9903 { 9904 value = (*(h->func.intfunc))(conf,ce,CONFIG_DENY); 9905 if (value == 1) 9906 break; 9907 } 9908 return 0; 9909 } 9910 return 0; 9911 } 9912 9913 int _conf_deny_channel(ConfigFile *conf, ConfigEntry *ce) 9914 { 9915 ConfigItem_deny_channel *deny = NULL; 9916 ConfigEntry *cep; 9917 9918 deny = safe_alloc(sizeof(ConfigItem_deny_channel)); 9919 for (cep = ce->items; cep; cep = cep->next) 9920 { 9921 if (!strcmp(cep->name, "channel")) 9922 { 9923 safe_strdup(deny->channel, cep->value); 9924 } 9925 else if (!strcmp(cep->name, "redirect")) 9926 { 9927 safe_strdup(deny->redirect, cep->value); 9928 } 9929 else if (!strcmp(cep->name, "reason")) 9930 { 9931 safe_strdup(deny->reason, cep->value); 9932 } 9933 else if (!strcmp(cep->name, "warn")) 9934 { 9935 deny->warn = config_checkval(cep->value,CFG_YESNO); 9936 } 9937 else if (!strcmp(cep->name, "class")) 9938 { 9939 safe_strdup(deny->class, cep->value); 9940 } 9941 else if (!strcmp(cep->name, "match") || !strcmp(cep->name, "mask")) 9942 { 9943 conf_match_block(conf, cep, &deny->match); 9944 } 9945 } 9946 AddListItem(deny, conf_deny_channel); 9947 return 0; 9948 } 9949 int _conf_deny_version(ConfigFile *conf, ConfigEntry *ce) 9950 { 9951 ConfigItem_deny_version *deny = NULL; 9952 ConfigEntry *cep; 9953 9954 deny = safe_alloc(sizeof(ConfigItem_deny_version)); 9955 for (cep = ce->items; cep; cep = cep->next) 9956 { 9957 if (!strcmp(cep->name, "mask")) 9958 { 9959 safe_strdup(deny->mask, cep->value); 9960 } 9961 else if (!strcmp(cep->name, "version")) 9962 { 9963 safe_strdup(deny->version, cep->value); 9964 } 9965 else if (!strcmp(cep->name, "flags")) 9966 { 9967 safe_strdup(deny->flags, cep->value); 9968 } 9969 } 9970 AddListItem(deny, conf_deny_version); 9971 return 0; 9972 } 9973 9974 int _test_deny(ConfigFile *conf, ConfigEntry *ce) 9975 { 9976 ConfigEntry *cep; 9977 int errors = 0; 9978 Hook *h; 9979 9980 if (!ce->value) 9981 { 9982 config_error("%s:%i: deny without type", 9983 ce->file->filename, ce->line_number); 9984 return 1; 9985 } 9986 if (!strcmp(ce->value, "channel")) 9987 { 9988 char has_channel = 0, has_warn = 0, has_reason = 0, has_redirect = 0, has_class = 0; 9989 char has_mask = 0, has_match = 0; 9990 for (cep = ce->items; cep; cep = cep->next) 9991 { 9992 if (config_is_blankorempty(cep, "deny channel")) 9993 { 9994 errors++; 9995 continue; 9996 } 9997 if (!strcmp(cep->name, "channel")) 9998 { 9999 if (has_channel) 10000 { 10001 config_warn_duplicate(cep->file->filename, 10002 cep->line_number, "deny channel::channel"); 10003 continue; 10004 } 10005 has_channel = 1; 10006 } 10007 else if (!strcmp(cep->name, "redirect")) 10008 { 10009 if (has_redirect) 10010 { 10011 config_warn_duplicate(cep->file->filename, 10012 cep->line_number, "deny channel::redirect"); 10013 continue; 10014 } 10015 has_redirect = 1; 10016 } 10017 else if (!strcmp(cep->name, "reason")) 10018 { 10019 if (has_reason) 10020 { 10021 config_warn_duplicate(cep->file->filename, 10022 cep->line_number, "deny channel::reason"); 10023 continue; 10024 } 10025 has_reason = 1; 10026 } 10027 else if (!strcmp(cep->name, "warn")) 10028 { 10029 if (has_warn) 10030 { 10031 config_warn_duplicate(cep->file->filename, 10032 cep->line_number, "deny channel::warn"); 10033 continue; 10034 } 10035 has_warn = 1; 10036 } 10037 else if (!strcmp(cep->name, "class")) 10038 { 10039 if (has_class) 10040 { 10041 config_warn_duplicate(cep->file->filename, 10042 cep->line_number, "deny channel::class"); 10043 continue; 10044 } 10045 has_class = 1; 10046 } 10047 else if (!strcmp(cep->name, "match")) 10048 { 10049 has_match = 1; 10050 test_match_block(conf, cep, &errors); 10051 } 10052 else if (!strcmp(cep->name, "mask")) 10053 { 10054 has_mask = 1; 10055 test_match_block(conf, cep, &errors); 10056 } 10057 else 10058 { 10059 config_error_unknown(cep->file->filename, 10060 cep->line_number, "deny channel", cep->name); 10061 errors++; 10062 } 10063 } 10064 if (!has_channel) 10065 { 10066 config_error_missing(ce->file->filename, ce->line_number, 10067 "deny channel::channel"); 10068 errors++; 10069 } 10070 if (!has_reason) 10071 { 10072 config_error_missing(ce->file->filename, ce->line_number, 10073 "deny channel::reason"); 10074 errors++; 10075 } 10076 if (has_mask && has_match) 10077 { 10078 config_error("%s:%d: You cannot have both ::mask and ::match. " 10079 "You should only use %s %s::match.", 10080 ce->file->filename, ce->line_number, ce->name, ce->value); 10081 errors++; 10082 } 10083 } 10084 else if (!strcmp(ce->value, "version")) 10085 { 10086 char has_mask = 0, has_version = 0, has_flags = 0; 10087 for (cep = ce->items; cep; cep = cep->next) 10088 { 10089 if (config_is_blankorempty(cep, "deny version")) 10090 { 10091 errors++; 10092 continue; 10093 } 10094 if (!strcmp(cep->name, "mask")) 10095 { 10096 if (has_mask) 10097 { 10098 config_warn_duplicate(cep->file->filename, 10099 cep->line_number, "deny version::mask"); 10100 continue; 10101 } 10102 has_mask = 1; 10103 } 10104 else if (!strcmp(cep->name, "version")) 10105 { 10106 if (has_version) 10107 { 10108 config_warn_duplicate(cep->file->filename, 10109 cep->line_number, "deny version::version"); 10110 continue; 10111 } 10112 has_version = 1; 10113 } 10114 else if (!strcmp(cep->name, "flags")) 10115 { 10116 if (has_flags) 10117 { 10118 config_warn_duplicate(cep->file->filename, 10119 cep->line_number, "deny version::flags"); 10120 continue; 10121 } 10122 has_flags = 1; 10123 } 10124 else 10125 { 10126 config_error_unknown(cep->file->filename, 10127 cep->line_number, "deny version", cep->name); 10128 errors++; 10129 } 10130 } 10131 if (!has_mask) 10132 { 10133 config_error_missing(ce->file->filename, ce->line_number, 10134 "deny version::mask"); 10135 errors++; 10136 } 10137 if (!has_version) 10138 { 10139 config_error_missing(ce->file->filename, ce->line_number, 10140 "deny version::version"); 10141 errors++; 10142 } 10143 if (!has_flags) 10144 { 10145 config_error_missing(ce->file->filename, ce->line_number, 10146 "deny version::flags"); 10147 errors++; 10148 } 10149 } 10150 else 10151 { 10152 int used = 0; 10153 for (h = Hooks[HOOKTYPE_CONFIGTEST]; h; h = h->next) 10154 { 10155 int value, errs = 0; 10156 if (h->owner && !(h->owner->flags & MODFLAG_TESTING) 10157 && !(h->owner->options & MOD_OPT_PERM)) 10158 continue; 10159 value = (*(h->func.intfunc))(conf,ce,CONFIG_DENY, &errs); 10160 if (value == 2) 10161 used = 1; 10162 if (value == 1) 10163 { 10164 used = 1; 10165 break; 10166 } 10167 if (value == -1) 10168 { 10169 used = 1; 10170 errors += errs; 10171 break; 10172 } 10173 if (value == -2) 10174 { 10175 used = 1; 10176 errors += errs; 10177 } 10178 } 10179 if (!used) { 10180 config_error("%s:%i: unknown deny type %s", 10181 ce->file->filename, ce->line_number, 10182 ce->value); 10183 return 1; 10184 } 10185 return errors; 10186 } 10187 10188 return errors; 10189 } 10190 10191 Secret *find_secret(const char *secret_name) 10192 { 10193 Secret *s; 10194 for (s = secrets; s; s = s->next) 10195 { 10196 if (!strcasecmp(s->name, secret_name)) 10197 return s; 10198 } 10199 return NULL; 10200 } 10201 10202 void free_secret_cache(SecretCache *c) 10203 { 10204 unrealdb_free_config(c->config); 10205 safe_free(c); 10206 } 10207 10208 void free_secret(Secret *s) 10209 { 10210 SecretCache *c, *c_next; 10211 for (c = s->cache; c; c = c_next) 10212 { 10213 c_next = c->next; 10214 DelListItem(c, s->cache); 10215 free_secret_cache(c); 10216 } 10217 safe_free(s->name); 10218 safe_free_sensitive(s->password); 10219 safe_free(s); 10220 } 10221 10222 char *_conf_secret_read_password_file(const char *fname) 10223 { 10224 char *pwd, *err; 10225 int fd, n; 10226 10227 #ifndef _WIN32 10228 fd = open(fname, O_RDONLY); 10229 #else 10230 fd = open(fname, _O_RDONLY|_O_BINARY); 10231 #endif 10232 if (fd < 0) 10233 { 10234 /* This should not happen, as we tested for file exists earlier.. */ 10235 config_error("Could not open file '%s': %s", fname, strerror(errno)); 10236 return NULL; 10237 } 10238 10239 pwd = safe_alloc_sensitive(512); 10240 n = read(fd, pwd, 511); 10241 if (n <= 0) 10242 { 10243 close(fd); 10244 config_error("Could not read from file '%s': %s", fname, strerror(errno)); 10245 safe_free_sensitive(pwd); 10246 return NULL; 10247 } 10248 close(fd); 10249 stripcrlf(pwd); 10250 sodium_stackzero(1024); 10251 if (!valid_secret_password(pwd, &err)) 10252 { 10253 config_error("Key from file '%s' does not meet password complexity requirements: %s", fname, err); 10254 safe_free_sensitive(pwd); 10255 return NULL; 10256 } 10257 return pwd; 10258 } 10259 10260 char *_conf_secret_read_prompt(const char *blockname) 10261 { 10262 char *pwd, *pwd_prompt; 10263 char buf[256]; 10264 10265 #ifdef _WIN32 10266 /* FIXME: add windows support? should be possible in GUI no? */ 10267 return NULL; 10268 #else 10269 snprintf(buf, sizeof(buf), "Enter password for secret '%s': ", blockname); 10270 pwd_prompt = getpass(buf); 10271 if (pwd_prompt) 10272 { 10273 pwd = safe_alloc_sensitive(512); 10274 strlcpy(pwd, pwd_prompt, 512); 10275 memset(pwd_prompt, 0, strlen(pwd_prompt)); // zero password out 10276 sodium_stackzero(1024); 10277 return pwd; 10278 } 10279 return NULL; 10280 #endif 10281 } 10282 10283 int _test_secret(ConfigFile *conf, ConfigEntry *ce) 10284 { 10285 int errors = 0; 10286 int has_password = 0, has_password_file = 0, has_password_prompt = 0; 10287 ConfigEntry *cep; 10288 char *err; 10289 Secret *existing; 10290 10291 if (!ce->value) 10292 { 10293 config_error("%s:%i: secret block needs a name, eg: secret xyz {", 10294 ce->file->filename, ce->line_number); 10295 errors++; 10296 return errors; /* need to return here since we dereference ce->value later.. */ 10297 } else { 10298 if (!security_group_valid_name(ce->value)) 10299 { 10300 config_error("%s:%i: secret block name '%s' contains invalid characters or is too long. " 10301 "Only letters, numbers, underscore and hyphen are allowed.", 10302 ce->file->filename, ce->line_number, ce->value); 10303 errors++; 10304 } 10305 } 10306 10307 existing = find_secret(ce->value); 10308 10309 for (cep = ce->items; cep; cep = cep->next) 10310 { 10311 if (!strcmp(cep->name, "password")) 10312 { 10313 int n; 10314 has_password = 1; 10315 CheckNull(cep); 10316 if (cep->items || 10317 (((n = Auth_AutoDetectHashType(cep->value))) && ((n == AUTHTYPE_BCRYPT) || (n == AUTHTYPE_ARGON2)))) 10318 { 10319 config_error("%s:%d: you cannot use hashed passwords here, see " 10320 "https://www.unrealircd.org/docs/Secret_block#secret-plaintext", 10321 cep->file->filename, cep->line_number); 10322 errors++; 10323 continue; 10324 } 10325 if (!valid_secret_password(cep->value, &err)) 10326 { 10327 config_error("%s:%d: secret::password does not meet password complexity requirements: %s", 10328 cep->file->filename, cep->line_number, err); 10329 errors++; 10330 } 10331 } else 10332 if (!strcmp(cep->name, "password-file")) 10333 { 10334 char *str; 10335 has_password_file = 1; 10336 CheckNull(cep); 10337 convert_to_absolute_path(&cep->value, CONFDIR); 10338 if (!file_exists(cep->value) && existing && existing->password) 10339 { 10340 /* Silently ignore the case where a secret block already 10341 * has the password read and now the file is no longer available. 10342 * This so secret::password-file can be used only to boot 10343 * and then the media (eg: USB stick) can be pulled. 10344 */ 10345 } else 10346 { 10347 str = _conf_secret_read_password_file(cep->value); 10348 if (!str) 10349 { 10350 config_error("%s:%d: secret::password-file: error reading password from file, see error from above.", 10351 cep->file->filename, cep->line_number); 10352 errors++; 10353 } 10354 safe_free_sensitive(str); 10355 } 10356 } else 10357 if (!strcmp(cep->name, "password-prompt")) 10358 { 10359 #ifdef _WIN32 10360 config_error("%s:%d: secret::password-prompt is not implemented in Windows at the moment, sorry!", 10361 cep->file->filename, cep->line_number); 10362 config_error("Choose a different method to enter passwords or use *NIX"); 10363 errors++; 10364 return errors; 10365 #endif 10366 has_password_prompt = 1; 10367 if (loop.booted && !find_secret(ce->value)) 10368 { 10369 config_error("%s:%d: you cannot add a new secret { } block that uses password-prompt and then /REHASH. " 10370 "With 'password-prompt' you can only add such a password on boot.", 10371 cep->file->filename, cep->line_number); 10372 config_error("Either use a different method to enter passwords or restart the IRCd on the console."); 10373 errors++; 10374 } 10375 if (!loop.booted && !running_interactively()) 10376 { 10377 config_error("ERROR: IRCd is not running interactively, but via a cron job or something similar."); 10378 config_error("%s:%d: unable to prompt for password since IRCd is not started in a terminal", 10379 cep->file->filename, cep->line_number); 10380 config_error("Either use a different method to enter passwords or start the IRCd in a terminal/SSH/.."); 10381 } 10382 } else 10383 if (!strcmp(cep->name, "password-url")) 10384 { 10385 config_error("%s:%d: secret::password-url is not supported yet in this UnrealIRCd version.", 10386 cep->file->filename, cep->line_number); 10387 errors++; 10388 } else 10389 { 10390 config_error_unknown(cep->file->filename, cep->line_number, 10391 "secret", cep->name); 10392 errors++; 10393 continue; 10394 } 10395 if (cep->items) 10396 { 10397 config_error("%s:%d: secret::%s does not support sub-options (%s)", 10398 cep->file->filename, cep->line_number, 10399 cep->name, cep->items->name); 10400 errors++; 10401 } 10402 } 10403 10404 if (!has_password && !has_password_file && !has_password_prompt) 10405 { 10406 config_error("%s:%d: secret { } block must contain 1 of: password OR password-file OR password-prompt", 10407 ce->file->filename, ce->line_number); 10408 errors++; 10409 } 10410 10411 return errors; 10412 } 10413 10414 /* NOTE: contrary to all other _conf* stuff, this one actually runs during config_test, 10415 * so during the early CONFIG TEST stage rather than CONFIG RUN. 10416 * This so all secret { } block configuration is available already during TEST/POSTTEST 10417 * stage for modules, so they can check if the password is correct or not. 10418 */ 10419 int _conf_secret(ConfigFile *conf, ConfigEntry *ce) 10420 { 10421 ConfigEntry *cep; 10422 Secret *s; 10423 Secret *existing = find_secret(ce->value); 10424 10425 s = safe_alloc(sizeof(Secret)); 10426 safe_strdup(s->name, ce->value); 10427 10428 for (cep = ce->items; cep; cep = cep->next) 10429 { 10430 if (!strcmp(cep->name, "password")) 10431 { 10432 safe_strdup_sensitive(s->password, cep->value); 10433 destroy_string(cep->value); /* destroy the original */ 10434 } else 10435 if (!strcmp(cep->name, "password-file")) 10436 { 10437 if (!file_exists(cep->value) && existing && existing->password) 10438 { 10439 /* Silently ignore the case where a secret block already 10440 * has the password read and now the file is no longer available. 10441 * This so secret::password-file can be used only to boot 10442 * and then the media (eg: USB stick) can be pulled. 10443 */ 10444 } else 10445 { 10446 s->password = _conf_secret_read_password_file(cep->value); 10447 } 10448 } else 10449 if (!strcmp(cep->name, "password-prompt")) 10450 { 10451 if (!loop.booted && running_interactively()) 10452 { 10453 s->password = _conf_secret_read_prompt(ce->value); 10454 if (!s->password || !valid_secret_password(s->password, NULL)) 10455 { 10456 config_error("Invalid password entered on console (does not meet complexity requirements)"); 10457 /* This cannot be the correct password, so exit */ 10458 exit(-1); 10459 } 10460 } 10461 } 10462 } 10463 10464 /* This may happen if we run twice, due to destroy_string() earlier: */ 10465 if (BadPtr(s->password)) 10466 { 10467 free_secret(s); 10468 return 1; 10469 } 10470 10471 /* If there is an existing secret { } block with this name in memory 10472 * and it has a different password, then free that secret block 10473 */ 10474 if (existing) 10475 { 10476 if (!strcmp(s->password, existing->password)) 10477 { 10478 free_secret(s); 10479 return 1; 10480 } 10481 /* passwords differ, so free the old existing one, 10482 * including purging the cache for it. 10483 */ 10484 DelListItem(existing, secrets); 10485 free_secret(existing); 10486 } 10487 AddListItem(s, secrets); 10488 return 1; 10489 } 10490 10491 void resource_download_complete(const char *url, const char *file, const char *errorbuf, int cached, void *rs_key) 10492 { 10493 ConfigResource *rs = (ConfigResource *)rs_key; 10494 10495 rs->type &= ~RESOURCE_DLQUEUED; 10496 10497 if (config_verbose) 10498 config_status("resource_download_complete() for %s [%s]", url, errorbuf?errorbuf:"success"); 10499 10500 if (!file && !cached) 10501 { 10502 /* DOWNLOAD FAILED */ 10503 if (rs->cache_file) 10504 { 10505 unreal_log(ULOG_ERROR, "config", "DOWNLOAD_FAILED_SOFT", NULL, 10506 "$file:$line_number: Failed to download '$url': $error_message\n" 10507 "Using a cached copy instead.", 10508 log_data_string("file", rs->wce->ce->file->filename), 10509 log_data_integer("line_number", rs->wce->ce->line_number), 10510 log_data_string("url", displayurl(url)), 10511 log_data_string("error_message", errorbuf)); 10512 safe_strdup(rs->file, rs->cache_file); 10513 } else { 10514 unreal_log(ULOG_ERROR, "config", "DOWNLOAD_FAILED_HARD", NULL, 10515 "$file:$line_number: Failed to download '$url': $error_message", 10516 log_data_string("file", rs->wce->ce->file->filename), 10517 log_data_integer("line_number", rs->wce->ce->line_number), 10518 log_data_string("url", displayurl(url)), 10519 log_data_string("error_message", errorbuf)); 10520 /* Set error condition, this so config_read_file() later will stop. */ 10521 loop.config_load_failed = 1; 10522 /* We keep the other transfers running since they may raise (more) errors. 10523 * Which can be helpful so you can differentiate between an error of an 10524 * include on one server, or complete lack of internet connectvitity. 10525 */ 10526 } 10527 } 10528 else 10529 { 10530 if (cached) 10531 { 10532 /* Copy from cache */ 10533 safe_strdup(rs->file, rs->cache_file); 10534 } else { 10535 /* Copy to cache */ 10536 const char *cache_file = unreal_mkcache(url); 10537 unreal_copyfileex(file, cache_file, 1); 10538 safe_strdup(rs->file, cache_file); 10539 } 10540 } 10541 10542 if (rs->file) 10543 { 10544 if (rs->type & RESOURCE_INCLUDE) 10545 { 10546 if (config_read_file(rs->file, (char *)displayurl(rs->url)) < 0) 10547 loop.config_load_failed = 1; 10548 } else { 10549 ConfigEntryWrapper *wce; 10550 for (wce = rs->wce; wce; wce = wce->next) 10551 safe_strdup(wce->ce->value, rs->file); // now information of url is lost, hm!! 10552 } 10553 } 10554 } 10555 10556 /** Request to REHASH the configuration file. 10557 * The rehash will not be done immediately, just scheduled. 10558 * This means this function can safely be called from modules or 10559 * other areas. 10560 * @param client The client requesting the /REHASH. 10561 * If this is NULL then the rehash was requested 10562 * via a signal to the process or GUI. 10563 */ 10564 void request_rehash(Client *client) 10565 { 10566 json_t *j; 10567 10568 if (loop.rehashing) 10569 { 10570 if (client) 10571 sendnotice(client, "A rehash is already in progress"); 10572 return; 10573 } 10574 10575 /* Free any old json_rehash_log */ 10576 if (json_rehash_log) 10577 { 10578 json_decref(json_rehash_log); 10579 json_rehash_log = NULL; 10580 } 10581 10582 /* Start a new json_rehash_log */ 10583 json_rehash_log = json_object(); 10584 if (client) 10585 json_expand_client(json_rehash_log, "rehash_client", client, 99); 10586 j = json_array(); 10587 json_object_set_new(json_rehash_log, "log", j); 10588 10589 /* Now actually process the rehash request... */ 10590 loop.rehashing = 1; 10591 loop.rehash_save_client = client; 10592 config_read_start(); 10593 /* More config reading (or network I/O), and the actual rehash will 10594 * happen in "the main loop". See end of SocketLoop() in src/ircd.c. 10595 */ 10596 } 10597 10598 int rehash_internal(Client *client) 10599 { 10600 int failure; 10601 10602 /* Log it here if it is by a signal */ 10603 if (client == NULL) 10604 unreal_log(ULOG_INFO, "config", "CONFIG_RELOAD", NULL, "Rehashing server configuration file [./unrealircd rehash]"); 10605 10606 loop.rehashing = 2; /* now doing the actual rehash */ 10607 10608 failure = config_test(); 10609 if (failure == 0) 10610 config_run(); 10611 /* TODO: uh.. are we supposed to do all this for a failed rehash too? maybe some but not all? */ 10612 reread_motdsandrules(); 10613 unload_all_unused_umodes(); 10614 unload_all_unused_extcmodes(); 10615 unload_all_unused_extbans(); 10616 unload_all_unused_caps(); 10617 unload_all_unused_history_backends(); 10618 unload_all_unused_rpc_handlers(); 10619 // unload_all_unused_moddata(); -- this will crash 10620 clicap_check_for_changes(); 10621 umodes_check_for_changes(); 10622 charsys_check_for_changes(); 10623 10624 /* Clear everything now that we are done */ 10625 loop.rehashing = 0; 10626 remote_rehash_client = NULL; 10627 procio_post_rehash(failure); 10628 json_object_set_new(json_rehash_log, "success", json_boolean(failure ? 0 : 1)); 10629 RunHook(HOOKTYPE_REHASH_LOG, failure, json_rehash_log); 10630 10631 loop.config_status = CONFIG_STATUS_COMPLETE; 10632 return 1; 10633 } 10634 10635 void link_cleanup(ConfigItem_link *link_ptr) 10636 { 10637 safe_free(link_ptr->servername); 10638 free_security_group(link_ptr->incoming.match); 10639 Auth_FreeAuthConfig(link_ptr->auth); 10640 safe_free(link_ptr->outgoing.file); 10641 safe_free(link_ptr->outgoing.bind_ip); 10642 safe_free(link_ptr->outgoing.hostname); 10643 safe_free(link_ptr->hub); 10644 safe_free(link_ptr->leaf); 10645 if (link_ptr->ssl_ctx) 10646 { 10647 SSL_CTX_free(link_ptr->ssl_ctx); 10648 link_ptr->ssl_ctx = NULL; 10649 } 10650 if (link_ptr->tls_options) 10651 { 10652 free_tls_options(link_ptr->tls_options); 10653 link_ptr->tls_options = NULL; 10654 } 10655 } 10656 10657 void delete_linkblock(ConfigItem_link *link_ptr) 10658 { 10659 if (link_ptr->class) 10660 { 10661 link_ptr->class->xrefcount--; 10662 /* Perhaps the class is temporary too and we need to free it... */ 10663 if (link_ptr->class->flag.temporary && 10664 !link_ptr->class->clients && !link_ptr->class->xrefcount) 10665 { 10666 delete_classblock(link_ptr->class); 10667 link_ptr->class = NULL; 10668 } 10669 } 10670 link_cleanup(link_ptr); 10671 DelListItem(link_ptr, conf_link); 10672 safe_free(link_ptr); 10673 } 10674 10675 void delete_classblock(ConfigItem_class *class_ptr) 10676 { 10677 safe_free(class_ptr->name); 10678 DelListItem(class_ptr, conf_class); 10679 safe_free(class_ptr); 10680 } 10681 10682 void listen_cleanup() 10683 { 10684 int i = 0; 10685 ConfigItem_listen *listen_ptr, *next; 10686 10687 for (listen_ptr = conf_listen; listen_ptr; listen_ptr = next) 10688 { 10689 next = listen_ptr->next; 10690 if (listen_ptr->flag.temporary && !listen_ptr->clients) 10691 { 10692 safe_free(listen_ptr->ip); 10693 free_tls_options(listen_ptr->tls_options); 10694 DelListItem(listen_ptr, conf_listen); 10695 safe_free(listen_ptr->webserver); 10696 safe_free(listen_ptr->websocket_forward); 10697 safe_free(listen_ptr); 10698 i++; 10699 } 10700 } 10701 10702 if (i) 10703 close_unbound_listeners(); 10704 } 10705 10706 ConfigResource *find_config_resource(const char *resource) 10707 { 10708 ConfigResource *rs; 10709 10710 for (rs = config_resources; rs; rs = rs->next) 10711 { 10712 #ifdef _WIN32 10713 if (rs->file && !strcasecmp(resource, rs->file)) 10714 return rs; 10715 #else 10716 if (rs->file && !strcmp(resource, rs->file)) 10717 return rs; 10718 #endif 10719 if (rs->url && !strcasecmp(resource, rs->url)) 10720 return rs; 10721 } 10722 return NULL; 10723 } 10724 10725 /* Add configuration resource to list. 10726 * For files this doesn't do terribly much, except that you can use 10727 * the return value to judge on whether you should call config_read_file() or not. 10728 * For urls this adds the resource to the list of links to be downloaded. 10729 * @param resource File or URL of the resource 10730 * @param type A RESOURCE_ type such as RESOURCE_INCLUDE 10731 * @param ce The ConfigEntry where the add_config_resource() happened 10732 * for, such as the include block, etc. 10733 * @returns 0 if the file is already on our list (so no need to load it!) 10734 */ 10735 int add_config_resource(const char *resource, int type, ConfigEntry *ce) 10736 { 10737 ConfigResource *rs; 10738 ConfigEntryWrapper *wce; 10739 10740 if (config_verbose) 10741 config_status("add_config_resource() for '%s", resource); 10742 10743 wce = safe_alloc(sizeof(ConfigEntryWrapper)); 10744 wce->ce = ce; 10745 10746 rs = find_config_resource(resource); 10747 if (rs) 10748 { 10749 /* Existing entry, add us to the list of 10750 * items who are interested in this resource ;) 10751 */ 10752 AddListItem(wce, rs->wce); 10753 return 0; 10754 } 10755 10756 /* New entry */ 10757 rs = safe_alloc(sizeof(ConfigResource)); 10758 rs->wce = wce; 10759 AddListItem(rs, config_resources); 10760 10761 if (!url_is_valid(resource)) 10762 { 10763 safe_strdup(rs->file, resource); 10764 } else { 10765 const char *cache_file; 10766 time_t modtime; 10767 10768 safe_strdup(rs->url, resource); 10769 rs->type = type|RESOURCE_REMOTE|RESOURCE_DLQUEUED; 10770 10771 cache_file = unreal_mkcache(rs->url); 10772 modtime = unreal_getfilemodtime(cache_file); 10773 if (modtime > 0) 10774 { 10775 safe_strdup(rs->cache_file, cache_file); /* Cached copy is available */ 10776 /* Check if there is an "url-refresh" argument */ 10777 ConfigEntry *cep, *prev = NULL; 10778 for (cep = ce->items; cep; cep = cep->next) 10779 { 10780 if (!strcmp(cep->name, "url-refresh")) 10781 { 10782 /* First find out the time value of url-refresh... (eg '7d' -> 86400*7) */ 10783 long refresh_time = 0; 10784 if (cep->value) 10785 refresh_time = config_checkval(cep->value, CFG_TIME); 10786 /* Then remove the config item so it is not seen by the rest of unrealircd. 10787 * Can't use DelListItem() here as ConfigEntry has no ->prev, only ->next. 10788 */ 10789 if (prev) 10790 prev->next = cep->next; /* (skip over us) */ 10791 else 10792 ce->items = cep->next; /* (new head) */ 10793 /* ..and free it */ 10794 config_entry_free(cep); 10795 /* And now check if the current cached copy is recent enough */ 10796 if (TStime() - modtime < refresh_time) 10797 { 10798 /* Don't download, use cached copy */ 10799 //config_status("DEBUG: using cached copy due to url-refresh %ld", refresh_time); 10800 resource_download_complete(rs->url, NULL, NULL, 1, rs); 10801 return 1; 10802 } else { 10803 //config_status("DEBUG: requires download attempt, out of date url-refresh %ld < %ld", refresh_time, TStime() - modtime); 10804 } 10805 break; // MUST break now as we touched the linked list. 10806 } 10807 prev = cep; 10808 } 10809 } 10810 download_file_async(rs->url, modtime, resource_download_complete, (void *)rs, NULL, DOWNLOAD_MAX_REDIRECTS); 10811 } 10812 return 1; 10813 } 10814 10815 void free_all_config_resources(void) 10816 { 10817 ConfigResource *rs, *next; 10818 ConfigEntryWrapper *wce, *wce_next; 10819 10820 for (rs = config_resources; rs; rs = next) 10821 { 10822 next = rs->next; 10823 for (wce = rs->wce; wce; wce = wce_next) 10824 { 10825 wce_next = wce->next; 10826 safe_free(wce); 10827 } 10828 rs->wce = NULL; 10829 if (rs->type & RESOURCE_REMOTE) 10830 { 10831 url_cancel_handle_by_callback_data(rs); 10832 /* Delete the file, but only if it's not a cached version */ 10833 if (rs->file && strncmp(rs->file, CACHEDIR, strlen(CACHEDIR))) 10834 { 10835 remove(rs->file); 10836 } 10837 safe_free(rs->url); 10838 } 10839 safe_free(rs->file); 10840 safe_free(rs->cache_file); 10841 DelListItem(rs, config_resources); 10842 safe_free(rs); 10843 } 10844 } 10845 10846 int tls_tests(void) 10847 { 10848 if (have_tls_listeners == 0) 10849 { 10850 config_error("Your server is not listening on any TLS ports."); 10851 config_status("Add this to your unrealircd.conf: listen { ip %s; port 6697; options { tls; }; };", 10852 port_6667_ip ? port_6667_ip : "*"); 10853 config_status("See https://www.unrealircd.org/docs/FAQ#no-tls-ports"); 10854 return 0; 10855 } 10856 10857 return 1; 10858 } 10859 10860 /** Check if the user attempts to unload (eg: by commenting out) a module 10861 * that is currently loaded and is tagged as MOD_OPT_PERM_RELOADABLE 10862 * (in other words: a module that allows re-loading but not un-loading) 10863 */ 10864 int reloadable_perm_module_unloaded(void) 10865 { 10866 Module *m, *m2; 10867 extern Module *Modules; 10868 int ret = 0; 10869 10870 for (m = Modules; m; m = m->next) 10871 { 10872 if ((m->options & MOD_OPT_PERM_RELOADABLE) && (m->flags & MODFLAG_LOADED)) 10873 { 10874 /* For each module w/MOD_OPT_PERM_RELOADABLE that is currently fully loaded... */ 10875 int found = 0; 10876 for (m2 = Modules; m2; m2 = m2->next) 10877 { 10878 if ((m != m2) && !strcmp(m->header->name, m2->header->name)) 10879 found = 1; 10880 } 10881 if (!found) 10882 { 10883 config_error("Attempt to unload module '%s' is not permitted. Module is permanent and reloadable only.", m->header->name); 10884 ret = 1; 10885 /* we don't return straight away so the user gets to see all errors and not just one */ 10886 } 10887 } 10888 } 10889 10890 return ret; 10891 } 10892 10893 const char *link_generator_spkifp(TLSOptions *tlsoptions) 10894 { 10895 SSL_CTX *ctx; 10896 SSL *ssl; 10897 X509 *cert; 10898 10899 ctx = init_ctx(tlsoptions, 1); 10900 if (!ctx) 10901 exit(1); 10902 ssl = SSL_new(ctx); 10903 if (!ssl) 10904 exit(1); 10905 cert = SSL_get_certificate(ssl); 10906 return spki_fingerprint_ex(cert); 10907 } 10908 10909 void link_generator(void) 10910 { 10911 ConfigItem_listen *lstn; 10912 TLSOptions *tlsopt = iConf.tls_options; /* never null */ 10913 int port = 0; 10914 char *ip = NULL; 10915 const char *spkifp; 10916 10917 for (lstn = conf_listen; lstn; lstn = lstn->next) 10918 { 10919 if ((lstn->options & LISTENER_SERVERSONLY) && 10920 (lstn->options & LISTENER_TLS)) 10921 { 10922 if (lstn->tls_options) 10923 tlsopt = lstn->tls_options; 10924 port = lstn->port; 10925 if (strcmp(lstn->ip, "*")) 10926 ip = lstn->ip; 10927 /* else NULL */ 10928 break; 10929 } 10930 } 10931 10932 if (!port) 10933 { 10934 printf("You don't have any listen { } blocks that are serversonly (and have tls enabled).\n"); 10935 printf("It is recommended to have at least one. Add this to your configuration file:\n"); 10936 printf("listen { ip *; port 6900; options { tls; serversonly; }; };\n"); 10937 exit(1); 10938 } 10939 10940 spkifp = link_generator_spkifp(tlsopt); 10941 if (!spkifp) 10942 { 10943 printf("Could not calculate spkifp. Maybe you have uncommon TLS options set? Odd...\n"); 10944 exit(1); 10945 } 10946 10947 printf("\n"); 10948 printf("Add the following link block to the unrealircd.conf on the OTHER side of the link\n"); 10949 printf("(so NOT in the unrealircd.conf on THIS machine). Here it is, just copy-paste:\n"); 10950 printf("################################################################################\n"); 10951 printf("link %s {\n" 10952 " incoming {\n" 10953 " mask *;\n" 10954 " }\n" 10955 " outgoing {\n" 10956 " hostname %s;\n" 10957 " port %d;\n" 10958 " options { tls; autoconnect; }\n" 10959 " }\n" 10960 " password \"%s\" { spkifp; }\n" 10961 " class servers;\n" 10962 "}\n", 10963 conf_me->name, 10964 ip ? ip : conf_me->name, 10965 port, 10966 spkifp); 10967 printf("################################################################################\n"); 10968 exit(0); 10969 }