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