Author: Bleep <tomh@inxpress.net>
[ircu2.10.12-pk.git] / ircd / chkconf.c
1 /*
2  * IRC - Internet Relay Chat, ircd/chkconf.c
3  * Copyright (C) 1993 Darren Reed
4  *
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)
8  * any later version.
9  *
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.
14  *
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.
18  *
19  * $Id$
20  */
21 #include "s_conf.h"
22 #include "client.h"
23 #include "class.h"
24 #include "fileio.h"
25 #include "ircd.h"
26 #include "ircd_alloc.h"
27 #include "ircd_chattr.h"
28 #include "ircd_string.h"
29 #include "sys.h"
30
31 #include <assert.h>
32 #include <ctype.h>
33 #include <errno.h>
34 #include <fcntl.h>
35 #include <stdarg.h>
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <string.h>
39 #include <sys/stat.h>
40 #include <unistd.h>
41
42
43 /*
44  * For the connect rule patch..  these really should be in a header,
45  * but i see h.h isn't included for some reason..  so they're here.
46  */
47 char *crule_parse(char *rule);
48 void crule_free(char **elem);
49
50 static void new_class(int cn);
51 static char confchar(unsigned int status);
52 static char *getfield(char *newline);
53 static int validate(struct ConfItem *top);
54 static struct ConfItem *chk_initconf(void);
55 static struct ConfClass *get_class(int cn, int ism);
56
57 static int numclasses = 0, *classarr = (int *)NULL, debugflag = 0;
58 static char *chk_configfile = CPATH;
59 static char nullfield[] = "";
60 static char maxsendq[12];
61
62 /* A few dummy variables and functions needed to link with runmalloc.o */
63 time_t CurrentTime;
64 struct Client me;
65 void debug(int level, const char *form, ...)
66 {
67   va_list vl;
68   va_start(vl, form);
69   if (debugflag > 8)
70   {
71     vfprintf(stderr, form, vl);
72     fprintf(stderr, "\n");
73   }
74   va_end(vl);
75 }
76 void sendto_one(struct Client *to, char *pattern, ...)
77 {
78 }
79 char *rpl_str(int numeric)
80 {
81   return "";
82 }
83
84 int main(int argc, char *argv[])
85 {
86   const char *dpath = DPATH;
87   chk_configfile = "ircd.conf";
88
89   while (argc > 1)
90   {
91     if (*argv[1] == '-')
92     {
93       switch (argv[1][1])
94       {
95         case 'd':
96           if (argc > 2)
97           {
98             dpath = argv[2];
99             --argc;
100             ++argv;
101           }
102           else
103           {
104             fprintf(stderr, "-d: Missing path\n");
105             exit(-1);
106           }
107           break;
108         case 'x':
109           debugflag = 1;
110           if (isdigit(argv[1][2]))
111             debugflag = atoi(&argv[1][2]);
112           break;
113         default:
114           fprintf(stderr, "Ignoring unknown option -%c\n", argv[1][1]);
115       }
116     }
117     else
118       chk_configfile = argv[1];
119     --argc;
120     ++argv;
121   }
122
123   if (chdir(dpath))
124   {
125     fprintf(stderr, "chdir(\"%s\") : %s\n", dpath, strerror(errno));
126     exit(-1);
127   }
128   else if (debugflag > 1)
129     fprintf(stderr, "chdir(\"%s\") : Success", dpath);
130
131   new_class(0);
132
133   return validate(chk_initconf());
134 }
135
136 /*
137  * chk_initconf()
138  *
139  * Read configuration file.
140  *
141  * Returns -1, if file cannot be opened
142  *          0, if file opened.
143  */
144 static struct ConfItem *chk_initconf(void)
145 {
146   FBFILE *file;
147   char line[512], *tmp, *s, *crule;
148   int ccount = 0, flags = 0;
149   struct ConfItem *aconf = NULL, *ctop = NULL;
150
151   fprintf(stderr, "chk_initconf(): ircd.conf = %s\n", chk_configfile);
152   if (NULL == (file = fbopen(chk_configfile, "r")))
153   {
154     perror("open");
155     return NULL;
156   }
157
158   while (fbgets(line, sizeof(line) - 1, file))
159   {
160     if (aconf) {
161       if (aconf->host)
162         MyFree(aconf->host);
163       if (aconf->passwd)
164         MyFree(aconf->passwd);
165       if (aconf->name)
166         MyFree(aconf->name);
167     }
168     else {
169       aconf = (struct ConfItem*) MyMalloc(sizeof(struct ConfItem));
170       assert(0 != aconf);
171     }
172     aconf->host        = NULL;
173     aconf->passwd      = NULL;
174     aconf->name        = NULL;
175     aconf->confClass   = NULL;
176     aconf->dns_pending = 0;
177
178     if ((tmp = strchr(line, '\n')))
179       *tmp = 0;
180     /*
181      * Do quoting of characters and # detection.
182      */
183     for (tmp = line; *tmp; tmp++)
184     {
185       if (*tmp == '\\')
186       {
187         switch (*(tmp + 1))
188         {
189           case 'n':
190             *tmp = '\n';
191             break;
192           case 'r':
193             *tmp = '\r';
194             break;
195           case 't':
196             *tmp = '\t';
197             break;
198           case '0':
199             *tmp = '\0';
200             break;
201           default:
202             *tmp = *(tmp + 1);
203             break;
204         }
205         if (!*(tmp + 1))
206           break;
207         else
208           for (s = tmp; (*s = *++s);)
209             ;
210         tmp++;
211       }
212       else if (*tmp == '#')
213         *tmp = '\0';
214     }
215     if (!*line || *line == '#' || *line == '\n' ||
216         *line == ' ' || *line == '\t')
217       continue;
218
219     if (line[1] != ':')
220     {
221       fprintf(stderr, "ERROR: Bad config line (%s)\n", line);
222       continue;
223     }
224
225     if (debugflag)
226       printf("\n%s\n", line);
227     fflush(stdout);
228
229     tmp = getfield(line);
230     if (!tmp)
231     {
232       fprintf(stderr, "\tERROR: no fields found\n");
233       continue;
234     }
235
236     aconf->status = CONF_ILLEGAL;
237
238     switch (*tmp)
239     {
240       case 'A':         /* Name, e-mail address of administrator */
241       case 'a':         /* of this server. */
242         aconf->status = CONF_ADMIN;
243         break;
244       case 'C':         /* Server where I should try to connect */
245       case 'c':         /* in case of lp failures             */
246         ccount++;
247         aconf->status = CONF_SERVER;
248         break;
249         /* Connect rule */
250       case 'D':
251         aconf->status = CONF_CRULEALL;
252         break;
253         /* Connect rule - autos only */
254       case 'd':
255         aconf->status = CONF_CRULEAUTO;
256         break;
257       case 'H':         /* Hub server line */
258       case 'h':
259         aconf->status = CONF_HUB;
260         break;
261       case 'I':         /* Just plain normal irc client trying  */
262       case 'i':         /* to connect me */
263         aconf->status = CONF_CLIENT;
264         break;
265       case 'K':         /* Kill user line on irc.conf           */
266         aconf->status = CONF_KILL;
267         break;
268       case 'k':         /* Kill user line based on IP in ircd.conf */
269         aconf->status = CONF_IPKILL;
270         break;
271         /* Operator. Line should contain at least */
272         /* password and host where connection is  */
273       case 'L':         /* guaranteed leaf server */
274       case 'l':
275         aconf->status = CONF_LEAF;
276         break;
277         /* Me. Host field is name used for this host */
278         /* and port number is the number of the port */
279       case 'M':
280       case 'm':
281         aconf->status = CONF_ME;
282         break;
283       case 'O':
284         aconf->status = CONF_OPERATOR;
285         break;
286         /* Local Operator, (limited privs --SRB) */
287       case 'o':
288         aconf->status = CONF_LOCOP;
289         break;
290       case 'P':         /* listen port line */
291       case 'p':
292         aconf->status = CONF_LISTEN_PORT;
293         break;
294       case 'T':
295       case 't':
296         aconf->status = CONF_TLINES;
297         break;
298       case 'U':
299       case 'u':
300         aconf->status = CONF_UWORLD;
301         break;
302       case 'Y':
303       case 'y':
304         aconf->status = CONF_CLASS;
305         break;
306       default:
307         fprintf(stderr, "\tERROR: unknown conf line letter (%c)\n", *tmp);
308         break;
309     }
310
311     if (IsIllegal(aconf))
312       continue;
313
314     for (;;)                    /* Fake loop, that I can use break here --msa */
315     {
316       if ((tmp = getfield(NULL)) == NULL)
317         break;
318       DupString(aconf->host, tmp);
319       if ((tmp = getfield(NULL)) == NULL)
320         break;
321       DupString(aconf->passwd, tmp);
322       if ((tmp = getfield(NULL)) == NULL)
323         break;
324       DupString(aconf->name, tmp);
325       if ((tmp = getfield(NULL)) == NULL)
326         break;
327       aconf->port = atoi(tmp);
328       if ((tmp = getfield(NULL)) == NULL)
329         break;
330       if (!(aconf->status & (CONF_CLASS | CONF_ME)))
331       {
332         aconf->confClass = get_class(atoi(tmp), 0);
333         break;
334       }
335       if (aconf->status & CONF_ME)
336         aconf->confClass = get_class(atoi(tmp), 1);
337       break;
338     }
339     if (!aconf->confClass && (aconf->status & (CONF_SERVER |
340         CONF_ME | CONF_OPS | CONF_CLIENT)))
341     {
342       fprintf(stderr, "\tWARNING: No class.      Default 0\n");
343       aconf->confClass = get_class(0, 0);
344     }
345     /*
346      * If conf line is a class definition, create a class entry
347      * for it and make the conf_line illegal and delete it.
348      */
349     if (aconf->status & CONF_CLASS)
350     {
351       if (!aconf->host)
352       {
353         fprintf(stderr, "\tERROR: no class #\n");
354         continue;
355       }
356       if (!tmp)
357       {
358         fprintf(stderr, "\tWARNING: missing sendq field\n");
359         fprintf(stderr, "\t\t default: %d\n", DEFAULTMAXSENDQLENGTH);
360         sprintf(maxsendq, "%d", DEFAULTMAXSENDQLENGTH);
361       }
362       else
363         sprintf(maxsendq, "%d", atoi(tmp));
364       new_class(atoi(aconf->host));
365       aconf->confClass = get_class(atoi(aconf->host), 0);
366       goto print_confline;
367     }
368
369     if (aconf->status & CONF_LISTEN_PORT)
370     {
371       if (!aconf->host)
372         fprintf(stderr, "\tERROR: %s\n", "null host field in P-line");
373       else if (strchr(aconf->host, '/'))
374         fprintf(stderr, "\t%s\n", "WARNING: / present in P-line "
375             "for non-UNIXPORT configuration");
376       aconf->confClass = get_class(0, 0);
377       goto print_confline;
378     }
379
380     if (aconf->status & CONF_SERVER &&
381         (!aconf->host || strchr(aconf->host, '*') || strchr(aconf->host, '?')))
382     {
383       fprintf(stderr, "\tERROR: bad host field\n");
384       continue;
385     }
386
387     if (aconf->status & CONF_SERVER && BadPtr(aconf->passwd))
388     {
389       fprintf(stderr, "\tERROR: empty/no password field\n");
390       continue;
391     }
392
393     if (aconf->status & CONF_SERVER && !aconf->name)
394     {
395       fprintf(stderr, "\tERROR: bad name field\n");
396       continue;
397     }
398
399     if (aconf->status & (CONF_OPS))
400       if (!strchr(aconf->host, '@'))
401       {
402         char *newhost;
403         int len = 3;            /* *@\0 = 3 */
404
405         len += strlen(aconf->host);
406         newhost = (char *)MyMalloc(len);
407         sprintf(newhost, "*@%s", aconf->host);
408         MyFree(aconf->host);
409         aconf->host = newhost;
410       }
411
412     /* parse the connect rules to detect errors, but free
413      *  any allocated storage immediately -- we're just looking
414      *  for errors..  */
415     if (aconf->status & CONF_CRULE)
416       if ((crule = (char *)crule_parse(aconf->name)) != NULL)
417         crule_free(&crule);
418
419     if (!aconf->confClass)
420       aconf->confClass = get_class(0, 0);
421     sprintf(maxsendq, "%d", ConfClass(aconf));
422
423     if ((aconf->status & CONF_ADMIN) && (!aconf->name ||
424         !aconf->passwd || !aconf->host))
425       fprintf(stderr, "ERROR: Your A: line must have 4 fields!\n");
426
427     if (!aconf->name)
428       DupString(aconf->name, nullfield);
429     if (!aconf->passwd)
430       DupString(aconf->passwd, nullfield);
431     if (!aconf->host)
432       DupString(aconf->host, nullfield);
433     if (aconf->status & (CONF_ME | CONF_ADMIN))
434     {
435       if (flags & aconf->status)
436         fprintf(stderr, "ERROR: multiple %c-lines\n",
437                 ToUpper(confchar(aconf->status)));
438       else
439         flags |= aconf->status;
440     }
441 print_confline:
442     if (debugflag > 8)
443       printf("(%d) (%s) (%s) (%s) (%u) (%s)\n",
444           aconf->status, aconf->host, aconf->passwd,
445           aconf->name, aconf->port, maxsendq);
446     fflush(stdout);
447     if (aconf->status & (CONF_SERVER | CONF_HUB | CONF_LEAF))
448     {
449       aconf->next = ctop;
450       ctop = aconf;
451       aconf = NULL;
452     }
453   }
454   fbclose(file);
455   return ctop;
456 }
457
458 static struct ConfClass *get_class(int cn, int ism)
459 {
460   static struct ConfClass cls;
461   if (ism == 1)
462   {
463     cls.conClass = (unsigned int)-1;
464     if ((cn >= 1) && (cn <= 64))
465       cls.conClass = cn;
466     else
467       fprintf(stderr, "\tWARNING: server numeric %d is not 1-64\n", cn);
468   }
469   else
470   {
471     int i = numclasses - 1;
472     cls.conClass = (unsigned int)-1;
473     for (; i >= 0; i--)
474       if (classarr[i] == cn)
475       {
476         cls.conClass = cn;
477         break;
478       }
479     if (i == -1)
480       fprintf(stderr, "\tWARNING: class %d not found\n", cn);
481   }
482   return &cls;
483 }
484
485 static void new_class(int cn)
486 {
487   numclasses++;
488   if (classarr)
489     classarr = (int *)MyRealloc(classarr, sizeof(int) * numclasses);
490   else
491     classarr = (int *)MyMalloc(sizeof(int));
492   classarr[numclasses - 1] = cn;
493 }
494
495 /*
496  * field breakup for ircd.conf file.
497  */
498 static char *getfield(char *newline)
499 {
500   static char *line = NULL;
501   char *end, *field;
502
503   if (newline)
504     line = newline;
505   if (line == NULL)
506     return (NULL);
507
508   field = line;
509   if ((end = strchr(line, ':')) == NULL)
510   {
511     line = NULL;
512     if ((end = strchr(field, '\n')) == NULL)
513       end = field + strlen(field);
514   }
515   else
516     line = end + 1;
517   *end = '\0';
518   return (field);
519 }
520
521 static int validate(struct ConfItem *top)
522 {
523   struct ConfItem *aconf, *bconf;
524   unsigned int otype, valid = 0;
525
526   if (!top)
527     return 0;
528
529   for (aconf = top; aconf; aconf = aconf->next)
530   {
531     if (aconf->status & CONF_MATCH)
532       continue;
533
534     if (aconf->status & CONF_SERVER)
535     {
536       otype = CONF_SERVER;
537
538       for (bconf = top; bconf; bconf = bconf->next)
539       {
540         if (bconf == aconf || !(bconf->status & otype))
541           continue;
542         if (bconf->confClass == aconf->confClass &&
543             0 == ircd_strcmp(bconf->name, aconf->name) &&
544             0 == ircd_strcmp(bconf->host, aconf->host))
545         {
546           aconf->status |= CONF_MATCH;
547           bconf->status |= CONF_MATCH;
548           break;
549         }
550       }
551     }
552     else
553       for (bconf = top; bconf; bconf = bconf->next)
554       {
555         if ((bconf == aconf) || !(bconf->status & CONF_SERVER))
556           continue;
557         if (0 == ircd_strcmp(bconf->name, aconf->name))
558         {
559           aconf->status |= CONF_MATCH;
560           break;
561         }
562       }
563   }
564
565   fprintf(stderr, "\n");
566   for (aconf = top; aconf; aconf = aconf->next) {
567     if (aconf->status & CONF_MATCH)
568       valid++;
569     else if ('N' != confchar(aconf->status)) 
570       fprintf(stderr, "Unmatched %c:%s:%s:%s\n",
571           confchar(aconf->status), aconf->host, aconf->passwd, aconf->name);
572   }
573   return valid ? 0 : -1;
574 }
575
576 static char confchar(unsigned int status)
577 {
578   static char letrs[] = "ICNoOMKARYLPH";
579   char *s = letrs;
580
581   status &= ~(CONF_MATCH | CONF_ILLEGAL);
582
583   for (; *s; s++, status >>= 1)
584     if (status & 1)
585       return *s;
586   return '-';
587 }