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