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