Import release 0.03
[secnet] / conffile.c
CommitLineData
2fe58dfd
SE
1/*
2 * $Log$
3 */
4
5/* conffile.c - process the configuration file */
6
7/* #define DUMP_PARSE_TREE */
8
9#include <stdio.h>
10#include <stdlib.h>
11#include <string.h>
12
13#include "secnet.h"
14#include "conffile.h"
15#include "conffile_internal.h"
16#include "util.h"
17#include "modules.h"
18
19static struct cloc no_loc={"none",0};
20
21struct atomlist {
22 struct atomlist *next;
23 atom_t a;
24};
25
26struct entry {
27 struct entry *next;
28 atom_t key;
29 list_t *val;
30};
31
32struct searchlist {
33 struct dict *d;
34 struct searchlist *next;
35};
36
37struct dict {
38 struct dict *parent;
39 struct searchlist *search;
40 struct entry *entries;
41 uint32_t size;
42};
43
44static struct atomlist *atoms=NULL;
45
46static void process_alist(dict_t *context, struct p_node *c);
47static list_t *process_invocation(dict_t *context, struct p_node *i);
48
49static list_t *dict_ilookup_primitive(dict_t *dict, atom_t key)
50{
51 struct entry *i;
52 for (i=dict->entries; i; i=i->next) {
53 if (key==i->key) return i->val;
54 }
55 return NULL;
56}
57
58static list_t *dict_ilookup(dict_t *dict, atom_t key)
59{
60 dict_t *d;
61 list_t *v;
62
63 v=dict_ilookup_primitive(dict, key);
64 if (v) return v;
65 /* Check dictionaries in search path */
66/* XXX */
67 /* Check lexical parents */
68 for (d=dict; d; d=d->parent) {
69 v=dict_ilookup_primitive(d, key);
70 if (v) return v;
71 }
72 return NULL;
73}
74
75static void dict_iadd(dict_t *dict, atom_t key, list_t *val)
76{
77 struct entry *e;
78 /* XXX May want to permit redefinition of keys in the future */
79 /* (although it could be very confusing) */
80 if (dict_ilookup_primitive(dict, key)) {
81 fatal("duplicate key \"%s\" in dictionary\n",key);
82 }
83 e=safe_malloc(sizeof(*e),"dict_add");
84 e->next=dict->entries;
85 e->key=key;
86 e->val=val;
87 dict->entries=e;
88 dict->size++;
89}
90
91/***** Functions beyond this point are private to the config system *****/
92
93static dict_t *dict_new(dict_t *parent)
94{
95 dict_t *d;
96
97 d=safe_malloc(sizeof(*d),"dict_new");
98 d->parent=parent;
99 d->search=NULL;
100 d->entries=NULL;
101 d->size=0;
102 return d;
103}
104
105static struct p_node *node_copy(struct p_node *n)
106{
107 struct p_node *r;
108 r=safe_malloc(sizeof(*r),"node_copy");
109 *r=*n;
110 return r;
111}
112
113static struct p_node *list_reverse(struct p_node *list)
114{
115 struct p_node *rl=NULL, *i, *n;
116
117 for (i=list; i; i=i->r) {
118 n=node_copy(i);
119 n->r=rl;
120 rl=n;
121 }
122 return rl;
123}
124
125/* Since we use left-recursion in the parser for efficiency, sequences
126 end up "backwards" in the parse tree. Rather than have complicated
127 code for, eg. processing assignments in the right order, we reverse
128 these sequences here. */
129static void ptree_mangle(struct p_node *t)
130{
131 if (!t) return;
132 ptree_mangle(t->l);
133 ptree_mangle(t->r);
134 switch (t->type) {
135 case T_DICT:
136 /* ASSERT !t->l || t->l->type==T_ALIST */
137 /* ASSERT !t->r || t->r->type==T_LISTITEM */
138 t->l=list_reverse(t->l);
139 t->r=list_reverse(t->r);
140 break;
141 case T_ASSIGNMENT:
142 /* ASSERT t->l->type==T_KEY */
143 /* ASSERT t->r->type==T_LISTITEM */
144 t->r=list_reverse(t->r);
145 break;
146 case T_ABSPATH:
147 case T_RELPATH:
148 /* ASSERT t->l==NULL */
149 /* ASSERT t->r->type==T_PATHELEM */
150 t->r=list_reverse(t->r);
151 break;
152 case T_EXEC:
153 /* ASSERT t->l */
154 /* ASSERT t->r->type==T_LISTITEM */
155 t->r=list_reverse(t->r);
156 break;
157 }
158}
159
160#ifdef DUMP_PARSE_TREE
161/* Convert a node type to a string, for parse tree dump */
162static string_t ntype(uint32_t type)
163{
164 switch(type) {
165 case T_STRING: return "T_STRING";
166 case T_NUMBER: return "T_NUMBER";
167 case T_KEY: return "T_KEY";
168 case T_ASSIGNMENT: return "T_ASSIGNMENT";
169 case T_LISTITEM: return "T_LISTITEM";
170 case T_EXEC: return "T_EXEC";
171 case T_PATHELEM: return "T_PATHELEM";
172 case T_ABSPATH: return "T_ABSPATH";
173 case T_RELPATH: return "T_RELPATH";
174 case T_DICT: return "T_DICT";
175 case T_ALIST: return "T_ALIST";
176 case T_ERROR: return "T_ERROR";
177 }
178 return "**unknown**";
179}
180
181static void ptree_indent(uint32_t amount)
182{
183 uint32_t i;
184 for (i=0; i<amount; i++) printf(" . ");
185}
186
187static void ptree_dump(struct p_node *n, uint32_t d)
188{
189 if (!n) {
190 printf("NULL\n");
191 return;
192 }
193
194 if (n->type<10) {
195 switch(n->type) {
196 case T_STRING: printf("T_STRING: \"%s\" (%s line %d)\n",
197 n->data.string,n->loc.file,n->loc.line); break;
198 case T_NUMBER: printf("T_NUMBER: %d (%s line %d)\n",
199 n->data.number, n->loc.file,n->loc.line); break;
200 case T_KEY: printf("T_KEY: %s (%s line %d)\n",
201 n->data.key, n->loc.file,n->loc.line); break;
202 default: printf("**unknown primitive type**\n"); break;
203 }
204 } else {
205 printf("%s: (%s line %d)\n",ntype(n->type),n->loc.file,n->loc.line);
206 ptree_indent(d);
207 printf(" |-"); ptree_dump(n->l, d+1);
208 ptree_indent(d);
209 printf(" +-"); ptree_dump(n->r, d+1);
210 }
211}
212
213#endif /* DUMP_PARSE_TREE */
214
215static dict_t *dict_find_root(dict_t *d)
216{
217 dict_t *i;
218
219 for (i=d; i->parent; i=i->parent);
220 return i;
221}
222
223static list_t *dict_lookup_path(dict_t *context, struct p_node *p)
224{
225 dict_t *i;
226 list_t *l;
227
228 /* ASSERT p->type==T_PATHELEM */
229 /* ASSERT p->l->type==T_KEY */
230 l=dict_ilookup(context, p->l->data.key);
231 if (!l) {
232 cfgfatal(p->loc,"conffile","can't find key %s\n",
233 p->l->data.key);
234 }
235
236 while (p->r) {
237 if (l->item->type != t_dict) {
238 cfgfatal(p->loc,"conffile","path element \"%s\" "
239 "is not a dictionary\n",p->l->data.key);
240 }
241 i=l->item->data.dict; /* First thing in list */
242
243 p=p->r;
244 l=dict_ilookup_primitive(i, p->l->data.key);
245 if (!l) {
246 cfgfatal(p->loc,"conffile","can't find key %s\n",
247 p->l->data.key);
248 }
249 }
250 return l;
251}
252
253static item_t *new_item(enum types type, struct cloc loc)
254{
255 item_t *i;
256
257 i=safe_malloc(sizeof(*i),"new_item");
258 i->type=type;
259 i->loc=loc;
260 return i;
261}
262
263static list_t *process_item(dict_t *context, struct p_node *i)
264{
265 item_t *item=NULL;
266
267 switch (i->type) {
268 case T_STRING:
269 item=new_item(t_string, i->loc);
270 item->data.string=i->data.string; /* XXX maybe strcpy */
271 break;
272 case T_NUMBER:
273 item=new_item(t_number, i->loc);
274 item->data.number=i->data.number;
275 break;
276 case T_ABSPATH:
277 context=dict_find_root(context);
278 /* falls through */
279 case T_RELPATH:
280 return dict_lookup_path(context, i->r);
281 /* returns immediately */
282 break;
283 case T_DICT:
284 item=new_item(t_dict, i->loc);
285 item->data.dict=dict_new(context);
286/* XXX dict_add_searchpath(context,process_ilist(context, i->r)); */
287 process_alist(item->data.dict, i->l);
288 break;
289 case T_EXEC:
290 return process_invocation(context, i);
291 /* returns immediately */
292 break;
293 default:
294#ifdef DUMP_PARSE_TREE
295 ptree_dump(i,0);
296 fatal("process_item: invalid node type for a list item (%s)\n",
297 ntype(i->type));
298#else
299 fatal("process_item: list item has invalid node type %d - recompile "
300 "with DUMP_PARSE_TREE defined in conffile.c for more "
301 "detailed debug output",i->type);
302#endif /* DUMP_PARSE_TREE */
303 break;
304 }
305 return list_append(NULL,item);
306}
307
308static list_t *process_ilist(dict_t *context, struct p_node *l)
309{
310 struct p_node *i;
311 list_t *r;
312
313 /* ASSERT l->type==T_LISTITEM */
314
315 r=list_new();
316
317 for (i=l; i; i=i->r) {
318 r=list_append_list(r,process_item(context,i->l));
319 }
320 return r;
321}
322
323static list_t *process_invocation(dict_t *context, struct p_node *i)
324{
325 list_t *cll;
326 item_t *cl;
327 list_t *args;
328
329 /* ASSERT i->type==T_EXEC */
330 /* ASSERT i->r->type==T_LISTITEM */
331 /* XXX it might be null too */
332 cll=process_item(context,i->l);
333 cl=cll->item;
334 if (cl->type != t_closure) {
335 cfgfatal(i->l->loc,"conffile","only closures can be invoked\n");
336 }
337 args=process_ilist(context, i->r);
338 return cl->data.closure->apply(cl->data.closure, i->loc, context, args);
339}
340
341static void process_alist(dict_t *context, struct p_node *c)
342{
343 struct p_node *i;
344 atom_t k;
345 list_t *l;
346
347 if (!c) return; /* NULL assignment lists are valid (empty dictionary) */
348
349 /* ASSERT c->type==T_ALIST */
350 if (c->type!=T_ALIST) {
351 fatal("invalid node type in assignment list\n");
352 }
353
354 for (i=c; i; i=i->r) {
355 /* ASSERT i->l && i->l->type==T_ASSIGNMENT */
356 /* ASSERT i->l->l->type==T_KEY */
357 /* ASSERT i->l->r->type==T_LISTITEM */
358 k=i->l->l->data.key;
359 l=process_ilist(context, i->l->r);
360 dict_iadd(context, k, l);
361 }
362}
363
364/* Take a list of items; turn any dictionaries in this list into lists */
365static list_t *makelist(closure_t *self, struct cloc loc,
366 dict_t *context, list_t *args)
367{
368 list_t *r=NULL, *i;
369 struct entry *e;
370
371 for (i=args; i; i=i->next) {
372 if (i->item->type==t_dict) {
373 /* Convert */
374 for (e=i->item->data.dict->entries; e; e=e->next) {
375 r=list_append_list(r, e->val);
376 }
377 } else {
378 r=list_append_list(r, list_append(NULL,i->item));
379 }
380 }
381 return r;
382}
383
384/* Read a file and turn it into a string */
385static list_t *readfile(closure_t *self, struct cloc loc,
386 dict_t *context, list_t *args)
387{
388 FILE *f;
389 string_t filename;
390 long length;
391 item_t *r;
392
393 r=list_elem(args,0);
394 if (!r) {
395 cfgfatal(loc,"readfile","you must supply a filename\n");
396 }
397 if (r->type!=t_string) {
398 cfgfatal(loc,"readfile","filename must be a string\n");
399 }
400 filename=r->data.string;
401 f=fopen(filename,"rb");
402 if (!f) {
403 fatal_perror("readfile (%s:%d): cannot open file \"%s\"",
404 loc.file,loc.line, filename);
405 }
406 if (fseek(f, 0, SEEK_END)!=0) {
407 fatal_perror("readfile (%s:%d): fseek(SEEK_END)",loc.file,loc.line);
408 }
409 length=ftell(f);
410 if (length<0) {
411 fatal_perror("readfile (%s:%d): ftell()",loc.file,loc.line);
412 }
413 if (fseek(f, 0, SEEK_SET)!=0) {
414 fatal_perror("readfile (%s:%d): fseek(SEEK_SET)",loc.file,loc.line);
415 }
416 r=new_item(t_string,loc);
417 r->data.string=safe_malloc(length+1,"readfile");
418 if (fread(r->data.string,length,1,f)!=1) {
419 fatal("readfile (%s:%d): fread: could not read all of file\n",
420 loc.file,loc.line);
421 }
422 r->data.string[length]=0;
423 if (fclose(f)!=0) {
424 fatal_perror("readfile (%s:%d): fclose",loc.file,loc.line);
425 }
426 return list_append(NULL,r);
427}
428
429static dict_t *process_config(struct p_node *c)
430{
431 dict_t *root;
432 dict_t *context;
433 item_t *i;
434 list_t *false;
435 list_t *true;
436
437 root=dict_new(NULL);
438 context=root;
439
440 /* Predefined keys for boolean values */
441 i=new_item(t_bool,no_loc);
442 i->data.bool=False;
443 false=list_append(NULL,i);
444 i=new_item(t_bool,no_loc);
445 i->data.bool=True;
446 true=list_append(NULL,i);
447 dict_add(root,"false",false);
448 dict_add(root,"False",false);
449 dict_add(root,"FALSE",false);
450 dict_add(root,"no",false);
451 dict_add(root,"No",false);
452 dict_add(root,"NO",false);
453 dict_add(root,"true",true);
454 dict_add(root,"True",true);
455 dict_add(root,"TRUE",true);
456 dict_add(root,"yes",true);
457 dict_add(root,"Yes",true);
458 dict_add(root,"YES",true);
459
460 add_closure(root,"makelist",makelist);
461 add_closure(root,"readfile",readfile);
462
463 init_builtin_modules(root);
464
465 process_alist(context, c);
466
467 return root;
468}
469
470/***** Externally accessible functions */
471
472atom_t intern(string_t s)
473{
474 struct atomlist *i;
475
476 for (i=atoms; i; i=i->next) {
477 if (strcmp(i->a, s)==0) break;
478 }
479
480 if (!i) {
481 /* Did't find it; create a new one */
482 i=safe_malloc(sizeof(*i),"intern: alloc list entry");
483 i->a=safe_strdup(s,"intern: alloc string");
484 i->next=atoms;
485 atoms=i;
486 }
487 return i->a;
488}
489
490list_t *dict_lookup(dict_t *dict, string_t key)
491{
492 return dict_ilookup(dict, intern(key));
493}
494
495list_t *dict_lookup_primitive(dict_t *dict, string_t key)
496{
497 return dict_ilookup_primitive(dict, intern(key));
498}
499
500void dict_add(dict_t *dict, string_t key, list_t *val)
501{
502 dict_iadd(dict,intern(key),val);
503}
504
505string_t *dict_keys(dict_t *dict)
506{
507 atom_t *r, *j;
508 struct entry *i;
509 r=safe_malloc(sizeof(*r)*(dict->size+1),"dict_keys");
510 for (i=dict->entries, j=r; i; i=i->next, j++) {
511 *j=i->key;
512 }
513 *j=NULL;
514 return r;
515}
516
517
518/* List-related functions */
519
520list_t *list_new(void)
521{
522 return NULL;
523}
524
525list_t *list_append_list(list_t *a, list_t *b)
526{
527 list_t *i;
528
529 if (!a) return b;
530 for (i=a; i->next; i=i->next);
531 i->next=b;
532 return a;
533}
534
535list_t *list_append(list_t *list, item_t *item)
536{
537 list_t *l;
538
539 l=safe_malloc(sizeof(*l),"list_append");
540 l->item=item;
541 l->next=NULL;
542
543 return list_append_list(list,l);
544}
545
546item_t *list_elem(list_t *l, uint32_t index)
547{
548 if (!l) return NULL;
549 if (index==0) return l->item;
550 return list_elem(l->next, index-1);
551}
552
553list_t *new_closure(closure_t *cl)
554{
555 item_t *i;
556
557 i=new_item(t_closure,no_loc);
558 i->data.closure=cl;
559 return list_append(NULL,i);
560}
561
562void add_closure(dict_t *dict, string_t name, apply_fn apply)
563{
564 closure_t *c;
565 c=safe_malloc(sizeof(*c),"add_closure");
566 c->description=name;
567 c->apply=apply;
568 c->interface=NULL;
569
570 dict_add(dict,name,new_closure(c));
571}
572
573void *find_cl_if(dict_t *dict, string_t name, uint32_t type,
574 bool_t fail_if_invalid, string_t desc, struct cloc loc)
575{
576 list_t *l;
577 item_t *i;
578 closure_t *cl;
579
580 l=dict_lookup(dict,name);
581 if (!l) {
582 if (!fail_if_invalid) return NULL;
583 cfgfatal(loc,desc,"closure \"%s\" not found\n",name);
584 }
585 i=list_elem(l,0);
586 if (i->type!=t_closure) {
587 if (!fail_if_invalid) return NULL;
588 cfgfatal(loc,desc,"\"%s\" must be a closure\n",name);
589 }
590 cl=i->data.closure;
591 if (cl->type!=type) {
592 if (!fail_if_invalid) return NULL;
593 cfgfatal(loc,desc,"\"%s\" is the wrong type of closure\n",name);
594 }
595 return cl->interface;
596}
597
598/* Convenience functions for modules reading configuration dictionaries */
599item_t *dict_find_item(dict_t *dict, string_t key, bool_t required,
600 string_t desc, struct cloc loc)
601{
602 list_t *l;
603 item_t *i;
604
605 l=dict_lookup(dict,key);
606 if (!l) {
607 if (!required) return NULL;
608 cfgfatal(loc,desc,"required parameter \"%s\" not found\n",key);
609 }
610 i=list_elem(l,0);
611 return i;
612}
613
614string_t dict_read_string(dict_t *dict, string_t key, bool_t required,
615 string_t desc, struct cloc loc)
616{
617 item_t *i;
618 string_t r;
619
620 i=dict_find_item(dict,key,required,desc,loc);
621 if (!i) return NULL;
622 if (i->type!=t_string) {
623 cfgfatal(loc,desc,"\"%s\" must be a string\n",key);
624 }
625 r=i->data.string;
626 return r;
627}
628
629uint32_t dict_read_number(dict_t *dict, string_t key, bool_t required,
630 string_t desc, struct cloc loc, uint32_t def)
631{
632 item_t *i;
633 uint32_t r;
634
635 i=dict_find_item(dict,key,required,desc,loc);
636 if (!i) return def;
637 if (i->type!=t_number) {
638 cfgfatal(loc,desc,"\"%s\" must be a number\n",key);
639 }
640 r=i->data.number;
641 return r;
642}
643
644bool_t dict_read_bool(dict_t *dict, string_t key, bool_t required,
645 string_t desc, struct cloc loc, bool_t def)
646{
647 item_t *i;
648 bool_t r;
649
650 i=dict_find_item(dict,key,required,desc,loc);
651 if (!i) return def;
652 if (i->type!=t_bool) {
653 cfgfatal(loc,desc,"\"%s\" must be a boolean\n",key);
654 }
655 r=i->data.bool;
656 return r;
657}
658
659static struct subnet string_to_subnet(item_t *i, string_t desc)
660{
661 struct subnet s;
662 uint32_t a, b, c, d, n;
663 uint32_t match;
664
665 /* i is not guaranteed to be a string */
666 if (i->type!=t_string) {
667 cfgfatal(i->loc,desc,"expecting a string (subnet specification)\n");
668 }
669
670 /* We expect strings of the form "a.b.c.d[/n]", i.e. the dots are
671 NOT optional. The subnet mask is optional; if missing it is assumed
672 to be /32. */
673 match=sscanf(i->data.string,"%u.%u.%u.%u/%u", &a, &b, &c, &d, &n);
674 if (match<4) {
675 cfgfatal(i->loc,desc,"\"%s\" is not a valid "
676 "subnet specification\n",i->data.string);
677 }
678 if (match<5) {
679 n=32;
680 }
681 if (a>255 || b>255 || c>255 || d>255 || n>32) {
682 cfgfatal(i->loc,desc,"\"%s\": range error\n",i->data.string);
683 }
684 s.prefix=(a<<24)|(b<<16)|(c<<8)|(d);
685 s.mask=(~0UL << (32-n));
686 if (s.prefix & ~s.mask) {
687 cfgfatal(i->loc,desc,"\"%s\": prefix not fully contained "
688 "in mask\n",i->data.string);
689 }
690 return s;
691}
692
693uint32_t string_to_ipaddr(item_t *i, string_t desc)
694{
695 uint32_t a, b, c, d;
696 uint32_t match;
697
698 /* i is not guaranteed to be a string */
699 if (i->type!=t_string) {
700 cfgfatal(i->loc,desc,"expecting a string (IP address)\n");
701 }
702
703 match=sscanf(i->data.string,"%u.%u.%u.%u", &a, &b, &c, &d);
704 if (match<4) {
705 cfgfatal(i->loc,desc,"\"%s\" is not a valid "
706 "IP address\n",i->data.string);
707 }
708 if (a>255 || b>255 || c>255 || d>255) {
709 cfgfatal(i->loc,desc,"\"%s\": range error\n",i->data.string);
710 }
711 return (a<<24)|(b<<16)|(c<<8)|(d);
712}
713
714void dict_read_subnet_list(dict_t *dict, string_t key, bool_t required,
715 string_t desc, struct cloc loc,
716 struct subnet_list *sl)
717{
718 list_t *l, *li;
719 item_t *i;
720 uint32_t e=0;
721
722 sl->entries=0;
723 sl->list=NULL;
724 l=dict_lookup(dict, key);
725 if (!l) {
726 if (!required) return;
727 cfgfatal(loc,desc,"required parameter \"%s\" not found\n",key);
728 }
729 /* Count the items in the list */
730 for (li=l; li; li=li->next) e++;
731 if (e==0) return;
732 sl->entries=e;
733 sl->list=safe_malloc(sizeof(struct subnet)*e, "dict_read_subnet_list");
734 e=0;
735 /* Fill in the list */
736 for (li=l; li; li=li->next) {
737 i=li->item;
738 if (i->type!=t_string) {
739 cfgfatal(loc,desc,"parameter \"%s\": all elements must "
740 "be strings\n",key);
741 }
742 sl->list[e++]=string_to_subnet(i,desc);
743 }
744}
745
746dict_t *read_conffile(char *name)
747{
748 FILE *conffile;
749 struct p_node *config;
750
751 if (strcmp(name,"-")==0) {
752 conffile=stdin;
753 } else {
754 conffile=fopen(name,"r");
755 if (!conffile)
756 fatal_perror("Cannot open configuration file \"%s\"",name);
757 }
758 config_lineno=1;
759 config_file=name;
760 config=parse_conffile(conffile);
761 fclose(conffile);
762
763#ifdef DUMP_PARSE_TREE
764 printf("*** config file parse tree BEFORE MANGLE\n");
765 ptree_dump(config,0);
766#endif /* DUMP_PARSE_TREE */
767 /* The root of the configuration is a T_ALIST, which needs reversing
768 before we mangle because it isn't the child of a T_DICT. */
769 config=list_reverse(config);
770 ptree_mangle(config);
771#ifdef DUMP_PARSE_TREE
772 printf("\n\n*** config file parse tree AFTER MANGLE\n");
773 ptree_dump(config,0);
774#endif /* DUMP_PARSE_TREE */
775 return process_config(config);
776}