aboutsummaryrefslogtreecommitdiff
path: root/ircd/chkconf.c
diff options
context:
space:
mode:
Diffstat (limited to 'ircd/chkconf.c')
-rw-r--r--ircd/chkconf.c745
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);
+}