edb489644db4077ad3a9b786dc763e958c5b50a8
[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, *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       if (*tmp == '\\') {
185         switch (*(tmp + 1)) {
186         case 'n':
187           *tmp = '\n';
188           break;
189         case 'r':
190           *tmp = '\r';
191           break;
192         case 't':
193           *tmp = '\t';
194           break;
195         case '0':
196           *tmp = '\0';
197           break;
198         default:
199           *tmp = *(tmp + 1);
200           break;
201         }
202         if ('\0' == *(tmp + 1))
203           break;
204         else
205           strcpy(tmp + 1, tmp + 2);
206       }
207       else if (*tmp == '#')
208         *tmp = '\0';
209     }
210     if (!*line || *line == '#' || *line == '\n' ||
211         *line == ' ' || *line == '\t')
212       continue;
213
214     if (line[1] != ':')
215     {
216       fprintf(stderr, "ERROR: Bad config line (%s)\n", line);
217       continue;
218     }
219
220     if (debugflag)
221       printf("\n%s\n", line);
222     fflush(stdout);
223
224     tmp = getfield(line);
225     if (!tmp)
226     {
227       fprintf(stderr, "\tERROR: no fields found\n");
228       continue;
229     }
230
231     aconf->status = CONF_ILLEGAL;
232
233     switch (*tmp)
234     {
235       case 'A':         /* Name, e-mail address of administrator */
236       case 'a':         /* of this server. */
237         aconf->status = CONF_ADMIN;
238         break;
239       case 'C':         /* Server where I should try to connect */
240       case 'c':         /* in case of lp failures             */
241         ccount++;
242         aconf->status = CONF_SERVER;
243         break;
244         /* Connect rule */
245       case 'D':
246         aconf->status = CONF_CRULEALL;
247         break;
248         /* Connect rule - autos only */
249       case 'd':
250         aconf->status = CONF_CRULEAUTO;
251         break;
252       case 'H':         /* Hub server line */
253       case 'h':
254         aconf->status = CONF_HUB;
255         break;
256       case 'I':         /* Just plain normal irc client trying  */
257       case 'i':         /* to connect me */
258         aconf->status = CONF_CLIENT;
259         break;
260       case 'K':         /* Kill user line on irc.conf           */
261         aconf->status = CONF_KILL;
262         break;
263       case 'k':         /* Kill user line based on IP in ircd.conf */
264         aconf->status = CONF_IPKILL;
265         break;
266         /* Operator. Line should contain at least */
267         /* password and host where connection is  */
268       case 'L':         /* guaranteed leaf server */
269       case 'l':
270         aconf->status = CONF_LEAF;
271         break;
272         /* Me. Host field is name used for this host */
273         /* and port number is the number of the port */
274       case 'M':
275       case 'm':
276         aconf->status = CONF_ME;
277         break;
278       case 'O':
279         aconf->status = CONF_OPERATOR;
280         break;
281         /* Local Operator, (limited privs --SRB) */
282       case 'o':
283         aconf->status = CONF_LOCOP;
284         break;
285       case 'P':         /* listen port line */
286       case 'p':
287         aconf->status = CONF_LISTEN_PORT;
288         break;
289       case 'T':
290       case 't':
291         aconf->status = CONF_TLINES;
292         break;
293       case 'U':
294       case 'u':
295         aconf->status = CONF_UWORLD;
296         break;
297       case 'Y':
298       case 'y':
299         aconf->status = CONF_CLASS;
300         break;
301       default:
302         fprintf(stderr, "\tERROR: unknown conf line letter (%c)\n", *tmp);
303         break;
304     }
305
306     if (IsIllegal(aconf))
307       continue;
308
309     for (;;)                    /* Fake loop, that I can use break here --msa */
310     {
311       if ((tmp = getfield(NULL)) == NULL)
312         break;
313       DupString(aconf->host, tmp);
314       if ((tmp = getfield(NULL)) == NULL)
315         break;
316       DupString(aconf->passwd, tmp);
317       if ((tmp = getfield(NULL)) == NULL)
318         break;
319       DupString(aconf->name, tmp);
320       if ((tmp = getfield(NULL)) == NULL)
321         break;
322       aconf->port = atoi(tmp);
323       if ((tmp = getfield(NULL)) == NULL)
324         break;
325       if (!(aconf->status & (CONF_CLASS | CONF_ME)))
326       {
327         aconf->confClass = get_class(atoi(tmp), 0);
328         break;
329       }
330       if (aconf->status & CONF_ME)
331         aconf->confClass = get_class(atoi(tmp), 1);
332       break;
333     }
334     if (!aconf->confClass && (aconf->status & (CONF_SERVER |
335         CONF_ME | CONF_OPS | CONF_CLIENT)))
336     {
337       fprintf(stderr, "\tWARNING: No class.      Default 0\n");
338       aconf->confClass = get_class(0, 0);
339     }
340     /*
341      * If conf line is a class definition, create a class entry
342      * for it and make the conf_line illegal and delete it.
343      */
344     if (aconf->status & CONF_CLASS)
345     {
346       if (!aconf->host)
347       {
348         fprintf(stderr, "\tERROR: no class #\n");
349         continue;
350       }
351       if (!tmp)
352       {
353         fprintf(stderr, "\tWARNING: missing sendq field\n");
354         fprintf(stderr, "\t\t default: %d\n", DEFAULTMAXSENDQLENGTH);
355         sprintf(maxsendq, "%d", DEFAULTMAXSENDQLENGTH);
356       }
357       else
358         sprintf(maxsendq, "%d", atoi(tmp));
359       new_class(atoi(aconf->host));
360       aconf->confClass = get_class(atoi(aconf->host), 0);
361       goto print_confline;
362     }
363
364     if (aconf->status & CONF_LISTEN_PORT)
365     {
366       if (!aconf->host)
367         fprintf(stderr, "\tERROR: %s\n", "null host field in P-line");
368       else if (strchr(aconf->host, '/'))
369         fprintf(stderr, "\t%s\n", "WARNING: / present in P-line "
370             "for non-UNIXPORT configuration");
371       aconf->confClass = get_class(0, 0);
372       goto print_confline;
373     }
374
375     if (aconf->status & CONF_SERVER &&
376         (!aconf->host || strchr(aconf->host, '*') || strchr(aconf->host, '?')))
377     {
378       fprintf(stderr, "\tERROR: bad host field\n");
379       continue;
380     }
381
382     if (aconf->status & CONF_SERVER && BadPtr(aconf->passwd))
383     {
384       fprintf(stderr, "\tERROR: empty/no password field\n");
385       continue;
386     }
387
388     if (aconf->status & CONF_SERVER && !aconf->name)
389     {
390       fprintf(stderr, "\tERROR: bad name field\n");
391       continue;
392     }
393
394     if (aconf->status & (CONF_OPS))
395       if (!strchr(aconf->host, '@'))
396       {
397         char *newhost;
398         int len = 3;            /* *@\0 = 3 */
399
400         len += strlen(aconf->host);
401         newhost = (char *)MyMalloc(len);
402         sprintf(newhost, "*@%s", aconf->host);
403         MyFree(aconf->host);
404         aconf->host = newhost;
405       }
406
407     /* parse the connect rules to detect errors, but free
408      *  any allocated storage immediately -- we're just looking
409      *  for errors..  */
410     if (aconf->status & CONF_CRULE)
411       if ((crule = (char *)crule_parse(aconf->name)) != NULL)
412         crule_free(&crule);
413
414     if (!aconf->confClass)
415       aconf->confClass = get_class(0, 0);
416     sprintf(maxsendq, "%d", ConfClass(aconf));
417
418     if ((aconf->status & CONF_ADMIN) && (!aconf->name ||
419         !aconf->passwd || !aconf->host))
420       fprintf(stderr, "ERROR: Your A: line must have 4 fields!\n");
421
422     if (!aconf->name)
423       DupString(aconf->name, nullfield);
424     if (!aconf->passwd)
425       DupString(aconf->passwd, nullfield);
426     if (!aconf->host)
427       DupString(aconf->host, nullfield);
428     if (aconf->status & (CONF_ME | CONF_ADMIN))
429     {
430       if (flags & aconf->status)
431         fprintf(stderr, "ERROR: multiple %c-lines\n",
432                 ToUpper(confchar(aconf->status)));
433       else
434         flags |= aconf->status;
435     }
436 print_confline:
437     if (debugflag > 8)
438       printf("(%d) (%s) (%s) (%s) (%u) (%s)\n",
439           aconf->status, aconf->host, aconf->passwd,
440           aconf->name, aconf->port, maxsendq);
441     fflush(stdout);
442     if (aconf->status & (CONF_SERVER | CONF_HUB | CONF_LEAF))
443     {
444       aconf->next = ctop;
445       ctop = aconf;
446       aconf = NULL;
447     }
448   }
449   fbclose(file);
450   return ctop;
451 }
452
453 static struct ConfClass *get_class(int cn, int ism)
454 {
455   static struct ConfClass cls;
456   if (ism == 1)
457   {
458     cls.conClass = (unsigned int)-1;
459     if ((cn >= 1) && (cn <= 64))
460       cls.conClass = cn;
461     else
462       fprintf(stderr, "\tWARNING: server numeric %d is not 1-64\n", cn);
463   }
464   else
465   {
466     int i = numclasses - 1;
467     cls.conClass = (unsigned int)-1;
468     for (; i >= 0; i--)
469       if (classarr[i] == cn)
470       {
471         cls.conClass = cn;
472         break;
473       }
474     if (i == -1)
475       fprintf(stderr, "\tWARNING: class %d not found\n", cn);
476   }
477   return &cls;
478 }
479
480 static void new_class(int cn)
481 {
482   numclasses++;
483   if (classarr)
484     classarr = (int *)MyRealloc(classarr, sizeof(int) * numclasses);
485   else
486     classarr = (int *)MyMalloc(sizeof(int));
487   classarr[numclasses - 1] = cn;
488 }
489
490 /*
491  * field breakup for ircd.conf file.
492  */
493 static char *getfield(char *newline)
494 {
495   static char *line = NULL;
496   char *end, *field;
497
498   if (newline)
499     line = newline;
500   if (line == NULL)
501     return (NULL);
502
503   field = line;
504   if ((end = strchr(line, ':')) == NULL)
505   {
506     line = NULL;
507     if ((end = strchr(field, '\n')) == NULL)
508       end = field + strlen(field);
509   }
510   else
511     line = end + 1;
512   *end = '\0';
513   return (field);
514 }
515
516 static int validate(struct ConfItem *top)
517 {
518   struct ConfItem *aconf, *bconf;
519   unsigned int otype, valid = 0;
520
521   if (!top)
522     return 0;
523
524   for (aconf = top; aconf; aconf = aconf->next)
525   {
526     if (aconf->status & CONF_MATCH)
527       continue;
528
529     if (aconf->status & CONF_SERVER)
530     {
531       otype = CONF_SERVER;
532
533       for (bconf = top; bconf; bconf = bconf->next)
534       {
535         if (bconf == aconf || !(bconf->status & otype))
536           continue;
537         if (bconf->confClass == aconf->confClass &&
538             0 == ircd_strcmp(bconf->name, aconf->name) &&
539             0 == ircd_strcmp(bconf->host, aconf->host))
540         {
541           aconf->status |= CONF_MATCH;
542           bconf->status |= CONF_MATCH;
543           break;
544         }
545       }
546     }
547     else
548       for (bconf = top; bconf; bconf = bconf->next)
549       {
550         if ((bconf == aconf) || !(bconf->status & CONF_SERVER))
551           continue;
552         if (0 == ircd_strcmp(bconf->name, aconf->name))
553         {
554           aconf->status |= CONF_MATCH;
555           break;
556         }
557       }
558   }
559
560   fprintf(stderr, "\n");
561   for (aconf = top; aconf; aconf = aconf->next) {
562     if (aconf->status & CONF_MATCH)
563       valid++;
564     else if ('N' != confchar(aconf->status)) 
565       fprintf(stderr, "Unmatched %c:%s:%s:%s\n",
566           confchar(aconf->status), aconf->host, aconf->passwd, aconf->name);
567   }
568   return valid ? 0 : -1;
569 }
570
571 static char confchar(unsigned int status)
572 {
573   static char letrs[] = "ICNoOMKARYLPH";
574   char *s = letrs;
575
576   status &= ~(CONF_MATCH | CONF_ILLEGAL);
577
578   for (; *s; s++, status >>= 1)
579     if (status & 1)
580       return *s;
581   return '-';
582 }