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
20 #ifndef lint
21 static const volatile char rcsid[] = "@(#)$Id: chkconf.c,v 1.52 2009/04/29 22:05:08 chopin Exp $";
22 #endif
23
24 #include "os.h"
25 #include "s_defines.h"
26 #define CHKCONF_C
27 #include "match_ext.h"
28 #undef CHKCONF_C
29
30 #define mystrdup(x) strdup(x)
31 #ifdef MyMalloc
32 #undef MyMalloc
33 #endif
34 #define MyMalloc(x) malloc(x)
35 /*#define MyFree(x) free(x)*/
36
37 static char *configfile = IRCDCONF_PATH;
38 #include "config_read.c"
39
40 #ifdef CONFIG_DIRECTIVE_INCLUDE
41 #define CK_FILE filelist->file
42 #define CK_LINE filelist->linenum
43 static aConfig *findConfLineNumber(int);
44 static aConfig *files;
45 #else
46 #define CK_FILE filelist->filename
47 #define CK_LINE nr - filelist->min
48 static struct wordcount *findConfLineNumber(int);
49 static struct wordcount *files;
50 struct wordcount {
51 char *filename;
52 int min;
53 int max;
54 struct wordcount *next;
55 };
56 static void mywc(void);
57 #ifdef M4_PREPROC
58 static char * mystrinclude(char *, int);
59 static int simulateM4Include(struct wordcount *, int, char *, int);
60 #endif
61 static int dgets(int, char *, int);
62 #endif
63
64 static void new_class(int);
65 static char *getfield(char *), confchar(u_int);
66 static int openconf(void);
67 static void validate(aConfItem *);
68 static aClass *get_class(int, int);
69 static aConfItem *initconf(void);
70 static void showconf(void);
71 static int checkSID(char *, int);
72
73 static int numclasses = 0, *classarr = (int *)NULL, debugflag = 0;
74 static char nullfield[] = "";
75 static char maxsendq[12];
76
77 #define SHOWSTR(x) ((x) ? (x) : "*")
78
main(int argc,char * argv[])79 int main(int argc, char *argv[])
80 {
81 aConfItem *result;
82 int showflag = 0;
83
84 if (argc > 1 && !strncmp(argv[1], "-h", 2))
85 {
86 (void)printf("Usage: %s [-h | -s | -d[#]] [ircd.conf]\n",
87 argv[0]);
88 (void)printf("\t-h\tthis help\n");
89 (void)printf("\t-s\tshows preprocessed config file (after "
90 "includes and/or M4)\n");
91 (void)printf("\t-d[#]\tthe bigger number, the more verbose "
92 "chkconf is in its checks\n");
93 (void)printf("\tDefault ircd.conf = %s\n", IRCDCONF_PATH);
94 exit(0);
95 }
96 new_class(0);
97
98 if (argc > 1 && !strncmp(argv[1], "-d", 2))
99 {
100 debugflag = 1;
101 if (argv[1][2])
102 debugflag = atoi(argv[1]+2);
103 argc--;
104 argv++;
105 }
106 if (argc > 1 && !strncmp(argv[1], "-s", 2))
107 {
108 showflag = 1;
109 argc--;
110 argv++;
111 }
112 if (argc > 1)
113 configfile = argv[1];
114 if (showflag)
115 {
116 showconf();
117 return 0;
118 }
119 #ifndef CONFIG_DIRECTIVE_INCLUDE
120 /* Initialize counters to be able to print line number even with m4 */
121 mywc();
122 # ifdef DEBUGMODE
123 {
124 struct wordcount *filelist;
125
126 for(filelist = files; filelist->next; filelist = filelist->next)
127 fprintf(stderr, "%s: Min %d - Max %d\n",
128 filelist->filename, filelist->min, filelist->max);
129 }
130 # endif
131 #endif
132 /* If I do not use result as temporary return value
133 * I get loops when M4_PREPROC is defined - Babar */
134 result = initconf();
135 validate(result);
136 #if defined(CONFIG_DIRECTIVE_INCLUDE)
137 config_free(files);
138 #endif
139 return 0;
140 }
141
142 /*
143 * openconf
144 *
145 * returns -1 on any error or else the fd opened from which to read the
146 * configuration file from. This may either be th4 file direct or one end
147 * of a pipe from m4.
148 */
openconf(void)149 static int openconf(void)
150 {
151 #ifdef M4_PREPROC
152 int pi[2];
153 #ifdef HAVE_GNU_M4
154 char *includedir, *includedirptr;
155
156 includedir = strdup(IRCDM4_PATH);
157 includedirptr = strrchr(includedir, '/');
158 if (includedirptr)
159 *includedirptr = '\0';
160 #endif
161
162 /* ircd.m4 with full path now! Kratz */
163 if (access(IRCDM4_PATH, R_OK) == -1)
164 {
165 (void)fprintf(stderr, "%s missing.\n", IRCDM4_PATH);
166 return -1;
167 }
168 if (pipe(pi) == -1)
169 return -1;
170 switch(fork())
171 {
172 case -1:
173 return -1;
174 case 0:
175 (void)close(pi[0]);
176 (void)close(2);
177 if (pi[1] != 1)
178 {
179 (void)close(1);
180 (void)dup2(pi[1], 1);
181 (void)close(pi[1]);
182 }
183 (void)dup2(1,2);
184 /*
185 * m4 maybe anywhere, use execvp to find it. Any error
186 * goes out with report_error. Could be dangerous,
187 * two servers running with the same fd's >:-) -avalon
188 */
189 (void)execlp(M4_PATH, "m4",
190 #ifdef HAVE_GNU_M4
191 #ifdef USE_M4_PREFIXES
192 "-P",
193 #endif
194 "-I", includedir,
195 #endif
196 #ifdef INET6
197 "-DINET6",
198 #endif
199 IRCDM4_PATH, configfile, (char *) NULL);
200 perror("m4");
201 exit(-1);
202 default :
203 (void)close(pi[1]);
204 return pi[0];
205 }
206 #else
207 return open(configfile, O_RDONLY);
208 #endif
209 }
210
211 /* show config as ircd would see it before starting to parse it */
showconf(void)212 static void showconf(void)
213 {
214 #if defined(CONFIG_DIRECTIVE_INCLUDE)
215 aConfig *p, *p2;
216 int etclen = 0;
217 FILE *fdn;
218 #else
219 int dh;
220 char line[512], c[80], *tmp;
221 #endif
222 int fd;
223
224 fprintf(stderr, "showconf(): %s\n", configfile);
225 if ((fd = openconf()) == -1)
226 {
227 #ifdef M4_PREPROC
228 (void)wait(0);
229 #endif
230 return;
231 }
232 #if defined(CONFIG_DIRECTIVE_INCLUDE)
233 if (debugflag)
234 {
235 etclen = strlen(IRCDCONF_DIR);
236 }
237 fdn = fdopen(fd, "r");
238 p2 = config_read(fdn, 0, new_config_file(configfile, NULL, 0));
239 for(p = p2; p; p = p->next)
240 {
241 if (debugflag)
242 printf("%s:%d:", p->file->filename +
243 (strncmp(p->file->filename, IRCDCONF_DIR,
244 etclen) == 0 ? etclen : 0), p->linenum);
245 printf("%s\n", p->line);
246 }
247 config_free(p2);
248 (void)fclose(fdn);
249 #else
250 (void)dgets(-1, NULL, 0); /* make sure buffer is at empty pos */
251 while ((dh = dgets(fd, line, sizeof(line) - 1)) > 0)
252 {
253 if ((tmp = (char *)index(line, '\n')))
254 *tmp = 0;
255 else while(dgets(fd, c, sizeof(c) - 1))
256 if ((tmp = (char *)index(c, '\n')))
257 {
258 *tmp = 0;
259 break;
260 }
261 printf("%s\n", line);
262 }
263 (void)close(fd);
264 #endif
265 #ifdef M4_PREPROC
266 (void)wait(0);
267 #endif
268 return;
269 }
270
271
272 /*
273 ** initconf()
274 ** Read configuration file.
275 **
276 ** returns a pointer to the config items chain if successful
277 ** NULL if config is invalid (some mandatory fields missing)
278 */
279
initconf(void)280 static aConfItem *initconf(void)
281 {
282 int fd;
283 char *tmp, *tmp3 = NULL, *s;
284 int ccount = 0, ncount = 0, flags = 0, nr = 0;
285 aConfItem *aconf = NULL, *ctop = NULL;
286 int mandatory_found = 0, valid = 1;
287 #if defined(CONFIG_DIRECTIVE_INCLUDE)
288 char *line;
289 aConfig *ConfigTop, *filelist;
290 aFile *ftop;
291 FILE *fdn;
292 #else
293 int dh;
294 struct wordcount *filelist = files;
295 char line[512], c[80], *ftop;
296 #endif
297
298 (void)fprintf(stderr, "initconf(): ircd.conf = %s\n", configfile);
299 if ((fd = openconf()) == -1)
300 {
301 #ifdef M4_PREPROC
302 (void)wait(0);
303 #endif
304 return NULL;
305 }
306
307 #if defined(CONFIG_DIRECTIVE_INCLUDE)
308 ftop = new_config_file(configfile, NULL, 0);
309 fdn = fdopen(fd, "r");
310 files = ConfigTop = config_read(fdn, 0, ftop);
311 for(filelist = ConfigTop; filelist; filelist = filelist->next)
312 #else
313 ftop = configfile;
314 (void)dgets(-1, NULL, 0); /* make sure buffer is at empty pos */
315 while ((dh = dgets(fd, line, sizeof(line) - 1)) > 0)
316 #endif
317 {
318 if (aconf)
319 {
320 if (aconf->host && (aconf->host != nullfield))
321 (void)free(aconf->host);
322 if (aconf->passwd && (aconf->passwd != nullfield))
323 (void)free(aconf->passwd);
324 if (aconf->name && (aconf->name != nullfield))
325 (void)free(aconf->name);
326 }
327 else
328 aconf = (aConfItem *)malloc(sizeof(*aconf));
329 aconf->host = (char *)NULL;
330 aconf->passwd = (char *)NULL;
331 aconf->name = (char *)NULL;
332 aconf->class = (aClass *)NULL;
333 /* abusing clients to store ircd.conf line number */
334 aconf->clients = ++nr;
335
336 #if defined(CONFIG_DIRECTIVE_INCLUDE)
337 line = filelist->line;
338 #else
339 while (filelist->next && (filelist->max < nr))
340 filelist = filelist->next;
341 if ((tmp = (char *)index(line, '\n')))
342 *tmp = 0;
343 else while(dgets(fd, c, sizeof(c) - 1))
344 if ((tmp = (char *)index(c, '\n')))
345 {
346 *tmp = 0;
347 break;
348 }
349 #endif
350 /*
351 * Do quoting of characters and # detection.
352 */
353 for (tmp = line; *tmp; tmp++)
354 {
355 if (*tmp == '\\')
356 {
357 switch (*(tmp+1))
358 {
359 case 'n' :
360 *tmp = '\n';
361 break;
362 case 'r' :
363 *tmp = '\r';
364 break;
365 case 't' :
366 *tmp = '\t';
367 break;
368 case '0' :
369 *tmp = '\0';
370 break;
371 default :
372 *tmp = *(tmp+1);
373 break;
374 }
375 if (!*(tmp+1))
376 break;
377 else
378 for (s = tmp; (*s = *(s+1)); s++)
379 ;
380 tmp++;
381 }
382 else if (*tmp == '#')
383 *tmp = '\0';
384 }
385 if (!*line || *line == '#' || *line == '\n' ||
386 *line == ' ' || *line == '\t')
387 continue;
388
389 if (line[1] != IRCDCONF_DELIMITER)
390 {
391 config_error(CF_ERR, CK_FILE, CK_LINE,
392 "wrong delimiter in line (%s)", line);
393 continue;
394 }
395
396 if (debugflag)
397 config_error(CF_NONE, CK_FILE, CK_LINE, "%s", line);
398
399 tmp = getfield(line);
400 if (!tmp)
401 {
402 config_error(CF_ERR, CK_FILE, CK_LINE,
403 "no field found");
404 continue;
405 }
406
407 aconf->status = CONF_ILLEGAL;
408 aconf->flags = 0L;
409
410 switch (*tmp)
411 {
412 case 'A': /* Name, e-mail address of administrator */
413 case 'a': /* of this server. */
414 aconf->status = CONF_ADMIN;
415 mandatory_found |= CONF_ADMIN;
416 break;
417 case 'B': /* bounce line */
418 case 'b':
419 aconf->status = CONF_BOUNCE;
420 break;
421 case 'C': /* Server where I should try to connect */
422 /* in case of lp failures */
423 ccount++;
424 aconf->status = CONF_CONNECT_SERVER;
425 break;
426 case 'c':
427 ccount++;
428 aconf->status = CONF_ZCONNECT_SERVER;
429 break;
430 case 'D': /* auto connect restrictions */
431 case 'd':
432 aconf->status = CONF_DENY;
433 break;
434 case 'H': /* Hub server line */
435 case 'h':
436 aconf->status = CONF_HUB;
437 break;
438 case 'i' : /* Restricted client */
439 aconf->flags |= CFLAG_RESTRICTED;
440 case 'I':
441 aconf->status = CONF_CLIENT;
442 mandatory_found |= CONF_CLIENT;
443 break;
444 case 'K': /* Kill user line on irc.conf */
445 aconf->status = CONF_KILL;
446 break;
447 case 'k':
448 aconf->status = CONF_OTHERKILL;
449 break;
450 /* Operator. Line should contain at least */
451 /* password and host where connection is */
452 case 'L': /* guaranteed leaf server */
453 case 'l':
454 aconf->status = CONF_LEAF;
455 break;
456 /* Me. Host field is name used for this host */
457 /* and port number is the number of the port */
458 case 'M':
459 case 'm':
460 aconf->status = CONF_ME;
461 mandatory_found |= CONF_ME;
462 break;
463 case 'N': /* Server where I should NOT try to */
464 case 'n': /* connect in case of lp failures */
465 /* but which tries to connect ME */
466 ++ncount;
467 aconf->status = CONF_NOCONNECT_SERVER;
468 break;
469 case 'o':
470 case 'O':
471 aconf->status = CONF_OPERATOR;
472 break;
473 case 'P': /* listen port line */
474 case 'p':
475 aconf->status = CONF_LISTEN_PORT;
476 mandatory_found |= CONF_LISTEN_PORT;
477 break;
478 case 'Q': /* a server that you don't want in your */
479 case 'q': /* network. USE WITH CAUTION! */
480 aconf->status = CONF_QUARANTINED_SERVER;
481 break;
482 case 'S': /* Service. Same semantics as */
483 case 's': /* CONF_OPERATOR */
484 aconf->status = CONF_SERVICE;
485 break;
486 case 'V':
487 aconf->status = CONF_VER;
488 break;
489 case 'Y':
490 case 'y':
491 aconf->status = CONF_CLASS;
492 break;
493 #ifdef XLINE
494 case 'X':
495 aconf->status = CONF_XLINE;
496 break;
497 #endif
498 default:
499 config_error(CF_WARN, CK_FILE, CK_LINE,
500 "unknown conf line letter (%c)\n", *tmp);
501 break;
502 }
503
504 if (IsIllegal(aconf))
505 continue;
506
507 do
508 {
509 if ((tmp = getfield(NULL)) == NULL)
510 break;
511 DupString(aconf->host, tmp);
512 if ((tmp = getfield(NULL)) == NULL)
513 break;
514 DupString(aconf->passwd, tmp);
515 if ((tmp = getfield(NULL)) == NULL)
516 break;
517 DupString(aconf->name, tmp);
518 if ((tmp = getfield(NULL)) == NULL)
519 break;
520 aconf->port = atoi(tmp);
521 if ((tmp = getfield(NULL)) == NULL)
522 break;
523 if (aconf->status & CONF_CLIENT_MASK)
524 {
525 if (!*tmp)
526 config_error(CF_WARN, CK_FILE, CK_LINE,
527 "no class, default 0");
528 aconf->class = get_class(atoi(tmp), nr);
529 }
530 tmp3 = getfield(NULL);
531 } while (0); /* to use break without compiler warnings */
532
533 if ((aconf->status & CONF_CLIENT) && tmp3)
534 {
535 /* Parse I-line flags */
536 for(s = tmp3; *s; ++s)
537 {
538 switch (*s)
539 {
540 #ifdef XLINE
541 case 'e':
542 #endif
543 case 'R':
544 case 'D':
545 case 'I':
546 case 'E':
547 case 'N':
548 case 'M':
549 case 'F':
550 break;
551 case ' ':
552 case '\t':
553 /* so there's no weird warnings */
554 break;
555 default:
556 config_error(CF_WARN, CK_FILE, CK_LINE,
557 "unknown I-line flag: %c", *s);
558 }
559 }
560 }
561 if ((aconf->status & CONF_OPERATOR) && tmp3)
562 {
563 /* Parse O-line flags */
564 for(s = tmp3; *s; ++s)
565 {
566 switch (*s)
567 {
568 case 'A':
569 case 'L':
570 case 'K':
571 case 'k':
572 case 'S':
573 case 's':
574 case 'C':
575 case 'c':
576 case 'l':
577 case 'h':
578 case 'd':
579 case 'r':
580 case 'R':
581 case 'D':
582 case 'e':
583 case 'T':
584 #ifdef CLIENTS_CHANNEL
585 case '&':
586 #endif
587 case 'p':
588 case 'P':
589 case 't':
590 break;
591 case ' ':
592 case '\t':
593 /* so there's no weird warnings */
594 break;
595 default:
596 config_error(CF_WARN, CK_FILE, CK_LINE,
597 "unknown O-line flag: %c", *s);
598 }
599 }
600 }
601
602 /*
603 ** If conf line is a class definition, create a class entry
604 ** for it and make the conf_line illegal and delete it.
605 */
606 if (aconf->status & CONF_CLASS)
607 {
608 if (!aconf->host)
609 {
610 config_error(CF_ERR, CK_FILE, CK_LINE,
611 "no class #");
612 continue;
613 }
614 if (!tmp)
615 {
616 config_error(CF_WARN, CK_FILE, CK_LINE,
617 "missing sendq field, "
618 "assuming default %d", QUEUELEN);
619 (void)sprintf(maxsendq, "%d", QUEUELEN);
620 }
621 else
622 (void)sprintf(maxsendq, "%d", atoi(tmp));
623 new_class(atoi(aconf->host));
624 aconf->class = get_class(atoi(aconf->host), nr);
625 goto print_confline;
626 }
627
628 if (aconf->status & CONF_LISTEN_PORT)
629 {
630 if (!aconf->host)
631 config_error(CF_ERR, CK_FILE, CK_LINE,
632 "null host field in P-line");
633 #ifdef UNIXPORT
634 else if (index(aconf->host, '/'))
635 {
636 struct stat sb;
637
638 if (stat(aconf->host, &sb) == -1)
639 {
640 config_error(CF_ERR, CK_FILE, CK_LINE,
641 "error in stat(%s)",
642 aconf->host);
643 }
644 else if ((sb.st_mode & S_IFMT) != S_IFDIR)
645 config_error(CF_ERR, CK_FILE, CK_LINE,
646 "%s not directory",
647 aconf->host);
648 }
649 #else
650 else if (index(aconf->host, '/'))
651 {
652 config_error(CF_WARN, CK_FILE, CK_LINE,
653 "/ present in P-line but UNIXPORT "
654 "undefined");
655 }
656 #endif
657 aconf->class = get_class(0, nr);
658 goto print_confline;
659 }
660
661 if (aconf->status & CONF_SERVER_MASK &&
662 (!aconf->host || index(aconf->host, '*') ||
663 index(aconf->host, '?')))
664 {
665 config_error(CF_ERR, CK_FILE, CK_LINE,
666 "bad host field");
667 continue;
668 }
669
670 if (aconf->status & CONF_SERVER_MASK && BadPtr(aconf->passwd))
671 {
672 config_error(CF_ERR, CK_FILE, CK_LINE,
673 "empty/no password field");
674 continue;
675 }
676
677 if (aconf->status & CONF_SERVER_MASK && !aconf->name)
678 {
679 config_error(CF_ERR, CK_FILE, CK_LINE,
680 "bad name field");
681 continue;
682 }
683
684 if (aconf->status & (CONF_SERVER_MASK|CONF_OPS))
685 if (!index(aconf->host, '@'))
686 {
687 char *newhost;
688 int len = 3; /* *@\0 = 3 */
689
690 len += strlen(aconf->host);
691 newhost = (char *)MyMalloc(len);
692 (void)sprintf(newhost, "*@%s", aconf->host);
693 (void)free(aconf->host);
694 aconf->host = newhost;
695 }
696
697 if (!aconf->class)
698 aconf->class = get_class(0, nr);
699 (void)sprintf(maxsendq, "%d", aconf->class->class);
700
701 if (!aconf->name)
702 aconf->name = nullfield;
703 if (!aconf->passwd)
704 aconf->passwd = nullfield;
705 if (!aconf->host)
706 aconf->host = nullfield;
707 if (aconf->status & CONF_ME)
708 {
709 if (flags & aconf->status)
710 config_error(CF_ERR, CK_FILE, CK_LINE,
711 "multiple M-lines");
712 else
713 flags |= aconf->status;
714 if (tmp && *tmp)
715 {
716 /* Check for SID at the end of M: line */
717 if (debugflag > 2)
718 (void)printf("SID is set to: %s\n", tmp);
719 if (checkSID(tmp, nr))
720 config_error(CF_ERR, CK_FILE, CK_LINE,
721 "SID invalid");
722 }
723 else
724 config_error(CF_ERR, CK_FILE, CK_LINE,
725 "no SID in M-line");
726 }
727 if (aconf->status & CONF_ADMIN)
728 {
729 if (flags & aconf->status)
730 config_error(CF_ERR, CK_FILE, CK_LINE,
731 "multiple A-lines");
732 else
733 flags |= aconf->status;
734 if (tmp && *tmp)
735 {
736 /* Check for Network at the end of A: line */
737 if (debugflag > 2)
738 (void)printf("Network is set to: %s\n", tmp);
739 }
740 else
741 config_error(CF_ERR, CK_FILE, CK_LINE,
742 "no network in A-line");
743 }
744
745 if (aconf->status & CONF_VER)
746 {
747 if (*aconf->host && !index(aconf->host, '/'))
748 config_error(CF_WARN, CK_FILE, CK_LINE,
749 "no / in V line");
750 else if (*aconf->passwd && !index(aconf->passwd, '/'))
751 config_error(CF_WARN, CK_FILE, CK_LINE,
752 "no / in V line");
753 }
754 print_confline:
755 if (debugflag > 8)
756 {
757 (void)printf("(%d) (%s) (%s) (%s) (%d) (%s)\n",
758 aconf->status, aconf->host, aconf->passwd,
759 aconf->name, aconf->port, maxsendq);
760 }
761 (void)fflush(stdout);
762 if (aconf->status & (CONF_SERVER_MASK|CONF_HUB|CONF_LEAF))
763 {
764 aconf->next = ctop;
765 ctop = aconf;
766 aconf = NULL;
767 }
768 }
769 #if defined(CONFIG_DIRECTIVE_INCLUDE)
770 (void)fclose(fdn);
771 #else
772 (void)close(fd);
773 #endif
774 #ifdef M4_PREPROC
775 (void)wait(0);
776 #endif
777
778 if (!(mandatory_found & CONF_ME))
779 {
780 config_error(CF_ERR, ftop, 0,
781 "no M-line found (mandatory)");
782 valid = 0;
783 }
784 if (!(mandatory_found & CONF_ADMIN))
785 {
786 config_error(CF_ERR, ftop, 0,
787 "no A-line found (mandatory)");
788 valid = 0;
789 }
790 if (!(mandatory_found & CONF_LISTEN_PORT))
791 {
792 config_error(CF_ERR, ftop, 0,
793 "no P-line found (mandatory)");
794 valid = 0;
795 }
796 if (!(mandatory_found & CONF_CLIENT))
797 {
798 config_error(CF_ERR, ftop, 0,
799 "no I-line found (mandatory)");
800 valid = 0;
801 }
802
803 if (valid)
804 {
805 return ctop;
806 }
807 return NULL;
808 }
809
get_class(int cn,int nr)810 static aClass *get_class(int cn, int nr)
811 {
812 static aClass cls;
813 int i = numclasses - 1;
814 #ifdef CONFIG_DIRECTIVE_INCLUDE
815 aConfig *filelist;
816 #else
817 struct wordcount *filelist;
818 #endif
819
820 cls.class = -1;
821 for (; i >= 0; i--)
822 if (classarr[i] == cn)
823 {
824 cls.class = cn;
825 break;
826 }
827 if (i == -1)
828 {
829 filelist = findConfLineNumber(nr);
830 config_error(CF_WARN, CK_FILE, CK_LINE,
831 "class %d not found", cn);
832 }
833 return &cls;
834 }
835
new_class(int cn)836 static void new_class(int cn)
837 {
838 numclasses++;
839 if (classarr)
840 classarr = (int *)realloc(classarr, sizeof(int) * numclasses);
841 else
842 classarr = (int *)malloc(sizeof(int));
843 classarr[numclasses-1] = cn;
844 }
845
846 /*
847 * field breakup for ircd.conf file.
848 */
getfield(char * irc_newline)849 static char *getfield(char *irc_newline)
850 {
851 static char *line = NULL;
852 char *end, *field;
853
854 if (irc_newline)
855 line = irc_newline;
856 if (line == NULL)
857 return(NULL);
858
859 field = line;
860 if ((end = (char *)index(line, IRCDCONF_DELIMITER)) == NULL)
861 {
862 line = NULL;
863 if ((end = (char *)index(field,'\n')) == NULL)
864 end = field + strlen(field);
865 }
866 else
867 line = end + 1;
868 *end = '\0';
869 return(field);
870 }
871
872 #ifndef CONFIG_DIRECTIVE_INCLUDE
873 /*
874 ** read a string terminated by \r or \n in from a fd
875 **
876 ** Created: Sat Dec 12 06:29:58 EST 1992 by avalon
877 ** Returns:
878 ** 0 - EOF
879 ** -1 - error on read
880 ** >0 - number of bytes returned (<=num)
881 ** After opening a fd, it is necessary to init dgets() by calling it as
882 ** dgets(x,y,0);
883 ** to mark the buffer as being empty.
884 */
dgets(int fd,char * buf,int num)885 static int dgets(int fd, char *buf, int num)
886 {
887 static char dgbuf[8192];
888 static char *head = dgbuf, *tail = dgbuf;
889 register char *s, *t;
890 register int n, nr;
891
892 /*
893 ** Sanity checks.
894 */
895 if (head == tail)
896 *head = '\0';
897 if (!num)
898 {
899 head = tail = dgbuf;
900 *head = '\0';
901 return 0;
902 }
903 if (num > sizeof(dgbuf) - 1)
904 num = sizeof(dgbuf) - 1;
905 dgetsagain:
906 if (head > dgbuf)
907 {
908 for (nr = tail - head, s = head, t = dgbuf; nr > 0; nr--)
909 *t++ = *s++;
910 tail = t;
911 head = dgbuf;
912 }
913 /*
914 ** check input buffer for EOL and if present return string.
915 */
916 if (head < tail &&
917 ((s = index(head, '\n')) || (s = index(head, '\r'))) && s < tail)
918 {
919 n = MIN(s - head + 1, num); /* at least 1 byte */
920 dgetsreturnbuf:
921 bcopy(head, buf, n);
922 head += n;
923 if (head == tail)
924 head = tail = dgbuf;
925 return n;
926 }
927
928 if (tail - head >= num) /* dgets buf is big enough */
929 {
930 n = num;
931 goto dgetsreturnbuf;
932 }
933
934 n = sizeof(dgbuf) - (tail - dgbuf) - 1;
935 nr = read(fd, tail, n);
936 if (nr == -1)
937 {
938 head = tail = dgbuf;
939 return -1;
940 }
941 if (!nr)
942 {
943 if (head < tail)
944 {
945 n = MIN(tail - head, num);
946 /* File hasn't got a final new line */
947 goto dgetsreturnbuf;
948 }
949 head = tail = dgbuf;
950 return 0;
951 }
952 tail += nr;
953 *tail = '\0';
954 for (t = head; (s = index(t, '\n')); )
955 {
956 if ((s > head) && (s > dgbuf))
957 {
958 t = s-1;
959 for (nr = 0; *t == '\\'; nr++)
960 t--;
961 if (nr & 1)
962 {
963 t = s+1;
964 s--;
965 nr = tail - t;
966 while (nr--)
967 *s++ = *t++;
968 tail -= 2;
969 *tail = '\0';
970 }
971 else
972 s++;
973 }
974 else
975 s++;
976 t = s;
977 }
978 *tail = '\0';
979 goto dgetsagain;
980 }
981 #endif
982
validate(aConfItem * top)983 static void validate(aConfItem *top)
984 {
985 Reg aConfItem *aconf, *bconf;
986 u_int otype = 0, valid = 0;
987 int nr;
988 #ifdef CONFIG_DIRECTIVE_INCLUDE
989 aConfig *filelist;
990 #else
991 struct wordcount *filelist;
992 #endif
993
994 if (!top)
995 return;
996
997 for (aconf = top; aconf; aconf = aconf->next)
998 {
999 if (aconf->status & CONF_MATCH)
1000 continue;
1001
1002 if (aconf->status & CONF_SERVER_MASK)
1003 {
1004 if (aconf->status & (CONF_CONNECT_SERVER|CONF_ZCONNECT_SERVER))
1005 otype = CONF_NOCONNECT_SERVER;
1006 else if (aconf->status & CONF_NOCONNECT_SERVER)
1007 otype = CONF_CONNECT_SERVER;
1008
1009 for (bconf = top; bconf; bconf = bconf->next)
1010 {
1011 if (bconf == aconf || !(bconf->status & otype))
1012 continue;
1013 if (bconf->class == aconf->class &&
1014 !mycmp(bconf->name, aconf->name) &&
1015 !mycmp(bconf->host, aconf->host))
1016 {
1017 aconf->status |= CONF_MATCH;
1018 bconf->status |= CONF_MATCH;
1019 break;
1020 }
1021 }
1022 }
1023 else
1024 for (bconf = top; bconf; bconf = bconf->next)
1025 {
1026 if ((bconf == aconf) ||
1027 !(bconf->status & CONF_SERVER_MASK))
1028 continue;
1029 if (!mycmp(bconf->name, aconf->name))
1030 {
1031 aconf->status |= CONF_MATCH;
1032 break;
1033 }
1034 }
1035 }
1036
1037 for (aconf = top; aconf; aconf = aconf->next)
1038 if (aconf->status & CONF_MATCH)
1039 valid++;
1040 else
1041 {
1042 nr = aconf->clients;
1043 filelist = findConfLineNumber(nr);
1044 config_error(CF_WARN, CK_FILE, CK_LINE,
1045 "unmatched %c%c%s%c%s%c%s",
1046 confchar(aconf->status), IRCDCONF_DELIMITER,
1047 aconf->host, IRCDCONF_DELIMITER,
1048 SHOWSTR(aconf->passwd), IRCDCONF_DELIMITER,
1049 aconf->name);
1050 }
1051 return;
1052 }
1053
1054 #ifdef M4_PREPROC
simulateM4Include(struct wordcount * filelist,int nr,char * filename,int fnrmin)1055 static int simulateM4Include(struct wordcount *filelist, int nr, char *filename, int fnrmin)
1056 {
1057 int fd, dh, fnr = 0, finalnewline = 0;
1058 char line[512];
1059 struct wordcount *listnew;
1060 #ifdef M4_PREPROC
1061 char *inc;
1062 #endif
1063
1064 listnew = (struct wordcount *)malloc(sizeof(struct wordcount));
1065 filelist->next = listnew;
1066 filelist = filelist->next;
1067 filelist->min = nr - fnrmin;
1068 filelist->filename = strdup(filename);
1069 filelist->max = 0;
1070 filelist->next = NULL;
1071 if ((fd = open(filename, O_RDONLY)) == -1)
1072 {
1073 perror(filename);
1074 return nr;
1075 }
1076 (void)dgets(-1, NULL, 0);
1077 while ((dh = dgets(fd, line, sizeof(line) - 1)) > 0)
1078 {
1079 if (dh != sizeof(line) - 1)
1080 {
1081 fnr++;
1082 if (fnr > fnrmin)
1083 {
1084 nr++;
1085 if (line[dh - 1] != '\n')
1086 {
1087 config_error(CF_WARN, CK_FILE, CK_LINE,
1088 "no final new line");
1089 finalnewline = 0;
1090 } else
1091 finalnewline = 1;
1092 line[dh - 1] = 0;
1093 #ifdef M4_PREPROC
1094 inc = mystrinclude(line, nr);
1095 if (inc)
1096 {
1097 filelist->max = --nr;
1098 nr = simulateM4Include(filelist, nr, inc, 0);
1099 while (filelist->next) filelist = filelist->next;
1100 nr = simulateM4Include(filelist, nr, filename, fnr);
1101 break;
1102 }
1103 #endif
1104 }
1105 }
1106 else
1107 if (fnr > fnrmin)
1108 {
1109 line[dh - 1] = 0;
1110
1111 config_error(CF_WARN, CK_FILE, CK_LINE,
1112 "line (%s) too long, maxlen %d",
1113 line, sizeof(line));
1114 }
1115 }
1116 (void)close(fd);
1117 #ifdef M4_PREPROC
1118 (void)wait(0);
1119 #endif
1120 nr += finalnewline;
1121 if (!filelist->max) filelist->max = nr;
1122 return nr;
1123 }
1124 #endif
1125
1126 #ifndef CONFIG_DIRECTIVE_INCLUDE
mywc(void)1127 static void mywc(void)
1128 {
1129 int fd, dh, nr = 0;
1130 char line[512];
1131 struct wordcount *listtmp;
1132 #ifdef M4_PREPROC
1133 char *inc;
1134 #endif
1135
1136 /* Dealing with ircd.m4 */
1137 files = listtmp = (struct wordcount *)malloc(sizeof(struct wordcount));
1138 listtmp->min = 0;
1139 #ifdef M4_PREPROC
1140 listtmp->filename = strdup(IRCDM4_PATH);
1141 inc = configfile;
1142 configfile = 0; /* used to have openconf launch only m4 ircd.m4 */
1143 #else
1144 listtmp->filename = strdup("ircd.conf");
1145 #endif
1146 if ((fd = openconf()) == -1)
1147 {
1148 (void)wait(0);
1149 return;
1150 }
1151 (void)dgets(-1, NULL, 0);
1152 /* We only count lines. No include in ircd.m4 if M4 defined */
1153 while ((dh = dgets(fd, line, sizeof(line) - 1)) > 0)
1154 if (dh != sizeof(line) - 1)
1155 nr++;
1156 listtmp->max = nr;
1157 (void)close(fd);
1158 #ifdef M4_PREPROC
1159 (void)wait(0);
1160 configfile = inc;
1161 nr = simulateM4Include(listtmp, nr, configfile, 0);
1162 #endif
1163 }
1164 #endif /* CONFIG_DIRECTIVE_INCLUDE */
1165
confchar(u_int status)1166 static char confchar(u_int status)
1167 {
1168 static char letrs[] = "QIicNCoOMKARYSLPHV";
1169 char *s = letrs;
1170
1171 status &= ~(CONF_MATCH|CONF_ILLEGAL);
1172
1173 for (; *s; s++, status >>= 1)
1174 if (status & 1)
1175 return *s;
1176 return '-';
1177 }
1178
checkSID(char * sid,int nr)1179 static int checkSID(char *sid, int nr)
1180 {
1181 char *s = sid;
1182 int len = 0, rc = 0;
1183 #ifdef CONFIG_DIRECTIVE_INCLUDE
1184 aConfig *filelist;
1185 #else
1186 struct wordcount *filelist;
1187 #endif
1188
1189 /* First SID char shall be numeric, rest must be alpha */
1190 filelist = findConfLineNumber(nr);
1191 if (!isdigit(*s))
1192 {
1193 config_error(CF_ERR, CK_FILE, CK_LINE,
1194 "SID %s invalid: 1st character must be number", sid);
1195 ++rc;
1196 }
1197 ++s; ++len;
1198 for (; *s; ++s, ++len)
1199 if (!isalnum(*s))
1200 {
1201 config_error(CF_ERR, CK_FILE, CK_LINE,
1202 "SID %s invalid: wrong character (%c)",
1203 sid, *s);
1204 ++rc;
1205 }
1206 if (!(len == SIDLEN))
1207 {
1208 config_error(CF_ERR, CK_FILE, CK_LINE,
1209 "SID %s invalid: wrong size (%d should be %d)",
1210 sid, len, SIDLEN);
1211 ++rc;
1212 }
1213 return rc;
1214 }
1215
1216 #ifdef M4_PREPROC
1217 /* Do: s/include\((.*)\)/\1/
1218 * and checks that it's not "commented out" */
mystrinclude(char * s,int nr)1219 static char * mystrinclude(char *s, int nr)
1220 {
1221 int off;
1222 struct wordcount *filelist;
1223 char *match = strstr(s, "include(");
1224
1225 if (!match)
1226 return 0;
1227 off = match - s;
1228 for(; s < match; ++s)
1229 if (!isspace(*s))
1230 return 0;
1231 s += 8; /* length("include(") */
1232 for(; match && *match != ')'; ++match);
1233 *match = '\0';
1234
1235 if (off > 0)
1236 {
1237 filelist = findConfLineNumber(nr);
1238 config_error(CF_WARN, CK_FILE, CK_LINE,
1239 "spaces before include(%s): first line of included "
1240 "file will be shifted", s);
1241 }
1242 return s;
1243 }
1244 #endif
1245
1246 #ifdef CONFIG_DIRECTIVE_INCLUDE
findConfLineNumber(int nr)1247 static aConfig *findConfLineNumber(int nr)
1248 {
1249 int mynr = 1;
1250 aConfig *p = files;
1251
1252 for( ; p->next && mynr < nr; p = p->next)
1253 mynr++;
1254 return p;
1255 }
1256 #else
1257 /* Locates file and line number of a config line */
findConfLineNumber(int nr)1258 static struct wordcount * findConfLineNumber(int nr)
1259 {
1260 struct wordcount *filelist = files;
1261 for(; filelist->next && (filelist->max < nr); filelist = filelist->next);
1262 return filelist;
1263 }
1264 #endif
1265