2 * IRC - Internet Relay Chat, ircd/chkconf.c
3 * Copyright (C) 1993 Darren Reed
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 1, or (at your option)
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
25 #include <sys/socket.h>
32 #include <arpa/inet.h>
47 * For the connect rule patch.. these really should be in a header,
48 * but i see h.h isn't included for some reason.. so they're here.
50 char *crule_parse(char *rule);
51 void crule_free(char **elem);
53 static void new_class(int cn);
54 static char confchar(unsigned int status);
55 static char *getfield(char *newline);
56 static int validate(aConfItem *top);
57 static aConfItem *chk_initconf(void);
58 static aConfClass *get_class(int cn, int ism);
60 static int numclasses = 0, *classarr = (int *)NULL, debugflag = 0;
61 static char *chk_configfile = CPATH;
62 static char nullfield[] = "";
63 static char maxsendq[12];
65 /* A few dummy variables and functions needed to link with runmalloc.o */
71 void debug(int UNUSED(level), const char *form, ...)
77 vfprintf(stderr, form, vl);
78 fprintf(stderr, "\n");
82 void sendto_one(aClient *UNUSED(to), char *UNUSED(pattern), ...)
85 char *rpl_str(int UNUSED(numeric))
90 int main(int argc, char *argv[])
92 const char *dpath = DPATH;
93 chk_configfile = "ircd.conf";
110 fprintf(stderr, "-d: Missing path\n");
116 if (isdigit(argv[1][2]))
117 debugflag = atoi(&argv[1][2]);
120 fprintf(stderr, "Ignoring unknown option -%c\n", argv[1][1]);
124 chk_configfile = argv[1];
131 fprintf(stderr, "chdir(\"%s\") : %s\n", dpath, strerror(errno));
134 else if (debugflag > 1)
135 fprintf(stderr, "chdir(\"%s\") : Success", dpath);
139 return validate(chk_initconf());
145 * Read configuration file.
147 * Returns -1, if file cannot be opened
150 static aConfItem *chk_initconf(void)
153 char line[512], *tmp, *s, *crule;
154 int ccount = 0, ncount = 0, flags = 0;
155 aConfItem *aconf = NULL, *ctop = NULL;
157 fprintf(stderr, "chk_initconf(): ircd.conf = %s\n", chk_configfile);
158 if (NULL == (file = fbopen(chk_configfile, "r")))
164 while (fbgets(line, sizeof(line) - 1, file))
169 RunFree(aconf->host);
171 RunFree(aconf->passwd);
173 RunFree(aconf->name);
176 aconf = (aConfItem *)RunMalloc(sizeof(*aconf));
177 aconf->host = (char *)NULL;
178 aconf->passwd = (char *)NULL;
179 aconf->name = (char *)NULL;
180 aconf->confClass = (aConfClass *) NULL;
181 if ((tmp = strchr(line, '\n')))
184 * Do quoting of characters and # detection.
186 for (tmp = line; *tmp; tmp++)
211 for (s = tmp; (*s = *++s);)
215 else if (*tmp == '#')
218 if (!*line || *line == '#' || *line == '\n' ||
219 *line == ' ' || *line == '\t')
224 fprintf(stderr, "ERROR: Bad config line (%s)\n", line);
229 printf("\n%s\n", line);
232 tmp = getfield(line);
235 fprintf(stderr, "\tERROR: no fields found\n");
239 aconf->status = CONF_ILLEGAL;
243 case 'A': /* Name, e-mail address of administrator */
244 case 'a': /* of this server. */
245 aconf->status = CONF_ADMIN;
247 case 'C': /* Server where I should try to connect */
248 case 'c': /* in case of lp failures */
250 aconf->status = CONF_CONNECT_SERVER;
254 aconf->status = CONF_CRULEALL;
256 /* Connect rule - autos only */
258 aconf->status = CONF_CRULEAUTO;
260 case 'H': /* Hub server line */
262 aconf->status = CONF_HUB;
264 case 'I': /* Just plain normal irc client trying */
265 case 'i': /* to connect me */
266 aconf->status = CONF_CLIENT;
268 case 'K': /* Kill user line on irc.conf */
269 aconf->status = CONF_KILL;
271 case 'k': /* Kill user line based on IP in ircd.conf */
272 aconf->status = CONF_IPKILL;
274 /* Operator. Line should contain at least */
275 /* password and host where connection is */
276 case 'L': /* guaranteed leaf server */
278 aconf->status = CONF_LEAF;
280 /* Me. Host field is name used for this host */
281 /* and port number is the number of the port */
284 aconf->status = CONF_ME;
286 case 'N': /* Server where I should NOT try to */
287 case 'n': /* connect in case of lp failures */
288 /* but which tries to connect ME */
290 aconf->status = CONF_NOCONNECT_SERVER;
293 aconf->status = CONF_OPERATOR;
295 /* Local Operator, (limited privs --SRB) */
297 aconf->status = CONF_LOCOP;
299 case 'P': /* listen port line */
301 aconf->status = CONF_LISTEN_PORT;
304 case 'R': /* extended K line */
305 case 'r': /* Offers more options of how to restrict */
306 aconf->status = CONF_RESTRICT;
311 aconf->status = CONF_TLINES;
315 aconf->status = CONF_UWORLD;
319 aconf->status = CONF_CLASS;
322 fprintf(stderr, "\tERROR: unknown conf line letter (%c)\n", *tmp);
326 if (IsIllegal(aconf))
329 for (;;) /* Fake loop, that I can use break here --msa */
331 if ((tmp = getfield(NULL)) == NULL)
333 DupString(aconf->host, tmp);
334 if ((tmp = getfield(NULL)) == NULL)
336 DupString(aconf->passwd, tmp);
337 if ((tmp = getfield(NULL)) == NULL)
339 DupString(aconf->name, tmp);
340 if ((tmp = getfield(NULL)) == NULL)
342 aconf->port = atoi(tmp);
343 if ((tmp = getfield(NULL)) == NULL)
345 if (!(aconf->status & (CONF_CLASS | CONF_ME)))
347 aconf->confClass = get_class(atoi(tmp), 0);
350 if (aconf->status & CONF_ME)
351 aconf->confClass = get_class(atoi(tmp), 1);
354 if (!aconf->confClass && (aconf->status & (CONF_CONNECT_SERVER |
355 CONF_ME | CONF_NOCONNECT_SERVER | CONF_OPS | CONF_CLIENT)))
357 fprintf(stderr, "\tWARNING: No class. Default 0\n");
358 aconf->confClass = get_class(0, 0);
361 * If conf line is a class definition, create a class entry
362 * for it and make the conf_line illegal and delete it.
364 if (aconf->status & CONF_CLASS)
368 fprintf(stderr, "\tERROR: no class #\n");
373 fprintf(stderr, "\tWARNING: missing sendq field\n");
374 fprintf(stderr, "\t\t default: %d\n", DEFAULTMAXSENDQLENGTH);
375 sprintf(maxsendq, "%d", DEFAULTMAXSENDQLENGTH);
378 sprintf(maxsendq, "%d", atoi(tmp));
379 new_class(atoi(aconf->host));
380 aconf->confClass = get_class(atoi(aconf->host), 0);
384 if (aconf->status & CONF_LISTEN_PORT)
390 fprintf(stderr, "\tERROR: %s\n", "null host field in P-line");
391 else if (strchr(aconf->host, '/'))
393 if (stat(aconf->host, &sb) == -1)
395 fprintf(stderr, "\tERROR: (%s) ", aconf->host);
398 else if ((sb.st_mode & S_IFMT) != S_IFDIR)
399 fprintf(stderr, "\tERROR: %s not directory\n", aconf->host);
403 fprintf(stderr, "\tERROR: %s\n", "null host field in P-line");
404 else if (strchr(aconf->host, '/'))
405 fprintf(stderr, "\t%s\n", "WARNING: / present in P-line "
406 "for non-UNIXPORT configuration");
408 aconf->confClass = get_class(0, 0);
412 if (aconf->status & CONF_SERVER_MASK &&
413 (!aconf->host || strchr(aconf->host, '*') || strchr(aconf->host, '?')))
415 fprintf(stderr, "\tERROR: bad host field\n");
419 if (aconf->status & CONF_SERVER_MASK && BadPtr(aconf->passwd))
421 fprintf(stderr, "\tERROR: empty/no password field\n");
425 if (aconf->status & CONF_SERVER_MASK && !aconf->name)
427 fprintf(stderr, "\tERROR: bad name field\n");
431 if (aconf->status & (CONF_SERVER_MASK | CONF_OPS))
432 if (!strchr(aconf->host, '@'))
435 int len = 3; /* *@\0 = 3 */
437 len += strlen(aconf->host);
438 newhost = (char *)RunMalloc(len);
439 sprintf(newhost, "*@%s", aconf->host);
440 RunFree(aconf->host);
441 aconf->host = newhost;
444 /* parse the connect rules to detect errors, but free
445 * any allocated storage immediately -- we're just looking
447 if (aconf->status & CONF_CRULE)
448 if ((crule = (char *)crule_parse(aconf->name)) != NULL)
451 if (!aconf->confClass)
452 aconf->confClass = get_class(0, 0);
453 sprintf(maxsendq, "%d", ConfClass(aconf));
455 if ((aconf->status & CONF_ADMIN) && (!aconf->name ||
456 !aconf->passwd || !aconf->host))
457 fprintf(stderr, "ERROR: Your A: line must have 4 fields!\n");
460 DupString(aconf->name, nullfield);
462 DupString(aconf->passwd, nullfield);
464 DupString(aconf->host, nullfield);
465 if (aconf->status & (CONF_ME | CONF_ADMIN))
467 if (flags & aconf->status)
468 fprintf(stderr, "ERROR: multiple %c-lines\n",
469 toUpper(confchar(aconf->status)));
471 flags |= aconf->status;
475 printf("(%d) (%s) (%s) (%s) (%u) (%s)\n",
476 aconf->status, aconf->host, aconf->passwd,
477 aconf->name, aconf->port, maxsendq);
479 if (aconf->status & (CONF_SERVER_MASK | CONF_HUB | CONF_LEAF))
490 static aConfClass *get_class(int cn, int ism)
492 static aConfClass cls;
495 cls.conClass = (unsigned int)-1;
496 if ((cn >= 1) && (cn <= 64))
499 fprintf(stderr, "\tWARNING: server numeric %d is not 1-64\n", cn);
503 int i = numclasses - 1;
504 cls.conClass = (unsigned int)-1;
506 if (classarr[i] == cn)
512 fprintf(stderr, "\tWARNING: class %d not found\n", cn);
517 static void new_class(int cn)
521 classarr = (int *)RunRealloc(classarr, sizeof(int) * numclasses);
523 classarr = (int *)RunMalloc(sizeof(int));
524 classarr[numclasses - 1] = cn;
528 * field breakup for ircd.conf file.
530 static char *getfield(char *newline)
532 static char *line = NULL;
541 if ((end = strchr(line, ':')) == NULL)
544 if ((end = strchr(field, '\n')) == NULL)
545 end = field + strlen(field);
553 static int validate(aConfItem *top)
555 Reg1 aConfItem *aconf, *bconf;
556 unsigned int otype, valid = 0;
561 for (aconf = top; aconf; aconf = aconf->next)
563 if (aconf->status & CONF_MATCH)
566 if (aconf->status & CONF_SERVER_MASK)
568 if (aconf->status & CONF_CONNECT_SERVER)
569 otype = CONF_NOCONNECT_SERVER;
570 else if (aconf->status & CONF_NOCONNECT_SERVER)
571 otype = CONF_CONNECT_SERVER;
572 else /* Does this ever happen ? */
575 for (bconf = top; bconf; bconf = bconf->next)
577 if (bconf == aconf || !(bconf->status & otype))
579 if (bconf->confClass == aconf->confClass &&
580 !strCasediff(bconf->name, aconf->name) &&
581 !strCasediff(bconf->host, aconf->host))
583 aconf->status |= CONF_MATCH;
584 bconf->status |= CONF_MATCH;
590 for (bconf = top; bconf; bconf = bconf->next)
592 if ((bconf == aconf) || !(bconf->status & CONF_SERVER_MASK))
594 if (!strCasediff(bconf->name, aconf->name))
596 aconf->status |= CONF_MATCH;
602 fprintf(stderr, "\n");
603 for (aconf = top; aconf; aconf = aconf->next)
604 if (aconf->status & CONF_MATCH)
607 fprintf(stderr, "Unmatched %c:%s:%s:%s\n",
608 confchar(aconf->status), aconf->host, aconf->passwd, aconf->name);
609 return valid ? 0 : -1;
612 static char confchar(unsigned int status)
614 static char letrs[] = "ICNoOMKARYLPH";
617 status &= ~(CONF_MATCH | CONF_ILLEGAL);
619 for (; *s; s++, status >>= 1)