diff options
Diffstat (limited to 'ircd/chkconf.c')
-rw-r--r-- | ircd/chkconf.c | 745 |
1 files changed, 745 insertions, 0 deletions
diff --git a/ircd/chkconf.c b/ircd/chkconf.c new file mode 100644 index 0000000..7c0032d --- /dev/null +++ b/ircd/chkconf.c @@ -0,0 +1,745 @@ +/************************************************************************ + * 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. + */ + +#ifndef lint +static char rcsid[] = "@(#)$Id: chkconf.c,v 1.13 1999/03/11 23:40:12 kalt Exp $"; +#endif + +#include "os.h" +#include "s_defines.h" +#define CHKCONF_C +#include "match_ext.h" +#undef CHKCONF_C + +#define MyMalloc(x) malloc(x) +/*#define MyFree(x) free(x)*/ + +static void new_class(); +static char *getfield(), confchar (); +static int openconf(), validate __P((aConfItem *)); +static int dgets __P((int, char *, int)); +static aClass *get_class(); +static aConfItem *initconf(); + +static int numclasses = 0, *classarr = (int *)NULL, debugflag = 0; +static char *configfile = IRCDCONF_PATH; +static char nullfield[] = ""; +static char maxsendq[12]; + +#define SHOWSTR(x) ((x) ? (x) : "*") + +int main(argc, argv) +int argc; +char *argv[]; +{ + if (argc > 1 && !strncmp(argv[1], "-h", 2)) { + (void)fprintf(stderr, "Usage: %s [-h] [-d[#]] [%s]\n", + argv[0], IRCDCONF_PATH); + exit(0); + } + new_class(0); + + if (argc > 1 && !strncmp(argv[1], "-d", 2)) + { + debugflag = 1; + if (argv[1][2]) + debugflag = atoi(argv[1]+2); + argc--, argv++; + } + if (argc > 1) + configfile = argv[1]; + return validate(initconf()); +} + +/* + * openconf + * + * returns -1 on any error or else the fd opened from which to read the + * configuration file from. This may either be th4 file direct or one end + * of a pipe from m4. + */ +static int openconf() +{ +#ifdef M4_PREPROC + int pi[2]; + + /* ircd.m4 with full path now! Kratz */ + if (access(IRCDM4_PATH, R_OK) == -1) + { + (void)fprintf(stderr, "%s missing.\n", IRCDM4_PATH); + return -1; + } + if (pipe(pi) == -1) + return -1; + switch(fork()) + { + case -1 : + return -1; + case 0 : + (void)close(pi[0]); + if (pi[1] != 1) + { + (void)dup2(pi[1], 1); + (void)close(pi[1]); + } + (void)dup2(1,2); + /* + * m4 maybe anywhere, use execvp to find it. Any error + * goes out with report_error. Could be dangerous, + * two servers running with the same fd's >:-) -avalon + */ + (void)execlp("m4", "m4", IRCDM4_PATH, configfile, 0); + perror("m4"); + exit(-1); + default : + (void)close(pi[1]); + return pi[0]; + } +#else + return open(configfile, O_RDONLY); +#endif +} + +/* +** initconf() +** Read configuration file. +** +** returns -1, if file cannot be opened +** 0, if file opened +*/ + +static aConfItem *initconf(opt) +int opt; +{ + int fd; + char line[512], *tmp, c[80], *s; + int ccount = 0, ncount = 0, dh, flags = 0; + aConfItem *aconf = NULL, *ctop = NULL; + + (void)fprintf(stderr, "initconf(): ircd.conf = %s\n", configfile); + if ((fd = openconf()) == -1) + { +#ifdef M4_PREPROC + (void)wait(0); +#endif + return NULL; + } + + (void)dgets(-1, NULL, 0); /* make sure buffer is at empty pos */ + while ((dh = dgets(fd, line, sizeof(line) - 1)) > 0) + { + if (aconf) + { + if (aconf->host) + (void)free(aconf->host); + if (aconf->passwd) + (void)free(aconf->passwd); + if (aconf->name) + (void)free(aconf->name); + } + else + aconf = (aConfItem *)malloc(sizeof(*aconf)); + aconf->host = (char *)NULL; + aconf->passwd = (char *)NULL; + aconf->name = (char *)NULL; + aconf->class = (aClass *)NULL; + if ((tmp = (char *)index(line, '\n'))) + *tmp = 0; + else while(dgets(fd, c, sizeof(c) - 1)) + if ((tmp = (char *)index(c, '\n'))) + { + *tmp = 0; + break; + } + /* + * 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 (!*(tmp+1)) + break; + else + for (s = tmp; (*s = *(s+1)); s++) + ; + tmp++; + } + else if (*tmp == '#') + *tmp = '\0'; + } + if (!*line || *line == '#' || *line == '\n' || + *line == ' ' || *line == '\t') + continue; + + if (line[1] != IRCDCONF_DELIMITER) + { + (void)fprintf(stderr, "ERROR: Bad config line (%s)\n", + line); + if (IRCDCONF_DELIMITER != ':') + (void)fprintf(stderr, + "\tWrong delimiter? (should be %c)\n", + IRCDCONF_DELIMITER); + continue; + } + + if (debugflag) + (void)printf("\n%s\n",line); + (void)fflush(stdout); + + tmp = getfield(line); + if (!tmp) + { + (void)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 'B': /* bounce line */ + case 'b': + aconf->status = CONF_BOUNCE; + break; + case 'C': /* Server where I should try to connect */ + case 'c': /* in case of lp failures */ + ccount++; + aconf->status = CONF_CONNECT_SERVER; + break; + case 'D': /* auto connect restrictions */ + case 'd': + aconf->status = CONF_DENY; + 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 */ + case 'k': + aconf->status = CONF_KILL; + 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 'N': /* Server where I should NOT try to */ + case 'n': /* connect in case of lp failures */ + /* but which tries to connect ME */ + ++ncount; + aconf->status = CONF_NOCONNECT_SERVER; + 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 'Q': /* a server that you don't want in your */ + case 'q': /* network. USE WITH CAUTION! */ + aconf->status = CONF_QUARANTINED_SERVER; + break; +#ifdef R_LINES + case 'R': /* extended K line */ + case 'r': /* Offers more options of how to restrict */ + aconf->status = CONF_RESTRICT; + break; +#endif + case 'S': /* Service. Same semantics as */ + case 's': /* CONF_OPERATOR */ + aconf->status = CONF_SERVICE; + break; + case 'U': /* Uphost, ie. host where client reading */ + case 'u': /* this should connect. */ + /* This is for client only, I must ignore this */ + /* ...U-line should be removed... --msa */ + break; + case 'V': + aconf->status = CONF_VER; + break; + case 'Y': + case 'y': + aconf->status = CONF_CLASS; + break; + default: + (void)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)) + aconf->class = get_class(atoi(tmp)); + break; + } + if (!aconf->class && (aconf->status & (CONF_CONNECT_SERVER| + CONF_NOCONNECT_SERVER|CONF_OPS|CONF_CLIENT))) + { + (void)fprintf(stderr, + "\tWARNING: No class. Default 0\n"); + aconf->class = get_class(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) + { + (void)fprintf(stderr,"\tERROR: no class #\n"); + continue; + } + if (!tmp) + { + (void)fprintf(stderr, + "\tWARNING: missing sendq field\n"); + (void)fprintf(stderr, "\t\t default: %d\n", + QUEUELEN); + (void)sprintf(maxsendq, "%d", QUEUELEN); + } + else + (void)sprintf(maxsendq, "%d", atoi(tmp)); + new_class(atoi(aconf->host)); + aconf->class = get_class(atoi(aconf->host)); + goto print_confline; + } + + if (aconf->status & CONF_LISTEN_PORT) + { +#ifdef UNIXPORT + struct stat sb; + + if (!aconf->host) + (void)fprintf(stderr, "\tERROR: %s\n", + "null host field in P-line"); + else if (index(aconf->host, '/')) + { + if (stat(aconf->host, &sb) == -1) + { + (void)fprintf(stderr, "\tERROR: (%s) ", + aconf->host); + perror("stat"); + } + else if ((sb.st_mode & S_IFMT) != S_IFDIR) + (void)fprintf(stderr, + "\tERROR: %s not directory\n", + aconf->host); + } +#else + if (!aconf->host) + (void)fprintf(stderr, "\tERROR: %s\n", + "null host field in P-line"); + else if (index(aconf->host, '/')) + (void)fprintf(stderr, "\t%s %s\n", + "WARNING: / present in P-line", + "for non-UNIXPORT configuration"); +#endif + aconf->class = get_class(0); + goto print_confline; + } + + if (aconf->status & CONF_SERVER_MASK && + (!aconf->host || index(aconf->host, '*') || + index(aconf->host, '?'))) + { + (void)fprintf(stderr, "\tERROR: bad host field\n"); + continue; + } + + if (aconf->status & CONF_SERVER_MASK && BadPtr(aconf->passwd)) + { + (void)fprintf(stderr, + "\tERROR: empty/no password field\n"); + continue; + } + + if (aconf->status & CONF_SERVER_MASK && !aconf->name) + { + (void)fprintf(stderr, "\tERROR: bad name field\n"); + continue; + } + + if (aconf->status & (CONF_SERVER_MASK|CONF_OPS)) + if (!index(aconf->host, '@')) + { + char *newhost; + int len = 3; /* *@\0 = 3 */ + + len += strlen(aconf->host); + newhost = (char *)MyMalloc(len); + (void)sprintf(newhost, "*@%s", aconf->host); + (void)free(aconf->host); + aconf->host = newhost; + } + + if (!aconf->class) + aconf->class = get_class(0); + (void)sprintf(maxsendq, "%d", aconf->class->class); + + if (!aconf->name) + aconf->name = nullfield; + if (!aconf->passwd) + aconf->passwd = nullfield; + if (!aconf->host) + aconf->host = nullfield; + if (aconf->status & (CONF_ME|CONF_ADMIN)) + { + if (flags & aconf->status) + (void)fprintf(stderr, + "ERROR: multiple %c-lines\n", + toupper(confchar(aconf->status))); + else + flags |= aconf->status; + } + + if (aconf->status & CONF_VER) + { + if (*aconf->host && !index(aconf->host, '/')) + (void)fprintf(stderr, + "\tWARNING: No / in V line."); + else if (*aconf->passwd && !index(aconf->passwd, '/')) + (void)fprintf(stderr, + "\tWARNING: No / in V line."); + } +print_confline: + if (debugflag > 8) + (void)printf("(%d) (%s) (%s) (%s) (%d) (%s)\n", + aconf->status, aconf->host, aconf->passwd, + aconf->name, aconf->port, maxsendq); + (void)fflush(stdout); + if (aconf->status & (CONF_SERVER_MASK|CONF_HUB|CONF_LEAF)) + { + aconf->next = ctop; + ctop = aconf; + aconf = NULL; + } + } + (void)close(fd); +#ifdef M4_PREPROC + (void)wait(0); +#endif + return ctop; +} + +static aClass *get_class(cn) +int cn; +{ + static aClass cls; + int i = numclasses - 1; + + cls.class = -1; + for (; i >= 0; i--) + if (classarr[i] == cn) + { + cls.class = cn; + break; + } + if (i == -1) + (void)fprintf(stderr,"\tWARNING: class %d not found\n", cn); + return &cls; +} + +static void new_class(cn) +int cn; +{ + numclasses++; + if (classarr) + classarr = (int *)realloc(classarr, sizeof(int) * numclasses); + else + classarr = (int *)malloc(sizeof(int)); + classarr[numclasses-1] = cn; +} + +/* + * field breakup for ircd.conf file. + */ +static char *getfield(irc_newline) +char *irc_newline; +{ + static char *line = NULL; + char *end, *field; + + if (irc_newline) + line = irc_newline; + if (line == NULL) + return(NULL); + + field = line; + if ((end = (char *)index(line, IRCDCONF_DELIMITER)) == NULL) + { + line = NULL; + if ((end = (char *)index(field,'\n')) == NULL) + end = field + strlen(field); + } + else + line = end + 1; + *end = '\0'; + return(field); +} + + +/* +** read a string terminated by \r or \n in from a fd +** +** Created: Sat Dec 12 06:29:58 EST 1992 by avalon +** Returns: +** 0 - EOF +** -1 - error on read +** >0 - number of bytes returned (<=num) +** After opening a fd, it is necessary to init dgets() by calling it as +** dgets(x,y,0); +** to mark the buffer as being empty. +*/ +static int dgets(fd, buf, num) +int fd, num; +char *buf; +{ + static char dgbuf[8192]; + static char *head = dgbuf, *tail = dgbuf; + register char *s, *t; + register int n, nr; + + /* + ** Sanity checks. + */ + if (head == tail) + *head = '\0'; + if (!num) + { + head = tail = dgbuf; + *head = '\0'; + return 0; + } + if (num > sizeof(dgbuf) - 1) + num = sizeof(dgbuf) - 1; +dgetsagain: + if (head > dgbuf) + { + for (nr = tail - head, s = head, t = dgbuf; nr > 0; nr--) + *t++ = *s++; + tail = t; + head = dgbuf; + } + /* + ** check input buffer for EOL and if present return string. + */ + if (head < tail && + ((s = index(head, '\n')) || (s = index(head, '\r'))) && s < tail) + { + n = MIN(s - head + 1, num); /* at least 1 byte */ +dgetsreturnbuf: + bcopy(head, buf, n); + head += n; + if (head == tail) + head = tail = dgbuf; + return n; + } + + if (tail - head >= num) /* dgets buf is big enough */ + { + n = num; + goto dgetsreturnbuf; + } + + n = sizeof(dgbuf) - (tail - dgbuf) - 1; + nr = read(fd, tail, n); + if (nr == -1) + { + head = tail = dgbuf; + return -1; + } + if (!nr) + { + if (head < tail) + { + n = MIN(head - tail, num); + goto dgetsreturnbuf; + } + head = tail = dgbuf; + return 0; + } + tail += nr; + *tail = '\0'; + for (t = head; (s = index(t, '\n')); ) + { + if ((s > head) && (s > dgbuf)) + { + t = s-1; + for (nr = 0; *t == '\\'; nr++) + t--; + if (nr & 1) + { + t = s+1; + s--; + nr = tail - t; + while (nr--) + *s++ = *t++; + tail -= 2; + *tail = '\0'; + } + else + s++; + } + else + s++; + t = s; + } + *tail = '\0'; + goto dgetsagain; +} + + +static int validate(top) +aConfItem *top; +{ + Reg aConfItem *aconf, *bconf; + u_int otype = 0, valid = 0; + + if (!top) + return 0; + + for (aconf = top; aconf; aconf = aconf->next) + { + if (aconf->status & CONF_MATCH) + continue; + + if (aconf->status & CONF_SERVER_MASK) + { + if (aconf->status & CONF_CONNECT_SERVER) + otype = CONF_NOCONNECT_SERVER; + else if (aconf->status & CONF_NOCONNECT_SERVER) + otype = CONF_CONNECT_SERVER; + + for (bconf = top; bconf; bconf = bconf->next) + { + if (bconf == aconf || !(bconf->status & otype)) + continue; + if (bconf->class == aconf->class && + !mycmp(bconf->name, aconf->name) && + !mycmp(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_MASK)) + continue; + if (!mycmp(bconf->name, aconf->name)) + { + aconf->status |= CONF_MATCH; + break; + } + } + } + + (void) fprintf(stderr, "\n"); + for (aconf = top; aconf; aconf = aconf->next) + if (aconf->status & CONF_MATCH) + valid++; + else + (void)fprintf(stderr, "Unmatched %c:%s:%s:%s\n", + confchar(aconf->status), aconf->host, + SHOWSTR(aconf->passwd), aconf->name); + return valid ? 0 : -1; +} + +static char confchar(status) +u_int status; +{ + static char letrs[] = "QIiCcNoOMKARYSLPHV"; + char *s = letrs; + + status &= ~(CONF_MATCH|CONF_ILLEGAL); + + for (; *s; s++, status >>= 1) + if (status & 1) + return *s; + return '-'; +} + +void outofmemory() +{ + (void)write(2, "Out of memory\n", 14); + exit(-1); +} |