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