1 /**********************************************************
2  * Config parser.
3  **********************************************************/
4 /*
5  * $Id: config.c,v 1.10 2005/08/23 16:51:29 mitry Exp $
6  *
7  * $Log: config.c,v $
8  * Revision 1.10  2005/08/23 16:51:29  mitry
9  * Stop running if can't reread config
10  *
11  * Revision 1.9  2005/08/11 19:15:14  mitry
12  * Changed code a bit
13  *
14  * Revision 1.8  2005/05/16 11:17:30  mitry
15  * Updated function prototypes. Changed code a bit.
16  *
17  * Revision 1.7  2005/05/11 13:22:40  mitry
18  * Snapshot
19  *
20  * Revision 1.6  2005/03/28 17:02:52  mitry
21  * Pre non-blocking i/o update. Mostly non working.
22  *
23  * Revision 1.5  2005/02/09 11:14:09  mitry
24  * Add support to escape next character with backslash (\)
25  *
26  *
27  */
28 
29 #include "headers.h"
30 #include "nodelist.h"
31 
32 static aslist_t		*defs = NULL;
33 static slist_t		*condlist = NULL, *curcond;
34 
35 #define IS_COMMENT( t ) ( *t == ';' || *t == '#' || ( *t== '/' && t[1] == '/' ))
36 
37 
getstr(char ** to,const char * from)38 static int getstr(char **to, const char *from)
39 {
40 	*to = xstrdup( from );
41 	return 1;
42 }
43 
44 
getpath(char ** to,const char * from)45 static int getpath(char **to, const char *from)
46 {
47 	if ( strcspn( from, "*[]?<>|" ) != strlen( from ))
48 		return 0;
49 	if ( from[strlen( from ) - 1] == '/' )
50 		chop( (char *) from, 1 );
51 	*to = xstrdup( from );
52 	return 1;
53 }
54 
55 
getlong(int * to,const char * from)56 static int getlong(int *to, const char *from)
57 {
58 	if ( strspn( from, "0123456789 \t" ) != strlen( from ))
59 		return 0;
60 	*to = atol( from );
61 	return 1;
62 }
63 
64 
getoct(int * to,const char * from)65 static int getoct(int *to, const char *from)
66 {
67 	if ( strspn( from, "01234567 \t" ) != strlen( from ))
68 		return 0;
69 	*to = strtol( from, (char**) NULL, 8 );
70 	return 1;
71 }
72 
73 
getaddrl(falist_t ** to,const char * from)74 static int getaddrl(falist_t **to, const char *from)
75 {
76 	FTNADDR_T(ta);
77 
78 	if ( !parseftnaddr( from, &ta, &DEFADDR, 0 ))
79 		return 0;
80 	if ( !DEFADDR.z ) {
81 		DEFADDR.z = ta.z;
82 		DEFADDR.n = ta.n;
83 	}
84 	falist_add( to, &ta );
85 	return 1;
86 }
87 
88 
getfasl(faslist_t ** to,const char * from)89 static int getfasl(faslist_t **to, const char *from)
90 {
91 	char		*p;
92 	FTNADDR_T	(ta);
93 
94 	if ( !parseftnaddr( from, &ta, &DEFADDR, 0 ))
95 		return 0;
96 	if ( !DEFADDR.z ) {
97 		DEFADDR.z = ta.z;
98 		DEFADDR.n = ta.n;
99 	}
100 	p = strchr( from, ' ' );
101 	if ( !p )
102 		return 0;
103 	p = skip_blanks( p );
104 	faslist_add( to, p, &ta );
105 	return 1;
106 }
107 
108 
getyesno(int * to,const char * from)109 static int getyesno(int *to, const char *from)
110 {
111 	switch( tolower( *from )) {
112 	case 'y':
113 	case 't':
114 	case '1':
115 		*to = 1;
116 		break;
117 
118 	default:
119 		*to = 0;
120 	}
121 	return 1;
122 }
123 
124 
getstrl(slist_t ** to,const char * from)125 static int getstrl(slist_t **to, const char *from)
126 {
127 	slist_add( to, from );
128 	return 1;
129 }
130 
131 
setvalue(cfgitem_t * ci,const char * t,int type)132 static int setvalue(cfgitem_t *ci, const char *t, int type)
133 {
134 	switch( type ) {
135 	case C_STR:
136 		return getstr( &ci->value.v_char, t );
137 	case C_PATH:
138 		return getpath( &ci->value.v_char, t );
139 	case C_STRL:
140 		return getstrl( &ci->value.v_sl, t );
141 	case C_ADRSTRL:
142 		return getfasl( &ci->value.v_fasl, t );
143 	case C_ADDRL:
144 		return getaddrl( &ci->value.v_al, t );
145 	case C_INT:
146 		return getlong( &ci->value.v_int, t );
147 	case C_OCT:
148 		return getoct( &ci->value.v_int, t );
149 	case C_YESNO:
150 		return getyesno( &ci->value.v_int, t );
151 	}
152 	return 0;
153 }
154 
155 
cfgi(int i)156 int cfgi(int i)
157 {
158 	cfgitem_t *ci, *cn = NULL;
159 
160 	for( ci = configtab[i].items; ci; ci = ci->next ) {
161 		if ( ci->condition && flagexp( ci->condition, 0 ) == 1 )
162 			return cci = ci->value.v_int;
163 		if ( !ci->condition )
164 			cn = ci;
165 	}
166 	return cci = cn->value.v_int;
167 }
168 
169 
cfgs(int i)170 char *cfgs(int i)
171 {
172 	cfgitem_t *ci, *cn = NULL;
173 
174 	for( ci = configtab[i].items; ci; ci = ci->next ) {
175 		if ( ci->condition && flagexp( ci->condition, 0 ))
176 			return ccs = ci->value.v_char;
177 		if ( !ci->condition )
178 			cn = ci;
179 	}
180 	return ccs = cn->value.v_char;
181 }
182 
183 
cfgsl(int i)184 slist_t *cfgsl(int i)
185 {
186 	cfgitem_t *ci, *cn = NULL;
187 
188 	for ( ci = configtab[i].items; ci; ci = ci->next ) {
189 		if ( ci->condition && flagexp( ci->condition, 0 ))
190 			return ccsl = ci->value.v_sl;
191 		if ( !ci->condition )
192 			cn = ci;
193 	}
194 	return ccsl = cn->value.v_sl;
195 }
196 
197 
cfgfasl(int i)198 faslist_t *cfgfasl(int i)
199 {
200 	cfgitem_t *ci, *cn = NULL;
201 
202 	for ( ci = configtab[i].items; ci; ci = ci->next ) {
203 		if ( ci->condition && flagexp( ci->condition, 0 ))
204 			return ccfasl = ci->value.v_fasl;
205 		if ( !ci->condition )
206 			cn = ci;
207 	}
208 	return ccfasl = cn->value.v_fasl;
209 }
210 
211 
cfgal(int i)212 falist_t *cfgal(int i)
213 {
214 	cfgitem_t *ci, *cn = NULL;
215 
216 	for ( ci = configtab[i].items; ci; ci = ci->next ) {
217 		if ( ci->condition && flagexp( ci->condition, 0 ))
218 			return ccal = ci->value.v_al;
219 		if ( !ci->condition )
220 			cn = ci;
221 	}
222 	return ccal = cn->value.v_al;
223 }
224 
225 
parsekeyword(const char * kw,const char * arg,const char * cfgname,int line)226 static int parsekeyword(const char *kw, const char *arg, const char *cfgname, int line)
227 {
228 	int		i = 0, rc = 1;
229 	slist_t		*cc;
230 	cfgitem_t	*ci;
231 
232 	while( configtab[i].keyword && strcasecmp( configtab[i].keyword, kw ))
233 		i++;
234 
235 	DEBUG(('C',2,"parse: '%s', '%s' [%d] on %s:%d%s%s",
236 		kw,arg,i,cfgname,line,curcond?" (c) if ":"",curcond?curcond->str:""));
237 
238 	if ( !configtab[i].keyword ) {
239 		write_log( "%s:%d: unknown keyword '%s'", cfgname, line, kw );
240 		return 0;
241 	}
242 
243         if ( curcond && ( configtab[i].flags & 2 )) {
244 		write_log( "%s:%d: keyword '%s' can't be defined inside if-expression",
245 			cfgname, line, kw );
246 		return 0;
247 	}
248 
249         for ( ci = configtab[i].items; ci; ci = ci->next ) {
250 		slist_t *a = ci->condition, *b = curcond;
251 
252 		for(; a && b && a->str == b->str; a = a->next, b = b->next );
253 
254 		if ( !a && !b )
255 			break;
256 	}
257 
258 	if ( !ci ) {
259 		ci = (cfgitem_t *) xcalloc( 1, sizeof( cfgitem_t ));
260 		for( cc = curcond; cc; cc = cc->next )
261 			slist_addl( &ci->condition, cc->str );
262 		ci->next = configtab[i].items;
263 		configtab[i].items = ci;
264         }
265 
266 	if ( setvalue( ci, arg, configtab[i]. type )) {
267 		if ( !( configtab[i].flags & 8 )) {
268 			if ( !curcond )
269 				configtab[i].flags |= 8;
270 			else
271 				configtab[i].flags |= 4;
272 		}
273 	} else {
274 		xfree( ci );
275 		write_log( "%s:%d: can't parse '%s %s'", cfgname, line, kw, arg );
276 		rc=0;
277 	}
278 	return rc;
279 }
280 
281 
282 /**
283   Reads in at most buflen - 1 characters from stream f and stores them
284   into the buffer pointed to by buf.
285 
286   Returns:
287       On success - the number of characters read
288       0 if line is comment
289      -1 if error occured.
290 
291 */
xfgets(char * buf,size_t buflen,FILE * f)292 static int xfgets(char *buf, size_t buflen, FILE *f)
293 {
294 	char	*str, *p, *c;
295 	int	rc = 0;
296 
297 	str = xmalloc( buflen + 1 );
298 
299 	if ( fgets( str, buflen, f ) == NULL ) {
300 		*buf = '\0';
301 		xfree( str );
302 		return -1;  /* error? */
303 	}
304 
305 	p = skip_blanks( str );
306 	if ( *p && IS_COMMENT( p ) ) { /* comment, skip line */
307 		xfree( str );
308 		return 0;
309 	}
310 
311 	c = p;
312 	while( *c ) {
313 		if ( *c == '\\' && !XBLANK( c + 1 ))
314 			xstrcpy( c, c + 1, strlen( c ));
315 		else if ( IS_COMMENT( c )) {
316 			*c = 0;
317 			break;
318 		}
319 		c++;
320 	}
321 	strtr( p, '\t', ' ');
322 	skip_blanksr( p );
323 	xstrcpy( buf, p, buflen );
324 	rc = strlen( p );
325 	xfree( str );
326 
327 	return rc;
328 }
329 
330 
parseconfig(const char * cfgname)331 static int parseconfig(const char *cfgname)
332 {
333     FILE *f;
334     char s[LARGE_STRING + 1] = {0}, *p, *t, *k;
335     int line = 0, rc = 1, xrc = 0;
336     slist_t *cc;
337 
338     if ( !( f = fopen( cfgname, "r" ))) {
339         write_log( "can't open config file '%s': %s", cfgname, strerror( errno ));
340         return 0;
341     }
342 
343     if ( verbose_config )
344         printf( "***** %s\n", cfgname );
345 
346     while(( xrc = xfgets( s, LARGE_STRING, f )) >= 0 ) {
347         line++;
348 
349         if ( xrc == 0 )
350             continue;
351 
352 contl:
353         p = s;
354 
355         if ( verbose_config )
356             printf( "** cfg line: %d: '%s', len = %-4d\n", line, p, strlen( s ));
357 
358         t = p + strlen( p ) - 1;
359 
360         if ( *t == '\\' && t > p && *( t - 1 ) == ' ' ) {
361             do {
362                 line++;
363             } while(( xrc = xfgets( t, LARGE_STRING - ( t - s ), f )) == 0 );
364             goto contl;
365         }
366 
367         if ( *p != '{' ) {
368             if ( *p == '$' && isgraph( p[1] ))
369                 if (( k = strchr( p, '=' )))
370                     *k = ' ';
371             t = strchr( p, ' ' );
372             /*
373             if ( !t )
374                 t = strchr( p, '\n' );
375             if ( !t )
376                 t = strchr( p, '\r' );
377             */
378             if( !t )
379                 t = strchr( p, 0 );
380             *t++ = 0;
381         } else
382             t = p + 1;
383 
384 contd:
385         t = skip_blanks( t );
386 
387         if (( k = strchr( t, '$' )) && k[1] == '(' ) {
388             char *e = (k + 2);
389             char *en = NULL;
390             aslist_t *as;
391 
392             for(; *e && isgraph( *e ) && *e != ')'; e++ );
393 
394             if ( *e == ')' ) {
395                 *e++ = 0;
396                 as = aslist_find( defs, k + 2 );
397 
398                 if ( as == NULL && ( en = getenv( k + 2 ))) {
399                     aslist_add( &defs, k + 2, en );  /* Add env define */
400                     as = aslist_find( defs, k + 2 );
401                 }
402 
403                 if ( as ) {
404                     if ( verbose_config )
405                         printf( "** %s define: '%s'->'%s'\n",
406                             ( en ? "env" : "cfg" ), k + 2, as->arg );
407                     e = xstrdup( e );
408                     xstrcpy( k, as->arg, LARGE_STRING - ( k - s ));
409                     if ( *e )
410                         xstrcat( k, e, LARGE_STRING - ( k - s ));
411                     xfree( e );
412                     if ( verbose_config )
413                         printf( "** cfg full line: '%s %s'\n", p, t );
414                 } else {
415                     write_log( "%s:%d: use of uninitialized define '$%s'", cfgname, line, k + 2);
416                     rc = 0;
417                     *k = 0;
418                 }
419                 goto contd;
420             }
421         }
422 
423         if ( *p== '$' && isgraph( p[1] ))  /* Found new define */
424             aslist_add( &defs, p + 1, t );
425         else if ( !strcasecmp( p, "include" )) {
426             if(!strncmp(cfgname,t,MAX_STRING)) {
427                 write_log("%s:%d: <include> including itself -> infinity loop",cfgname,line);
428                 rc=0;
429             } else if(!parseconfig(t)) {
430                 write_log("%s:%d: was errors parsing included file '%s'",cfgname,line,t);
431                 rc=0;
432             }
433         } else if(*p=='{'||!strcasecmp(p,"if")) {
434             for(k=t;*k&&(*k!=':'||k[1]!=' ');k++);
435             if(*k==':'&&k[1]==' ') {
436                 *k++=0;
437                 if(*(k-2)==' ')*(k-2)=0;
438                 while(*k==' ')k++;
439                 for(p=k;*p&&*p!=' ';p++);
440                 *p++=0;
441                 while(*p==' ')p++;
442                 if(!*p) {
443                     write_log("%s:%d: inline <if>-expression witout arguments",cfgname,line);
444                     rc=0;k=NULL;
445                 }
446                 if(*(p+strlen(p)-1)=='}')*(p+strlen(p)-1)=0;
447             } else k=NULL;
448             cc=slist_add(&condlist,t);
449             if(flagexp(cc,1)<0) {
450                 write_log("%s:%d: can't parse expression '%s'",cfgname,line,t);
451                 rc=0;
452             } else slist_addl(&curcond,cc->str);
453             if(k&&curcond) {
454                 if(!parsekeyword(k,p,cfgname,line))rc=0;
455                 slist_dell(&curcond);
456             }
457         } else if(!strcasecmp(p,"else")) {
458             if(!curcond) {
459                 write_log("%s:%d: misplaced <else> without <if>",cfgname,line);
460                 rc=0;
461             } else {
462                 k=slist_dell(&curcond);
463                 snprintf(s,LARGE_STRING,"not(%s)",k);
464                 cc=slist_add(&condlist,s);
465                 slist_addl(&curcond,cc->str);
466             }
467         } else if(*p=='}'||!strcasecmp(p,"endif")) {
468             if(!curcond) {
469                 write_log("%s:%d: misplaced <endif> without <if>",cfgname,line);
470                 rc=0;
471             } else slist_dell(&curcond);
472         } else if(!parsekeyword(p,t,cfgname,line))rc=0;
473     }
474 
475     fclose( f );
476     return rc;
477 }
478 
479 
readconfig(const char * cfgname)480 int readconfig(const char *cfgname)
481 {
482 	int		rc, i;
483 	cfgitem_t	*ci;
484 
485 	curcond = NULL;
486 	rc = parseconfig( cfgname );
487     aslist_kill(&defs);
488     if(curcond) {
489         write_log("%s: not all <if>-expressions closed",cfgname);
490         while(curcond)slist_dell(&curcond);
491         rc=0;
492     }
493     if(!rc)return 0;
494     for(i=0;i<CFG_NNN;i++)
495         if(!(configtab[i].flags&8)) {
496         if(configtab[i].flags&1) {
497             write_log("required keyword '%s' not defined",configtab[i].keyword);
498             rc=0;
499         }
500         ci=xcalloc(1,sizeof(cfgitem_t));
501         if(configtab[i].def_val)
502             setvalue(ci,configtab[i].def_val,configtab[i].type);
503         else memset(&ci->value,0,sizeof(ci->value));
504         ci->condition=NULL;ci->next=NULL;
505         if(configtab[i].flags&12) {
506             cfgitem_t *cia=configtab[i].items;
507             for(;cia&&cia->next;cia=cia->next);
508             if(cia)cia->next=ci;
509         }
510         if(!configtab[i].items)configtab[i].items=ci;
511     }
512     if(rc) { /* read tables */
513         recode_to_local(NULL);
514         recode_to_remote(NULL);
515     }
516     return rc;
517 }
518 
519 
rereadconfig(int client)520 void rereadconfig(int client)
521 {
522     outbound_done();
523     log_done();
524     killsubsts( &psubsts );
525     killconfig();
526 
527     if ( !readconfig( configname )) {
528         if ( client >= 0 )
529             sendrpkt( 1, client, "Bad config, terminated");
530         write_log( "There were some errors parsing config, exiting" );
531         exit( S_FAILURE );
532     }
533 
534     if( !log_init( cfgs( CFG_MASTERLOG ), NULL )) {
535         write_log( "can't open master log '%s', aborting.", ccs );
536         exit( S_FAILURE );
537     }
538 
539     if ( outbound_init( cfgs( CFG_ASOOUTBOUND ), cfgs( CFG_BSOOUTBOUND ),
540         cfgs( CFG_QSTOUTBOUND ), cfgal( CFG_ADDRESS )->addr.z ) == 0 ) {
541 
542         write_log("No outbounds defined");
543         exit( S_FAILURE );
544     }
545 
546     psubsts = parsesubsts( cfgfasl( CFG_SUBST ));
547 
548 #ifdef NEED_DEBUG
549     parse_log_levels();
550 
551     if ( facilities_levels['C'] >= 8 )
552         dumpconfig();
553 #endif
554 
555     IFPerl( perl_on_reload( 0 ));
556     do_rescan = 1;
557 }
558 
559 
killconfig(void)560 void killconfig(void)
561 {
562     int i;
563     cfgitem_t *c,*t;
564     slist_kill(&condlist);
565     for(i=0;i<CFG_NNN;i++) {
566         for(c=configtab[i].items;c;c=t) {
567             switch(configtab[i].type) {
568                 case C_PATH:
569                 case C_STR:
570                 xfree(c->value.v_char);
571                 break;
572                 case C_STRL:
573                 slist_kill(&c->value.v_sl);
574                 break;
575                 case C_ADRSTRL:
576                 faslist_kill(&c->value.v_fasl);
577                 break;
578                 case C_ADDRL:
579                 falist_kill(&c->value.v_al);
580                 break;
581                 case C_OCT:
582                 case C_INT:
583                 case C_YESNO:
584                 c->value.v_int=0;
585                 break;
586             }
587             slist_killn(&c->condition);
588             t=c->next;
589             xfree(c);
590         }
591         configtab[i].items=NULL;
592         configtab[i].flags=0;
593     }
594 }
595 
596 
597 #ifdef NEED_DEBUG
dumpconfig(void)598 void dumpconfig(void)
599 {
600 	int		i;
601 	char		buf[LARGE_STRING + 5];
602 	cfgitem_t	*c;
603 	slist_t		*sl;
604 	falist_t	*al;
605 	faslist_t	*fasl;
606 	subst_t		*s;
607 	dialine_t	*l;
608 
609 	for( i = 0; i < CFG_NNN; i++ ) {
610 		write_log( "conf: %s. (type=%d, flags=%d)", configtab[i].keyword,
611 			configtab[i].type, configtab[i].flags );
612 		for( c = configtab[i].items; c; c = c->next ) {
613 			xstrcpy( buf, "conf:   ", LARGE_STRING );
614 			if ( c->condition ) {
615 				xstrcat( buf, "if (", LARGE_STRING );
616 				for( sl = c->condition; sl; sl = sl->next ) {
617 					xstrcat( buf, sl->str, LARGE_STRING );
618 					if ( sl->next )
619 						xstrcat( buf, ") and (", LARGE_STRING );
620 				}
621 				xstrcat( buf, "): ", LARGE_STRING );
622 			} else
623 				xstrcat( buf, "default: ", LARGE_STRING );
624 
625 			switch( configtab[i].type ) {
626 			case C_PATH:
627 			case C_STR:
628 				snprintf( buf + strlen( buf ), LARGE_STRING, "'%s'", c->value.v_char );
629 				break;
630 			case C_INT:
631 				snprintf( buf + strlen( buf ), LARGE_STRING, "%d", c->value.v_int );
632 				break;
633 			case C_OCT:
634 				snprintf( buf + strlen( buf ), LARGE_STRING, "%o", c->value.v_int );
635 				break;
636 			case C_YESNO:
637 				snprintf( buf + strlen( buf ), LARGE_STRING, "%s", c->value.v_int ? "yes" : "no" );
638 				break;
639 			case C_STRL:
640 				for( sl = c->value.v_sl; sl; sl = sl->next )
641 					snprintf( buf + strlen( buf ), LARGE_STRING, "'%s', ", sl->str );
642 				xstrcat( buf, "%", LARGE_STRING );
643 				break;
644 			case C_ADRSTRL:
645 				for( fasl = c->value.v_fasl; fasl; fasl = fasl->next )
646 					snprintf( buf + strlen( buf ), LARGE_STRING, "%s '%s', ",
647 						fasl->addr.d ? ftnaddrtoda( &fasl->addr ) : ftnaddrtoa( &fasl->addr ),
648 						fasl->str );
649 				xstrcat( buf, "%", LARGE_STRING );
650 				break;
651 			case C_ADDRL:
652 				for( al = c->value.v_al; al; al = al->next )
653 					snprintf( buf + strlen( buf ), LARGE_STRING, "%s, ",
654 						al->addr.d ? ftnaddrtoda( &al->addr ) : ftnaddrtoa( &al->addr ));
655 				xstrcat( buf, "%", LARGE_STRING );
656 				break;
657 			}
658 			write_log("%s",buf);
659 		}
660 	}
661 
662 	for( s = psubsts; s; s = s->next ) {
663 		write_log( "subst for %s [%d]", ftnaddrtoa( &s->addr ), s->nhids );
664 		for( l = s->hiddens; l; l = l->next )
665 			write_log( " * %s,%s,%s,%d,%d", l->phone, l->host, l->timegaps,
666 				l->flags, l->num );
667 	}
668 
669 }
670 #endif
671