1
/* Copyright (c) 2007-2012 Sam Trenholme
5
* Redistribution and use in source and binary forms, with or without
6
* modification, are permitted provided that the following conditions
9
* 1. Redistributions of source code must retain the above copyright
10
* notice, this list of conditions and the following disclaimer.
11
* 2. Redistributions in binary form must reproduce the above copyright
12
* notice, this list of conditions and the following disclaimer in the
13
* documentation and/or other materials provided with the distribution.
15
* This software is provided 'as is' with no guarantees of correctness or
16
* fitness for purpose.
23
#include "DwStr_functions.h"
27
dwm_fs fsm[DWM_MAX_STATES + 1]; /* Finite state machine */
28
char *fsm_desc=dwm_machine;
32
int dwm_file_depth = 0; /* How many files we're nested in */
34
/* Set the new fsm state for a given pattern seen in a given fsm state
35
* Input: c: Pointer to string with FSM description (pointing to the
36
* character we are currently parsing)
37
* d: The action number for this state that we are processing
38
* state: The state we are processing
39
* Output: A revised pointer to the FSM description */
40
char *dwm_set_newstate(char *c, int d, int32_t state) {
41
if(*c < 'a' || *c > 'z') {
44
/* If the state starts with x, it's an "expanded" state, with a
45
* two-letter name like "xa", "xc", "xp", or "xz" */
48
if(*c < 'a' || *c > 'z') {
51
fsm[state].newstate[d] = *c - 'a' + 26;
53
fsm[state].newstate[d] = *c - 'a';
59
/* Set the action for a given pattern in a given fsm state
60
* Input: c: Pointer to string with FSM description (pointing to the
61
* character we are currently parsing)
62
* max: How far we are in the current line (bounds checker)
63
* d: The action number for this state that we are processing
64
* state: The state we are processing
65
* Output: A revised pointer to the FSM description */
66
char *dwm_set_action(char *c, int max, int d, int32_t state) {
69
fsm[state].action[d] = 10; /* Terminate with success */
70
fsm[state].newstate[d] = 0;
82
fsm[state].action[d] = b;
89
return dwm_set_newstate(c,d,state);
92
/* Convert a one-character FSM class in to a number we put in to
93
* the tokenized FSM machine */
94
char dwm_pattern_process(int b) {
95
/* We don't allow literal hashes or quotes in the FSM definition */
96
if(b == '#' || b == '"') {
99
/* H becomes a hash symbol (so a FSM definition can have comments) */
103
/* Q becomes a " symbol (so we don't have ugly escape quoted sequences
104
* in the FSM definition) */
108
/* Make letter shortcuts non-printable ASCII control characters */
109
if(b >= 'A' && b <= 'Z') {
110
b = b - '@'; /* '@' is 'A' - 1 in ASCII */
115
/* Set a single state in the finite state machine */
116
char *dwm_set_fsm(int32_t state, char *c, int max) {
119
for(; max<52; max++) {
121
/* First letter: pattern */
123
if(b < '!' || b > '~') {
126
fsm[state].pattern[d] = dwm_pattern_process(b);
128
/* Second latter: action -or- next state */
131
if(*c >= 'a' && *c <= 'z') {
132
fsm[state].action[d] = 0;
133
c = dwm_set_newstate(c,d,state);
135
c = dwm_set_action(c,max,d,state);
143
/* Advance past whitespace between patterns/actions */
152
if(*c == '\r' || *c == '\n') { /* Newline ends a state */
156
if(d >= DWM_MAX_PATTERNS - 1) { /* Bounds checking */
163
/* Tokenize a single line describing the finite state machine */
164
char *dwm_tokenize_line(char *c) {
168
/* The first character of a line is the state we are describing */
169
if(*c < 'a' || *c > 'z') {
172
/* If the first character happens to be an 'x', the state description
173
* is two letters long in a form like "xa", "xb", and so on */
177
if(*c < 'a' || *c > 'z') {
184
if(b < 0 || b > DWM_MAX_STATES - 1) {
187
/* OK, now move forward to see the first pattern description */
192
while(*c == ' ' && max < 52) {
199
/* dwm_set_fsm sets how we react to various patterns when in this
201
c = dwm_set_fsm(b,c,max);
205
if(*c != '\r' && *c != '\n') {
212
/* Initialize the finite state machine based on the vales set in the
213
* dwm_machine constant (which is set in DwMararc.h) */
214
void dwm_init_fsm() {
219
/* First, we zero it out */
220
for(a=0;a<DWM_MAX_STATES;a++) {
221
for(b=0;b<DWM_MAX_PATTERNS;b++) {
222
fsm[a].pattern[b] = 0;
223
fsm[a].action[b] = 0;
224
fsm[a].newstate[b] = 0;
228
/* Now, we look at the defined fsm and tokenize it */
231
for(a=0;a<DWM_MAX_STATES;a++) {
232
c = dwm_tokenize_line(c);
244
/* Fatal error while parsing Mararc file */
245
void dwm_fatal(char *why) {
246
dw_alog_number("Fatal error in dwood3rc file on line ",dwm_linenum,
251
/* Given an input character, and a character class (a number from one
252
* to 32), return -1 if we don't match, 1 if we do match */
253
int dwm_char_class(int32_t in, int cclass) {
254
/* In order to save space, we use this C bit of messyness:
255
* condition ? true : false
256
* This is normally against coding style; we do it so the function
257
* fits within the 52-line-per-function limit */
259
case 1: /* A-Za-z_ */
260
return dwm_is_alpha(in) ? 1 : -1;
262
case 2: /* A-Za-z0-9_ */
263
return dwm_is_alphanum(in) ? 1 : -1;
265
case 4: /* A-Za-z0-9_- */
266
return dwm_is_dname(in) ? 1 : -1;
268
case 9: /* printable ASCII except # and " */
269
return dwm_is_instring(in) ? 1 : -1;
272
return dwm_is_number(in) ? 1 : -1;
275
return (in == '\r') ? 1 : -1;
277
case 19: /* A-Za-z0-9 */
278
return dwm_is_dnamestart(in) ? 1 : -1;
281
return (in == '\n') ? 1 : -1;
283
case 23: /* ' ' or \t */
284
return dwm_is_whitespace(in) ? 1 : -1;
286
case 24: /* printable ASCII/hi-bit or \t */
287
return dwm_is_any(in) ? 1 : -1;
289
case 25: /* A-Za-z */
290
return dwm_is_alphastart(in) ? 1 : -1;
297
/* Given an input character, and a state, return an action (upper 16 bits)
298
* and a new state (lower 16 bits).
299
* Input: An input character and a current state
300
* Output: ((action << 16) | newstate) or -1 on error */
301
int32_t dwm_process_character(int32_t in, int32_t state) {
303
uint16_t newstate = 0;
308
if(state >= DWM_MAX_STATES) {
312
if(fsm[state].pattern[0] == 0) { /* Invalid state */
316
for(a=0;a<DWM_MAX_PATTERNS && fsm[state].pattern[a] != 0;a++) {
317
b = fsm[state].pattern[a];
320
/* patterns between 1 and 32 are multi-character classes,
321
* such as "letters" */
323
match = dwm_char_class(in,b);
324
/* Higher numbered "patterns" are one-character patterns,
325
* with the value of the character being the ASCII character
326
* we match against */
335
action = fsm[state].action[a];
336
newstate = fsm[state].newstate[a];
341
if(fsm[state].pattern[a] == 0) { /* Invalid state */
345
if(newstate >= DWM_MAX_STATES || action > 4096) {
349
return (action << 16) | newstate;
352
/* Initialize the Mararc parameters */
354
/* Initialize the data used to store MaraRC dictionary parameters */
355
void dwm_dict_init() {
357
for(a=0; a < KEY_D_COUNT; a++) {
362
/* Initialize all mararc params */
363
void dwm_init_mararc() {
365
for(a = 0; a < KEY_S_COUNT; a++) {
369
/* Numeric mararc variables have default values. */
370
key_n[DWM_N_maxprocs] = 32;
371
#ifndef FALLBACK_TIME
372
key_n[DWM_N_timeout_seconds] = 1;
373
#else /* FALLBACK_TIME */
374
key_n[DWM_N_timeout_seconds] = 2;
375
#endif /* FALLBACK_TIME */
376
key_n[DWM_N_dns_port] = 53;
377
key_n[DWM_N_upstream_port] = 53;
378
key_n[DWM_N_handle_overload] = 1;
379
key_n[DWM_N_handle_noreply] = 1;
380
key_n[DWM_N_recurse_min_bind_port] = 15000;
381
key_n[DWM_N_recurse_number_ports] = 4096;
382
key_n[DWM_N_hash_magic_number] = 1; /* Not real default value */
383
key_n[DWM_N_maximum_cache_elements] = 1024;
384
key_n[DWM_N_maradns_uid] = 99;
385
key_n[DWM_N_maradns_gid] = 99;
386
key_n[DWM_N_resurrections] = 1;
387
key_n[DWM_N_num_retries] = 5;
388
key_n[DWM_N_verbose_level] = 3;
389
key_n[DWM_N_max_tcp_procs] = 8;
390
key_n[DWM_N_timeout_seconds_tcp] = 4;
391
key_n[DWM_N_tcp_listen] = 0;
392
key_n[DWM_N_max_ar_chain] = 1;
393
key_n[DWM_N_ttl_age] = 1;
394
key_n[DWM_N_max_inflights] = 8;
395
key_n[DWM_N_deliver_all] = 1;
396
key_n[DWM_N_filter_rfc1918] = 1;
397
key_n[DWM_N_ns_glueless_type] = 1;
398
key_n[DWM_N_reject_aaaa] = 0;
399
key_n[DWM_N_reject_mx] = 1;
400
key_n[DWM_N_truncation_hack] = 1;
401
key_n[DWM_N_reject_ptr] = 0;
402
key_n[DWM_N_min_ttl_incomplete_cname] = 3600;
403
key_n[DWM_N_max_ttl] = 86400;
406
/* Look for a Mararc parameter; -1 if not found/error; 0-n if found
407
* (0: First index, 1: second index, etc.) */
408
int32_t dwm_grep_params(dw_str *seek, char *list[], int max) {
413
if(seek == 0 || list == 0 || max < 1) {
415
goto catch_dwm_grep_params;
418
for(e = 0; e < max; e++) {
421
goto catch_dwm_grep_params;
423
look = dw_create(64);
424
dw_qrappend((uint8_t *)list[e],look,0);
425
if(dw_issame(seek,look) == 1) {
427
goto catch_dwm_grep_params;
437
catch_dwm_grep_params:
445
/* Start to read another file to parse data from */
446
void dwm_execfile(dw_str *execfile, dw_str *fname) {
447
char *name = 0, *a = 0;
455
if(dw_cstr_append((uint8_t *)"execfile",8,cmp) == -1) {
459
if(dw_issame(cmp,execfile) != 1) {
460
dwm_fatal("Should have execfile here");
467
if(chdir(EXECFILE_DIR) != 0) {
468
dwm_fatal("Could not enter execfile directory");
473
name = (char *)dw_to_cstr(fname);
480
/* Clean up path to filename */
484
for(counter = 0; counter < 200; counter++) {
488
if((*a < 'a' || *a > 'z') && *a != '_' && *a != '/') {
494
dwm_parse_file(name);
499
/* Based on the actions done, determine what to do (this is called from
502
* -1: Error (bad action)
504
* 1: Set normal string variable
505
* 2: Append to normal string variable
506
* 3: Set dictionary variable
507
* 4: Append to dictionary variable
508
* 5: Set numeric variable
509
* 6: Init dictionary variable
510
* 7: Read other file for more dwood2rc parameters
513
int dwm_set_todo(dw_str **actions) {
514
if(actions[1] == 0) { /* Do nothing */
517
if(actions[6] != 0 && actions[1] != 0 && actions[2] == 0) {
518
/* Init dictionary variable */
521
if(actions[7] != 0) { /* Read and parse other file */
522
dwm_execfile(actions[1],actions[7]);
525
if(actions[3] == 0) { /* No string to set */
526
if(actions[4] == 0) { /* No number to set */
528
} else if(actions[2] != 0) { /* Dictionary variable */
530
} else if(actions[5] != 0) { /* += instead of = */
531
return -1; /* Not supported; should we support this? */
536
if(actions[2] != 0) { /* Dictionary variable */
537
if(actions[6] != 0) { /* foo["."] = {} line */
540
if(actions[5] != 0) { /* += instead of = */
545
if(actions[5] != 0) { /* += instead of = */
551
/* Add a dictionary key/value pair to the compiled list of MaraRC
554
* Input: Number of MaraRC parameter to modify
555
* Dictionary key index to modify
556
* Value for said key index
558
* Output: Void finction
560
* Global variables modified: key_d
562
void dwm_dict_add(int num, dw_str *key, dw_str *value, int todo) {
566
if(num >= KEY_D_COUNT || num < 0) { /* Sanity check */
567
dwm_fatal("Unknown dictionary variable");
570
if(todo == 6 && (key != 0 || value != 0)) {
571
dwm_fatal("Illegal dictionary initialization");
574
/* Initialize dictionary if needed*/
575
if(key_d[num] == 0 && todo == 6) { /* Initialized with {} */
576
key_d[num] = dwd_init();
579
} else if(key_d[num] != 0 && todo == 6) { /* Only initialize once */
580
dwm_fatal("Dictionary variable already initialized");
581
#endif /* TINY_BINARY */
582
} else if(key_d[num] == 0) {
583
dwm_fatal("Uninitialized dictionary variable");
586
if(todo != 4) { /* =, create new dictionary element */
588
check = dwd_fetch(key_d[num],key);
590
dw_log_dwstr_str("Warning: Dictionary element \"",
591
key,"\" defined more than once",0);
593
/* I am tempted to make this fatal but will not since
594
* it could make managing large anti-phish/malware
595
* blacklists harder */
597
"Dictionary elements must be defined only once");*/
599
#endif /* TINY_BINARY */
600
key_d[num] = dwd_add(key_d[num],key,value);
604
/* OK, += so we append an already existing element */
605
temp = dwd_fetch(key_d[num],key);
607
dwm_fatal("Appending unset dictionary index");
610
dw_append(value,temp);
612
dwd_add(key_d[num],key,temp);
618
/* Fetch a value from a given dictionary variable (num is the number for
619
* the dictionary variable we are seeking a given value for, given the
620
* dictionary variable and the key inside that variable) */
621
dw_str *dwm_dict_fetch(int num, dw_str *key) {
622
if(num >= KEY_D_COUNT) {
626
return dwd_fetch(key_d[num],key);
630
/* For a given dictionary variable, and a key, return (as a *copied* dw_str
631
* object) the next key or 0 if we're at the last key. If the key given to
632
* this function is 0, return the first key. */
633
dw_str *dwm_dict_nextkey(int num, dw_str *key) {
634
if(num >= KEY_D_COUNT) {
637
return dwd_nextkey(key_d[num],key);
640
/* Based on the actions done, set the appropriate Mararc parameters */
641
void dwm_set_keys(dw_str **actions) {
642
int todo = 0, num = 0;
645
todo = dwm_set_todo(actions);
648
} else if(todo == -1) {
649
dwm_fatal("Bad deadwoodrc action");
652
if(todo == 1 || todo == 2) { /* Normal variable */
653
num = dwm_grep_params(actions[1],key_s_names,KEY_S_COUNT);
655
} else if(todo == 3 || todo == 4 || todo == 6) { /* Dict. variable */
656
num = dwm_grep_params(actions[1],key_d_names,KEY_D_COUNT);
657
if(num < 0) { /* Make sure the parameter is a legal one */
658
dwm_fatal("Unknown dwood3rc dictionary parameter");
660
dwm_dict_add(num, actions[2], actions[3], todo);
662
} else if(todo == 5) { /* Numeric variable */
664
num = dwm_grep_params(actions[1],key_n_names,KEY_N_COUNT);
665
val = dw_atoi(actions[4],0,10);
667
dwm_fatal("Invalid numeric value");
669
if(num < 0) { /* Make sure the parameter is a legal one */
670
dwm_fatal("Unknown dwood3rc numeric parameter");
674
} else { /* Shouldn't get here */
678
if(list == 0) { /* Sanity check */
679
dwm_fatal("Unknown dwood3rc action");
681
if(num < 0) { /* Make sure the parameter is a legal one */
682
dwm_fatal("Unknown dwood3rc string parameter");
685
if(list[num] == 0 && (todo & 1) == 0) {
686
dwm_fatal("Appending to unset string parameter");
687
} else if(list[num] != 0 && (todo & 1) == 0 /* append */) {
688
dw_append(actions[3],list[num]);
690
} else if(list[num] != 0) {
691
dwm_fatal("Deadwoodrc parameter set twice");
694
list[num] = dw_copy(actions[3]);
697
/* If there is an action to perform, perform the action */
698
void dwm_do_action(int32_t ch, int32_t action, dw_str **actions) {
699
if(actions[action] == 0) {
700
/* This function is *only* called from dwm_parse_line,
701
* and the destructor for these strings is at the
702
* end of that function */
703
actions[action] = dw_create(384);
705
dw_addchar(ch, actions[action]);
708
/* Parse a line in a MaraRC (Dwood#RC) file */
709
int dwm_parse_line(FILE *look) {
712
int32_t action = 0, state = 0, mix = 0, ch = 0, last = 0;
715
for(a = 0; a < 11; a++) {
721
ret = 1; /* End of file reached */
722
goto catch_dwm_parse_line;
725
while(action != 10) {
726
mix = dwm_process_character(ch,state);
729
goto catch_dwm_parse_line;
731
action = (mix >> 16) & 0x7fff;
732
state = mix & 0x7fff;
733
if(action == 10) { /* Terminate */
734
ret = 0; /* Line parsed */
735
break; /* Now we need to set the keys */
736
} else if(action == 8) {
737
ret = -4; /* Fatal: No leading whitespace */
738
goto catch_dwm_parse_line;
739
} else if(action > 0 && action < 10) {
740
dwm_do_action(ch,action,actions);
744
if(feof(look) && last != '\r' && last != '\n') {
745
ret = -3; /* Premature EOF; incomplete last line */
746
goto catch_dwm_parse_line;
750
dwm_set_keys(actions); /* Sets mararc parameters based on actions */
752
catch_dwm_parse_line: /* Actions strings destructor */
753
for(a = 0; a < 11; a++) {
754
if(actions[a] != 0) {
755
dw_destroy(actions[a]);
762
/* Parse a single filename; used by dwm_parse_mararc and dwm_execfile to
763
* parse the contents of a single file */
764
int dwm_parse_file(char *name) {
768
if(dwm_file_depth > 8 || name == 0) {
774
look = fopen(name,"rb");
776
return -1; /* File open error */
780
a = dwm_parse_line(look);
782
dwm_fatal("incomplete last line");
784
dwm_fatal("leading whitespace not allowed");
786
if(a != 0 && a != 1) {
787
dwm_fatal("deadwoodrc parse error");
789
if(dwm_file_depth == 1) {
801
/* Parse a mararc file; this should only be called once when executing
802
* deadwood. Note that this is the *only* public method in this entire
803
* file; all other functions in this file should only be called from
804
* other functions in this file.
805
* Input: c string that points to mararc file
806
* Output: 1 on success; program exits on mararc parse error */
807
int dwm_parse_mararc(char *name) {
813
return dwm_parse_file(name);
818
/* Debugging function to make sure fsm is correctly set up */
821
int a = 0, b = 0, q = 0;
822
for(a = 0; a < DWM_MAX_STATES; a++) {
823
if(fsm[a].pattern[0] != 0) {
824
printf("State %d\n",a);
826
for(b = 0; b < DWM_MAX_PATTERNS; b++) {
827
q = fsm[a].pattern[b];
828
if(q > 0 && q < 32 && fsm[a].action[b] != 10) {
829
printf("Pattern @%d action %d newstate %d\n",
833
} else if(q >= '!' && fsm[a].action[b] != 10) {
834
printf("Pattern %c action %d newstate %d\n",
839
} else if(q > 0 && q < 32) {
840
printf("Pattern @%d action %d (terminate)\n",
843
} else if(q >= '!') {
844
printf("Pattern %c action %d (terminate)\n",
851
if(fsm[a].pattern[0] != 0) {
860
dwm_parse_mararc("deadwoodrc");
864
for(a=0;a<KEY_S_COUNT;a++) {
865
printf("%s is ",key_s_names[a]);
867
printf("not set.\n");
873
for(a=0;a<KEY_D_COUNT;a++) {
874
printf("%s is ",key_d_names[a]);
876
printf(" not set.\n");
885
#endif /* HAVE_MAIN */