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