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