/* * IRC - Internet Relay Chat, ircd/chkconf.c * Copyright (C) 1993 Darren Reed * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 1, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Id: chkconf.c 440 2002-12-29 02:55:36Z pierreg0 $ */ #include "config.h" #include "s_conf.h" #include "client.h" #include "class.h" #include "fileio.h" #include "ircd.h" #include "ircd_alloc.h" #include "ircd_chattr.h" #include "ircd_string.h" #include "sys.h" #include #include #include #include #include #include #include #include #include #include /* * stuff that isn't used by s_conf.c anymore */ #define CONF_ME 0x0040 #define CONF_KILL 0x0080 #define CONF_ADMIN 0x0100 #define CONF_CLASS 0x0400 #define CONF_LISTEN_PORT 0x2000 #define CONF_IPKILL 0x00010000 #define CONF_CRULEALL 0x00200000 #define CONF_CRULEAUTO 0x00400000 #define CONF_TLINES 0x00800000 #define CONF_KLINE (CONF_KILL | CONF_IPKILL) #define CONF_CRULE (CONF_CRULEALL | CONF_CRULEAUTO) /* DEFAULTMAXSENDQLENGTH went into the features subsystem... */ #define DEFAULTMAXSENDQLENGTH 40000 /* * For the connect rule patch.. these really should be in a header, * but i see h.h isn't included for some reason.. so they're here. */ struct CRuleNode; struct CRuleNode* crule_parse(const char* rule); void crule_free(struct CRuleNode** elem); static void new_class(int cn); static char confchar(unsigned int status); static char *getfield(char *newline); static int validate(struct ConfItem *top); static struct ConfItem *chk_initconf(void); static struct ConnectionClass *get_class(int cn, int ism); static int numclasses = 0, *classarr = (int *)NULL, debugflag = 0; static char *chk_configfile = ""; static char nullfield[] = ""; static char maxsendq[12]; /* A few dummy variables and functions needed to link with runmalloc.o */ time_t CurrentTime; struct Client me; void debug(int level, const char *form, ...) { va_list vl; va_start(vl, form); if (debugflag > 8) { vfprintf(stderr, form, vl); fprintf(stderr, "\n"); } va_end(vl); } void sendto_one(struct Client *to, char *pattern, ...) { } char *rpl_str(int numeric) { return ""; } int main(int argc, char *argv[]) { const char *dpath = "./"; chk_configfile = "ircd.conf"; while (argc > 1) { if (*argv[1] == '-') { switch (argv[1][1]) { case 'd': if (argc > 2) { dpath = argv[2]; --argc; ++argv; } else { fprintf(stderr, "-d: Missing path\n"); exit(-1); } break; case 'x': debugflag = 1; if (isdigit(argv[1][2])) debugflag = atoi(&argv[1][2]); break; default: fprintf(stderr, "Ignoring unknown option -%c\n", argv[1][1]); } } else chk_configfile = argv[1]; --argc; ++argv; } if (chdir(dpath)) { fprintf(stderr, "chdir(\"%s\") : %s\n", dpath, (strerror(errno)) ? strerror(errno) : "Unknown error"); exit(-1); } else if (debugflag > 1) fprintf(stderr, "chdir(\"%s\") : Success", dpath); new_class(0); return validate(chk_initconf()); } /* * chk_initconf() * * Read configuration file. * * Returns -1, if file cannot be opened * 0, if file opened. */ static struct ConfItem *chk_initconf(void) { FBFILE *file; char line[512]; char *tmp; struct CRuleNode* crule; int ccount = 0, flags = 0; struct ConfItem *aconf = NULL, *ctop = NULL; fprintf(stderr, "chk_initconf(): ircd.conf = %s\n", chk_configfile); if (NULL == (file = fbopen(chk_configfile, "r"))) { perror("open"); return NULL; } while (fbgets(line, sizeof(line) - 1, file)) { if (aconf) { if (aconf->host) MyFree(aconf->host); if (aconf->passwd) MyFree(aconf->passwd); if (aconf->name) MyFree(aconf->name); } else { aconf = (struct ConfItem*) MyMalloc(sizeof(struct ConfItem)); assert(0 != aconf); } aconf->host = NULL; aconf->passwd = NULL; aconf->name = NULL; aconf->conn_class = NULL; aconf->dns_pending = 0; if ((tmp = strchr(line, '\n'))) *tmp = 0; /* * Do quoting of characters and # detection. */ for (tmp = line; *tmp; ++tmp) { if (*tmp == '\\') { switch (*(tmp + 1)) { case 'n': *tmp = '\n'; break; case 'r': *tmp = '\r'; break; case 't': *tmp = '\t'; break; case '0': *tmp = '\0'; break; default: *tmp = *(tmp + 1); break; } if ('\0' == *(tmp + 1)) break; else strcpy(tmp + 1, tmp + 2); } else if (*tmp == '#') *tmp = '\0'; } if (!*line || *line == '#' || *line == '\n' || *line == ' ' || *line == '\t') continue; if (line[1] != ':') { fprintf(stderr, "ERROR: Bad config line (%s)\n", line); continue; } if (debugflag) printf("\n%s\n", line); fflush(stdout); tmp = getfield(line); if (!tmp) { fprintf(stderr, "\tERROR: no fields found\n"); continue; } aconf->status = CONF_ILLEGAL; switch (*tmp) { case 'A': /* Name, e-mail address of administrator */ case 'a': /* of this server. */ aconf->status = CONF_ADMIN; break; case 'C': /* Server where I should try to connect */ case 'c': /* in case of lp failures */ ccount++; aconf->status = CONF_SERVER; break; /* Connect rule */ case 'D': aconf->status = CONF_CRULEALL; break; /* Connect rule - autos only */ case 'd': aconf->status = CONF_CRULEAUTO; break; case 'H': /* Hub server line */ case 'h': aconf->status = CONF_HUB; break; case 'I': /* Just plain normal irc client trying */ case 'i': /* to connect me */ aconf->status = CONF_CLIENT; break; case 'K': /* Kill user line on irc.conf */ aconf->status = CONF_KILL; break; case 'k': /* Kill user line based on IP in ircd.conf */ aconf->status = CONF_IPKILL; break; /* Operator. Line should contain at least */ /* password and host where connection is */ case 'L': /* guaranteed leaf server */ case 'l': aconf->status = CONF_LEAF; break; /* Me. Host field is name used for this host */ /* and port number is the number of the port */ case 'M': case 'm': aconf->status = CONF_ME; break; case 'O': aconf->status = CONF_OPERATOR; break; /* Local Operator, (limited privs --SRB) */ case 'o': aconf->status = CONF_LOCOP; break; case 'P': /* listen port line */ case 'p': aconf->status = CONF_LISTEN_PORT; break; case 'T': case 't': aconf->status = CONF_TLINES; break; case 'U': case 'u': aconf->status = CONF_UWORLD; break; case 'Y': case 'y': aconf->status = CONF_CLASS; break; default: fprintf(stderr, "\tERROR: unknown conf line letter (%c)\n", *tmp); break; } if (IsIllegal(aconf)) continue; for (;;) /* Fake loop, that I can use break here --msa */ { if ((tmp = getfield(NULL)) == NULL) break; DupString(aconf->host, tmp); if ((tmp = getfield(NULL)) == NULL) break; DupString(aconf->passwd, tmp); if ((tmp = getfield(NULL)) == NULL) break; DupString(aconf->name, tmp); if ((tmp = getfield(NULL)) == NULL) break; aconf->port = atoi(tmp); if ((tmp = getfield(NULL)) == NULL) break; if (!(aconf->status & (CONF_CLASS | CONF_ME))) { aconf->conn_class = get_class(atoi(tmp), 0); break; } if (aconf->status & CONF_ME) aconf->conn_class = get_class(atoi(tmp), 1); break; } if (!aconf->conn_class && (aconf->status & (CONF_SERVER | CONF_ME | CONF_OPS | CONF_CLIENT))) { fprintf(stderr, "\tWARNING: No class. Default 0\n"); aconf->conn_class = get_class(0, 0); } /* * If conf line is a class definition, create a class entry * for it and make the conf_line illegal and delete it. */ if (aconf->status & CONF_CLASS) { if (!aconf->host) { fprintf(stderr, "\tERROR: no class #\n"); continue; } if (!tmp) { fprintf(stderr, "\tWARNING: missing sendq field\n"); fprintf(stderr, "\t\t default: %d\n", DEFAULTMAXSENDQLENGTH); sprintf(maxsendq, "%d", DEFAULTMAXSENDQLENGTH); } else sprintf(maxsendq, "%d", atoi(tmp)); new_class(atoi(aconf->host)); aconf->conn_class = get_class(atoi(aconf->host), 0); goto print_confline; } if (aconf->status & CONF_LISTEN_PORT) { if (!aconf->host) fprintf(stderr, "\tERROR: %s\n", "null host field in P-line"); else if (strchr(aconf->host, '/')) fprintf(stderr, "\t%s\n", "WARNING: / present in P-line " "for non-UNIXPORT configuration"); aconf->conn_class = get_class(0, 0); goto print_confline; } if (aconf->status & CONF_SERVER && (!aconf->host || strchr(aconf->host, '*') || strchr(aconf->host, '?'))) { fprintf(stderr, "\tERROR: bad host field\n"); continue; } if (aconf->status & CONF_SERVER && BadPtr(aconf->passwd)) { fprintf(stderr, "\tERROR: empty/no password field\n"); continue; } if (aconf->status & CONF_SERVER && !aconf->name) { fprintf(stderr, "\tERROR: bad name field\n"); continue; } if (aconf->status & (CONF_OPS)) if (!strchr(aconf->host, '@')) { char *newhost; int len = 3; /* *@\0 = 3 */ len += strlen(aconf->host); newhost = (char *)MyMalloc(len); sprintf(newhost, "*@%s", aconf->host); MyFree(aconf->host); aconf->host = newhost; } /* parse the connect rules to detect errors, but free * any allocated storage immediately -- we're just looking * for errors.. */ if (aconf->status & CONF_CRULE) if ((crule = crule_parse(aconf->name)) != NULL) crule_free(&crule); if (!aconf->conn_class) aconf->conn_class = get_class(0, 0); sprintf(maxsendq, "%d", ConfClass(aconf)); if ((aconf->status & CONF_ADMIN) && (!aconf->name || !aconf->passwd || !aconf->host)) fprintf(stderr, "ERROR: Your A: line must have 4 fields!\n"); if (!aconf->name) DupString(aconf->name, nullfield); if (!aconf->passwd) DupString(aconf->passwd, nullfield); if (!aconf->host) DupString(aconf->host, nullfield); if (aconf->status & (CONF_ME | CONF_ADMIN)) { if (flags & aconf->status) fprintf(stderr, "ERROR: multiple %c-lines\n", ToUpper(confchar(aconf->status))); else flags |= aconf->status; } print_confline: if (debugflag > 8) printf("(%d) (%s) (%s) (%s) (%u) (%s)\n", aconf->status, aconf->host, aconf->passwd, aconf->name, aconf->port, maxsendq); fflush(stdout); if (aconf->status & (CONF_SERVER | CONF_HUB | CONF_LEAF)) { aconf->next = ctop; ctop = aconf; aconf = NULL; } } fbclose(file); return ctop; } static struct ConnectionClass *get_class(int cn, int ism) { static struct ConnectionClass cls; if (ism == 1) { cls.cc_class = (unsigned int)-1; if ((cn >= 1) && (cn <= 64)) cls.cc_class = cn; else fprintf(stderr, "\tWARNING: server numeric %d is not 1-64\n", cn); } else { int i = numclasses - 1; cls.cc_class = (unsigned int)-1; for (; i >= 0; i--) if (classarr[i] == cn) { cls.cc_class = cn; break; } if (i == -1) fprintf(stderr, "\tWARNING: class %d not found\n", cn); } return &cls; } static void new_class(int cn) { numclasses++; if (classarr) classarr = (int *)MyRealloc(classarr, sizeof(int) * numclasses); else classarr = (int *)MyMalloc(sizeof(int)); classarr[numclasses - 1] = cn; } /* * field breakup for ircd.conf file. */ static char *getfield(char *newline) { static char *line = NULL; char *end, *field; if (newline) line = newline; if (line == NULL) return (NULL); field = line; if ((end = strchr(line, ':')) == NULL) { line = NULL; if ((end = strchr(field, '\n')) == NULL) end = field + strlen(field); } else line = end + 1; *end = '\0'; return (field); } static int validate(struct ConfItem *top) { struct ConfItem *aconf, *bconf; unsigned int otype, valid = 0; if (!top) return 0; for (aconf = top; aconf; aconf = aconf->next) { if (aconf->status & CONF_MATCH) continue; if (aconf->status & CONF_SERVER) { otype = CONF_SERVER; for (bconf = top; bconf; bconf = bconf->next) { if (bconf == aconf || !(bconf->status & otype)) continue; if (bconf->conn_class == aconf->conn_class && 0 == ircd_strcmp(bconf->name, aconf->name) && 0 == ircd_strcmp(bconf->host, aconf->host)) { aconf->status |= CONF_MATCH; bconf->status |= CONF_MATCH; break; } } } else for (bconf = top; bconf; bconf = bconf->next) { if ((bconf == aconf) || !(bconf->status & CONF_SERVER)) continue; if (0 == ircd_strcmp(bconf->name, aconf->name)) { aconf->status |= CONF_MATCH; break; } } } fprintf(stderr, "\n"); for (aconf = top; aconf; aconf = aconf->next) { if (aconf->status & CONF_MATCH) valid++; else if ('N' != confchar(aconf->status)) fprintf(stderr, "Unmatched %c:%s:%s:%s\n", confchar(aconf->status), aconf->host, aconf->passwd, aconf->name); } return valid ? 0 : -1; } static char confchar(unsigned int status) { static char letrs[] = "ICNoOMKARYLPH"; char *s = letrs; status &= ~(CONF_MATCH | CONF_ILLEGAL); for (; *s; s++, status >>= 1) if (status & 1) return *s; return '-'; }