1    | /***************************************
2    |   $Revision: 1.12 $
3    | 
4    |   Error reporting (er) er_macro.c - simple macro processor
5    | 
6    |   Status: NOT REVUED, PARTLY TESTED
7    | 
8    |   Design and implementation by: Marek Bukowy
9    | 
10   |   ******************/ /******************
11   |   Copyright (c) 1999,2000,2001,2002                   RIPE NCC
12   |  
13   |   All Rights Reserved
14   |   
15   |   Permission to use, copy, modify, and distribute this software and its
16   |   documentation for any purpose and without fee is hereby granted,
17   |   provided that the above copyright notice appear in all copies and that
18   |   both that copyright notice and this permission notice appear in
19   |   supporting documentation, and that the name of the author not be
20   |   used in advertising or publicity pertaining to distribution of the
21   |   software without specific, written prior permission.
22   |   
23   |   THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
24   |   ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS; IN NO EVENT SHALL
25   |   AUTHOR BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY
26   |   DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
27   |   AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
28   |   OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
29   |   ***************************************/
30   | 
31   | #include "rip.h"
32   | 
33   | #include <string.h>
34   | #include <glib.h>
35   | 
36   | 
37   | /*++++++++++++++++++++++++++++++++++++++
38   |   
39   |   processes a macro call, i.e. executes one of the predefined macros
40   |   selected by the 0th word of the array, using other words as
41   |   arguments to that macro. Uses the er_macro_array[] to find the
42   |   macro definition.  Allocates the result string and stores the
43   |   pointer to it in **output.
44   |   
45   |   int ER_process_split    returns 0 on success, non-0 on failure.
46   | 
47   |   int argc                number of words in the word array
48   |   
49   |   char **argv             word array (pointers to strings)
50   |   
51   |   char **output           storage for the result pointer (to allocated text)
52   |   ++++++++++++++++++++++++++++++++++++++*/
53   | int
54   | ER_process_split(int argc, char **argv, char **output)
55   | {
56   |   unsigned char *ch;
57   |   char *pattern;
58   |   GString *result = g_string_new("");
59   |   int retval = 0;
60   | 
61   |   TH_acquire_write_lock( &er_paths_lock );
62   | 
63   |   if( /* if called without the macro name */
64   |      argc == 0  
65   |      /* or macro can not be found */
66   |      || (pattern = g_hash_table_lookup(er_macro_hash, argv[0])) == NULL ) {
67   | 
68   |     retval = -1;
69   |   }
70   |   else {
71   |     /* copy the macro definition by portions, substituting the $([0-9]) 
72   |        entries with arguments. Error if not enough arguments.
73   |     */
74   |     do {
75   |       
76   |       if( (ch = (unsigned char *) strstr( pattern, "$(" )) == NULL ) {
77   | 	/* no more entries. copy the rest */
78   | 	g_string_append ( result, pattern );
79   | 	break;
80   |       }
81   |       else {
82   | 	/* pass the string between here and ch */
83   | 	while( pattern != (char *)ch ) {
84   | 	  g_string_append_c ( result, *pattern );
85   | 	  pattern++;
86   | 	}
87   | 	/* check the next 3 characters exist, break the look if not */
88   | 	if( *(ch+2) == '\0' ||  *(ch+3) == '\0') {
89   | 	  break;
90   | 	}
91   | 
92   | 	/* look for the digit and ")", pass the $( through if not present */
93   | 	if( ! isdigit(*(ch+2)) || *(ch+3) != ')' ) {
94   | 	  /* not need to do anything to make it pass through */
95   | 	  ;
96   | 	}
97   | 	else {
98   | 	  /* substitute the $(?) with the appropriate argument.
99   | 	     error if not enough arguments or $(0) is used.*/
100  | 	  int a = *(ch+2) - '0';
101  | 	
102  | 	  if( argc < a || a==0) {
103  | 	    retval = -1;
104  | 	    break;
105  | 	  }
106  | 	  g_string_append( result, argv[a]);
107  | 	  /* advance the pattern pointer */
108  | 	  pattern += strlen("$(1)");
109  | 	}
110  |       }
111  |     } while(1);
112  |   }
113  | 
114  |   /* copy the pointer, free the orig structure, keep the text */
115  |     
116  |   *output = (result->str); 
117  |   
118  |   g_string_free( result, FALSE );
119  | 
120  |   TH_release_write_lock( &er_paths_lock );
121  | 
122  |   return retval;
123  | }
124  | 
125  | 
126  | /*++++++++++++++++++++++++++++++++++++++
127  |   
128  |   Take a text line and parse it as an error specification
129  |   line. Optionally, if the first word is a macro, run the macro using
130  |   other words as its arguments.  This is basically a wrapper around
131  |   ER_process_split() that splits the string into argv and calls the
132  |   ER_parse.
133  | 
134  |   sets the errbuf to the result of ER_parse_spec.
135  | 
136  |   int ER_macro_spec      returns 0 on success, non-0 on failure.  
137  | 
138  |   char *input            input line
139  | 
140  |   char **errbuf          storage for the result pointer (to allocated text)
141  |   ++++++++++++++++++++++++++++++++++++++*/
142  | int
143  | ER_macro_spec(char *input, char **errbuf)
144  | {
145  |   char *copy = ut_string_compress(input);
146  |   char **argv = g_strsplit(copy, " ", 0);
147  |   int argc = 0, ret;
148  |   char *fullspec;
149  | 
150  |   while( argv[argc] != NULL ) {
151  |     argc++;
152  |   }
153  |   
154  | 
155  |   if( ER_process_split(argc, argv, &fullspec) != 0 ) {
156  |     /* macro unknown. That's OK, just parse that text now */
157  | 
158  |     fullspec = UT_strdup(input);
159  |   }
160  | 
161  |   ret = ER_parse_spec(fullspec, errbuf);
162  | 
163  |   UT_free(fullspec);
164  |   UT_free(copy);
165  |   g_strfreev(argv);
166  | 
167  |   return ret;
168  |   
169  | }
170  | 
171  | 
172  | /*++++++++++++++++++++++++++++++++++++++
173  |   (Re)Define a macro.
174  |   
175  |   char *name     macro name
176  |   
177  |   char *def      macro contents
178  |   ++++++++++++++++++++++++++++++++++++++*/
179  | void
180  | ER_make_macro(char *name, char *def)
181  | {
182  |   char *cp_name = UT_strdup(name);
183  |   char *cp_def  = UT_strdup(def);
184  | 
185  |   void *oldkey, *oldval;
186  | 
187  |   TH_acquire_write_lock( &er_paths_lock );
188  | 
189  |   /* cleanup on redefinition */
190  |   if( g_hash_table_lookup_extended(er_macro_hash, name, 
191  | 				   &oldkey, &oldval) == TRUE ) {
192  |     g_hash_table_remove(er_macro_hash, name);
193  |     UT_free(oldkey);
194  |     UT_free(oldval);
195  |   }
196  |   
197  |   g_hash_table_insert(er_macro_hash, cp_name, cp_def);
198  | 
199  |   TH_release_write_lock( &er_paths_lock );
200  | }
201  | 
202  | 
203  | /*++++++++++++++++++++++++++++++++++++++
204  |   
205  |   predefine some macros useful for the whois_rip server.
206  |   XXX - this should not be here, it should be done via the CA module!
207  | 
208  |   ++++++++++++++++++++++++++++++++++++++*/
209  | void
210  | ER_macro_predef(void)
211  | {
212  | 
213  | #define DBUPDLOG_FORMAT "  FORMAT SEVCHAR|FACSYMB|TEXTLONG|DATETIME|PIDFULL|PROGNAME|MNEMONIC  "
214  | #define RIPLOG_FORMAT   "  FORMAT SEVCHAR|FACSYMB|TEXTLONG|DATETIME|PIDFULL|PROGNAME|THR_ID|MNEMONIC  "
215  | 
216  |   /* catch-all for dbupdate */
217  |   ER_make_macro("DBUPERR", "CREATE dbuperr {"
218  | 		DBUPDLOG_FORMAT "NAME '$(1)' DATE}"
219  | 		" ( FAC MM|UP SEV W- )");
220  | 
221  |   /* catch-all for rip */
222  |   ER_make_macro("ALLRIPERR", "CREATE allriperr { " 
223  | 		RIPLOG_FORMAT "NAME '$(1)' DATE}" 
224  | 		" (FAC ALL SEV W- )");
225  | 
226  |   /* selected: errors in ripupdate */
227  |   ER_make_macro("RIPUPERR", "CREATE ripuperr {" 
228  | 		RIPLOG_FORMAT "NAME '$(1)' DATE}" 
229  | 		" (FAC UD SEV W- )");
230  | 
231  |   /* querylog: logs all rip queries */
232  |   ER_make_macro("QRYLOG", "CREATE qrylog {" 
233  | 		RIPLOG_FORMAT "NAME '$(1)' DATE}" 
234  | 		" (FAC PW ASP PW_I_QRYLOG SEV I )");
235  | 
236  |   /* audit: any security related messages from RIP */
237  |   ER_make_macro("RIPAUDIT", "CREATE ripaudit {"
238  | 		RIPLOG_FORMAT "NAME '$(1)' DATE}"
239  | 		"( FAC PW ASP PW_I_PASSUN SEV i )" 
240  | 		" (  FAC AC ASP AC_I_PERMBAN SEV I )");
241  | 
242  |   /* ripupdlog: logs all update transactions */
243  |   ER_make_macro("RIPUPDLOG", "CREATE 'ripupdlog_$(2)' {" 
244  | 		RIPLOG_FORMAT "NAME '$(1)_$(2)' DATE}" 
245  | 		" ( FAC UD ASP 0xffffffff SEV I THR self)");
246  | 
247  |   /* ripmirlog */
248  |   ER_make_macro("RIPMIRLOG", "CREATE ripmirlog {"  
249  | 		RIPLOG_FORMAT "NAME '$(1)' DATE }"
250  | 		"( FAC PM ASP 0xffffffff SEV I )");
251  | 
252  |   /* server log: all administration by SV (startup, shutdown, etc) and errors */
253  |   ER_make_macro("RIPSVRLOG", "CREATE ripsvrlog {" 
254  | 		RIPLOG_FORMAT "NAME '$(1)' DATE}" 
255  | 		" ( FAC SV ASP 0xffffffff SEV I-F )");											      
256  |   /* dbase log: all errors of SQ */
257  |   ER_make_macro("SQLOG", " CREATE sqlog {" 
258  | 		RIPLOG_FORMAT "NAME '$(1)' DATE}" 
259  | 		" ( FAC SQ SEV W- )");
260  |   
261  | }
262  | 
263  | 
264  | /*++++++++++++++++++++++++++++++++++++++
265  |   
266  |   Prints the arguments (key and value of a hash) to the given
267  |   connection (used for listing the defined macros)
268  | 
269  |   void * key       hash key
270  | 
271  |   void * value     hash value
272  | 
273  |   void *condat     connection data structure
274  |   ++++++++++++++++++++++++++++++++++++++*/
275  | static
276  | void er_macro_list_hook (void* key, void * value, void *condat)
277  | {
278  |   SK_cd_printf(condat, "%s: %s\n", (char *) key, (char *) value);
279  | }
280  |      
281  | 
282  | 
283  | /*++++++++++++++++++++++++++++++++++++++
284  |   
285  |   Lists all currently defined macros to the given connection.
286  | 
287  |   sk_conn_st *condat  connection data structure
288  |   ++++++++++++++++++++++++++++++++++++++*/
289  | void 
290  | ER_macro_list(sk_conn_st *condat)
291  | {
292  |   TH_acquire_read_lock( &er_paths_lock );
293  |   g_hash_table_foreach(er_macro_hash, er_macro_list_hook, condat );
294  |   TH_release_read_lock( &er_paths_lock );
295  | }
296  | 
297  | 
298  | 
299  | /*++++++++++++++++++++++++++++++++++++++
300  | 
301  |  Defines the macros with the definitions from the config file,
302  |  overriding any currently defined ones if the same name is used.
303  |  
304  |  ++++++++++++++++++++++++++++++++++++++*/
305  | void 
306  | ER_proc_ca_macro(void)
307  | {
308  |   char *alldef = ca_get_er_macro ;
309  |   char *this_line = alldef;
310  |   char *defname, *defbody, *end_line;
311  |   
312  |   /* alldef is a copy of the configured value. so we can modify it
313  |      if it helps us to do it line by line */
314  |   
315  |   /* ER_MACRO may not be present in the configuration, in which case 
316  |      ca_get_er_macro returns NULL */
317  | 
318  |   if( alldef != NULL ) {
319  |     
320  |     while( *this_line != '\0' ) {
321  |       /* separate the line */
322  |       end_line = strchr(this_line, '\n');
323  |       *end_line = '\0';
324  |       
325  |       /* advance to non-whitespace */
326  |       while( isspace(* (unsigned char*) this_line) ) {
327  | 	this_line++;
328  |       }
329  |       
330  |       /* find the name and body of the definition */
331  |       defname = strsep(&this_line, " \t");
332  |       defbody = this_line;
333  |       
334  |       /* fire */
335  |       dieif( defname == NULL || defbody == NULL );
336  |       ER_make_macro( defname, defbody );
337  |       
338  |       this_line = end_line + 1;
339  |     }
340  |     
341  |     UT_free(alldef);
342  |   } 
343  |   
344  | }
345  | 
346  | 
347  | /*++++++++++++++++++++++++++++++++++++++
348  |   
349  |   Processes the error definitions from the config file. The
350  |   definitions can be specified with the use of a macro or without.
351  |   
352  |   ++++++++++++++++++++++++++++++++++++++*/
353  | void 
354  | ER_proc_ca_err(void)
355  | {
356  |   char *alldef = ca_get_er_def ;
357  |   char *this_line = alldef;
358  |   char *end_line;
359  |   char *erret = NULL;
360  |   int res;
361  |  
362  |     /* alldef is a copy of the configured value. so we can modify it
363  |        if it helps us to do it line by line */
364  | 
365  |   /* ER_DEF may not be present in the configuration, in which case 
366  |      ca_get_er_def returns NULL */
367  |   if( alldef != NULL ) {
368  |     
369  |     while( *this_line != '\0' ) {
370  |       /* separate the line */
371  |       end_line = strchr(this_line, '\n');
372  |       *end_line = '\0';
373  |       
374  |       /* fire */      
375  |       if( (res = ER_macro_spec(this_line, &erret)) != 0 ) {
376  | 	fputs(erret, stderr);
377  | 	die;
378  |       }
379  |       
380  |       UT_free(erret); 
381  |       
382  |       this_line = end_line + 1;
383  |     }
384  |     
385  |     UT_free(alldef);
386  |   }
387  | }