Imported Upstream version 2.5.11
[libapache-mod-security.git] / apache2 / msc_test.c
1 /*
2  * ModSecurity for Apache 2.x, http://www.modsecurity.org/
3  * Copyright (c) 2004-2009 Breach Security, Inc. (http://www.breach.com/)
4  *
5  * This product is released under the terms of the General Public Licence,
6  * version 2 (GPLv2). Please refer to the file LICENSE (included with this
7  * distribution) which contains the complete text of the licence.
8  *
9  * There are special exceptions to the terms and conditions of the GPL
10  * as it is applied to this software. View the full text of the exception in
11  * file MODSECURITY_LICENSING_EXCEPTION in the directory of this software
12  * distribution.
13  *
14  * If any of the files related to licensing are missing or if you have any
15  * other questions related to licensing please contact Breach Security, Inc.
16  * directly using the email address support@breach.com.
17  *
18  */
19 #include <apr.h>
20 #include <apr_getopt.h>
21
22 #include "modsecurity.h"
23 #include "re.h"
24 #include "pdf_protect.h"
25
26 #define ISHEX(X) (((X >= '0')&&(X <= '9')) || ((X >= 'a')&&(X <= 'f')) || ((X >= 'A')&&(X <= 'F')))
27
28 #define BUFLEN                   32768
29
30 #define RESULT_SUCCESS           0
31 #define RESULT_ERROR            -1
32 #define RESULT_MISMATCHED       -2
33 #define RESULT_WRONGSIZE        -3
34 #define RESULT_WRONGRET         -4
35
36 #define DEFAULT_ACTION           "phase:2,log,auditlog,pass"
37
38 #define CMDLINE_OPTS             "t:n:p:P:r:I:D:Nh"
39
40 /* Types */
41 typedef struct tfn_data_t tfn_data_t;
42 typedef struct op_data_t op_data_t;
43 typedef struct action_data_t action_data_t;
44
45 struct tfn_data_t {
46     const char               *name;
47     const char               *param;
48     unsigned char            *input;
49     apr_size_t                input_len;
50     msre_tfn_metadata        *metadata;
51 };
52
53 struct op_data_t {
54     const char               *name;
55     const char               *param;
56     unsigned char            *input;
57     apr_size_t                input_len;
58     msre_ruleset             *ruleset;
59     msre_rule                *rule;
60     msre_var                 *var;
61     msre_op_metadata         *metadata;
62 };
63
64 struct action_data_t {
65     const char               *name;
66     unsigned char            *input;
67     apr_size_t                input_len;
68     msre_ruleset             *ruleset;
69     msre_rule                *rule;
70     msre_var                 *var;
71     msre_actionset           *actionset;
72     msre_action              *action;
73 };
74
75
76 /* Globals */
77 static int debuglog_level = 0;
78 static char *test_name = NULL;
79 static apr_pool_t *g_mp = NULL;
80 static modsec_rec *g_msr = NULL;
81 static unsigned char buf[BUFLEN];
82 msc_engine *modsecurity = NULL;
83
84
85 /* Stubs */
86 char *format_error_log_message(apr_pool_t *mp, error_message *em) {
87     return "FAKE ERROR LOG MESSAGE";
88 }
89
90 apr_status_t send_error_bucket(modsec_rec *msr, ap_filter_t *f, int status) {
91     return APR_SUCCESS;
92 }
93
94 int apache2_exec(modsec_rec *msr, const char *command, const char **argv, char **output) {
95     return 0;
96 }
97
98 char *get_apr_error(apr_pool_t *p, apr_status_t rc) {
99     char *text = apr_pcalloc(p, 201);
100     if (text == NULL) return NULL;
101     apr_strerror(rc, text, 200);
102     return text;
103 }
104
105 void msr_log(modsec_rec *msr, int level, const char *text, ...) {
106     va_list ap;
107     char str1[1024] = "";
108     char str2[1256] = "";
109
110     if ((msr == NULL) || (level > msr->txcfg->debuglog_level)) {
111         return;
112     }
113     if (msr->txcfg->debuglog_fd == NOT_SET_P) {
114         if (apr_file_open(&msr->txcfg->debuglog_fd, msr->txcfg->debuglog_name, APR_READ|APR_WRITE|APR_CREATE|APR_APPEND|APR_BINARY, APR_OS_DEFAULT, g_mp) != APR_SUCCESS) {
115             fprintf(stderr, "ERROR: failed to create unit test debug log \"%s\".\n", msr->txcfg->debuglog_name);
116             msr->txcfg->debuglog_fd = NULL;
117         }
118     }
119
120     va_start(ap, text);
121     if (msr->txcfg->debuglog_fd != NULL) {
122         apr_size_t nbytes_written = 0;
123         apr_vsnprintf(str1, sizeof(str1), text, ap);
124         apr_snprintf(str2, sizeof(str2), "%lu: [%d] [%s] %s\n", (unsigned long)getpid(), level, test_name, str1);
125
126         apr_file_write_full(msr->txcfg->debuglog_fd, str2, strlen(str2), &nbytes_written);
127     }
128     va_end(ap);
129 }
130
131 void msr_log_error(modsec_rec *msr, const char *text, ...) {
132     va_list ap;
133     int level = 3;
134     char str1[1024] = "";
135     char str2[1256] = "";
136
137     if ((msr == NULL) || (level > msr->txcfg->debuglog_level)) {
138         return;
139     }
140     if (msr->txcfg->debuglog_fd == NOT_SET_P) {
141         if (apr_file_open(&msr->txcfg->debuglog_fd, msr->txcfg->debuglog_name, APR_READ|APR_WRITE|APR_CREATE|APR_APPEND|APR_BINARY, APR_OS_DEFAULT, g_mp) != APR_SUCCESS) {
142             fprintf(stderr, "ERROR: failed to create unit test debug log \"%s\".\n", msr->txcfg->debuglog_name);
143             msr->txcfg->debuglog_fd = NULL;
144         }
145     }
146
147     va_start(ap, text);
148     if (msr->txcfg->debuglog_fd != NULL) {
149         apr_size_t nbytes_written = 0;
150         apr_vsnprintf(str1, sizeof(str1), text, ap);
151         apr_snprintf(str2, sizeof(str2), "%lu: [%d] [%s] %s\n", (unsigned long)getpid(), level, test_name, str1);
152
153         apr_file_write_full(msr->txcfg->debuglog_fd, str2, strlen(str2), &nbytes_written);
154     }
155     va_end(ap);
156 }
157
158 void msr_log_warn(modsec_rec *msr, const char *text, ...) {
159     va_list ap;
160     int level = 4;
161     char str1[1024] = "";
162     char str2[1256] = "";
163
164     if ((msr == NULL) || (level > msr->txcfg->debuglog_level)) {
165         return;
166     }
167     if (msr->txcfg->debuglog_fd == NOT_SET_P) {
168         if (apr_file_open(&msr->txcfg->debuglog_fd, msr->txcfg->debuglog_name, APR_READ|APR_WRITE|APR_CREATE|APR_APPEND|APR_BINARY, APR_OS_DEFAULT, g_mp) != APR_SUCCESS) {
169             fprintf(stderr, "ERROR: failed to create unit test debug log \"%s\".\n", msr->txcfg->debuglog_name);
170             msr->txcfg->debuglog_fd = NULL;
171         }
172     }
173
174     va_start(ap, text);
175     if (msr->txcfg->debuglog_fd != NULL) {
176         apr_size_t nbytes_written = 0;
177         apr_vsnprintf(str1, sizeof(str1), text, ap);
178         apr_snprintf(str2, sizeof(str2), "%lu: [%d] [%s] %s\n", (unsigned long)getpid(), level, test_name, str1);
179
180         apr_file_write_full(msr->txcfg->debuglog_fd, str2, strlen(str2), &nbytes_written);
181     }
182     va_end(ap);
183 }
184
185 const char *ap_get_remote_host(conn_rec *conn, void *dir_config, int type, int *str_is_ip) {
186     return "FAKE-REMOTE-HOST";
187 }
188
189 char *get_env_var(request_rec *r, char *name) {
190     return "FAKE-ENV-VAR";
191 }
192
193 apr_status_t unixd_set_global_mutex_perms(apr_global_mutex_t *gmutex) {
194     return APR_SUCCESS;
195 }
196
197 apr_status_t unixd_set_proc_mutex_perms(apr_proc_mutex_t *pmutex) {
198     return APR_SUCCESS;
199 }
200
201
202 /* Escaping functions */
203
204 static unsigned char hex2dec(unsigned char *what) {
205     register unsigned char digit;
206
207     digit = (what[0] >= 'A' ? ((what[0] & 0xdf) - 'A') + 10 : (what[0] - '0'));
208     digit *= 16;
209     digit += (what[1] >= 'A' ? ((what[1] & 0xdf) - 'A') + 10 : (what[1] - '0'));
210
211     return digit;
212 }
213
214 static unsigned char *unescape_inplace(unsigned char *str, apr_size_t *len)
215 {
216     apr_size_t i, j;
217     for (i = j = 0; i < *len; j++) {
218         if ((str[i] == '\\') && (i + 3 < *len) && (str[i + 1] == 'x') && ISHEX(str[i + 2]) && ISHEX(str[i + 3]) ) {
219             str[j] = hex2dec(str + i + 2);
220             i += 4;
221         }
222         else {
223             str[j] = str[i++];
224         }
225     }
226     *len = j;
227
228     while (j < i) {
229         str[j++] = '\0';
230     }
231
232     return str;
233 }
234
235 static char *escape(unsigned char *str, apr_size_t *len)
236 {
237     char *new = apr_pcalloc(g_mp, (*len * 4) + 1);
238     apr_size_t i, j;
239     for (i = j = 0; i < *len; i++) {
240         if ((str[i] >= 0x20) && (str[i] <= 0x7e)) {
241             new[j++] = str[i];
242         }
243         else {
244             sprintf(new + j, "\\x%02x", str[i]);
245             j += 4;
246         }
247     }
248     *len = j;
249
250     return new;
251 }
252
253
254 /* Testing functions */
255
256 static int init_tfn(tfn_data_t *data, const char *name, unsigned char *input, apr_size_t input_len, char **errmsg) {
257     *errmsg = NULL;
258
259     data->name = name;
260     data->input = apr_pmemdup(g_mp, input, input_len);
261     data->input_len = input_len;
262     data->metadata = msre_engine_tfn_resolve(modsecurity->msre, name);
263     if (data->metadata == NULL) {
264         *errmsg = apr_psprintf(g_mp, "Failed to fetch tfn \"%s\".", name);
265         return -1;
266     }
267
268     return 0;
269 }
270
271 static int test_tfn(tfn_data_t *data, unsigned char **rval, apr_size_t *rval_len, char **errmsg)
272 {
273     int rc = -1;
274
275     *errmsg = NULL;
276
277     /* Execute the tfn */
278     rc = data->metadata->execute(g_mp, data->input, (long)(data->input_len), (char **)rval, (long *)rval_len);
279     if (rc < 0) {
280         *errmsg = apr_psprintf(g_mp, "Failed to execute tfn \"%s\".", data->name);
281     }
282
283     return rc;
284 }
285
286 static int init_op(op_data_t *data, const char *name, const char *param, unsigned char *input, apr_size_t input_len, char **errmsg) {
287     const char *args = apr_psprintf(g_mp, "@%s %s", name, param);
288     char *conf_fn;
289     int rc = -1;
290
291     *errmsg = NULL;
292
293     data->name = name;
294     data->param = param;
295     data->input = input;
296     data->input_len = input_len;
297
298     if ( apr_filepath_merge(&conf_fn, NULL, "t/unit-test.conf", APR_FILEPATH_TRUENAME, g_mp) != APR_SUCCESS) {
299         *errmsg = apr_psprintf(g_mp, "Failed to build a conf filename.");
300         return -1;
301     }
302
303     /* Register UNIT_TEST variable */
304     msre_engine_variable_register(modsecurity->msre,
305         "UNIT_TEST",
306         VAR_SIMPLE,
307         0, 0,
308         NULL,
309         NULL,
310         VAR_DONT_CACHE,
311         PHASE_REQUEST_HEADERS
312     );
313
314     /* Lookup the operator */
315     data->metadata = msre_engine_op_resolve(modsecurity->msre, name);
316     if (data->metadata == NULL) {
317         *errmsg = apr_psprintf(g_mp, "Failed to fetch op \"%s\".", name);
318         return -1;
319     }
320
321     /* Create a ruleset/rule */
322     data->ruleset = msre_ruleset_create(modsecurity->msre, g_mp);
323     if (data->ruleset == NULL) {
324         *errmsg = apr_psprintf(g_mp, "Failed to create ruleset for op \"%s\".", name);
325         return -1;
326     }
327     data->rule = msre_rule_create(data->ruleset, RULE_TYPE_NORMAL, conf_fn, 1, "UNIT_TEST", args, DEFAULT_ACTION, errmsg);
328     if (data->rule == NULL) {
329         *errmsg = apr_psprintf(g_mp, "Failed to create rule for op \"%s\": %s", name, *errmsg);
330         return -1;
331     }
332
333     /* Create a fake variable */
334     data->var = (msre_var *)apr_pcalloc(g_mp, sizeof(msre_var));
335     data->var->name = "UNIT_TEST";
336     data->var->value = apr_pstrmemdup(g_mp, (char *)input, input_len);
337     data->var->value_len = input_len;
338     data->var->metadata = msre_resolve_var(modsecurity->msre, data->var->name);
339     if (data->var->metadata == NULL) {
340         *errmsg = apr_psprintf(g_mp, "Failed to resolve variable for op \"%s\": %s", name, data->var->name);
341         return -1;
342     }
343
344     /* Initialize the operator parameter */
345     if (data->metadata->param_init != NULL) {
346         rc = data->metadata->param_init(data->rule, errmsg);
347         if (rc <= 0) {
348             *errmsg = apr_psprintf(g_mp, "Failed to init op \"%s\": %s", name, *errmsg);
349             return rc;
350         }
351     }
352
353     return 0;
354 }
355
356 static int test_op(op_data_t *data, char **errmsg)
357 {
358     int rc = -1;
359
360     *errmsg = NULL;
361
362     /* Execute the operator */
363     if (data->metadata->execute != NULL) {
364         rc = data->metadata->execute(g_msr, data->rule, data->var, errmsg);
365         if (rc < 0) {
366             *errmsg = apr_psprintf(g_mp, "Failed to execute op \"%s\": %s", data->name, *errmsg);
367         }
368     }
369
370     return rc;
371 }
372
373 static int init_action(action_data_t *data, const char *name, const char *param, char **errmsg)
374 {
375     const char *action_string = NULL;
376     char *conf_fn;
377
378     *errmsg = NULL;
379
380     if ((param == NULL) || (strcmp("", param) == 0)) {
381         action_string = apr_psprintf(g_mp, "%s", name);
382     }
383     else {
384         action_string = apr_psprintf(g_mp, "%s:%s", name, param);
385     }
386     if (action_string == NULL) {
387         *errmsg = apr_psprintf(g_mp, "Failed to build action string for action: \"%s\".", name);
388         return -1;
389     }
390
391     if ( apr_filepath_merge(&conf_fn, NULL, "t/unit-test.conf", APR_FILEPATH_TRUENAME, g_mp) != APR_SUCCESS) {
392         *errmsg = apr_psprintf(g_mp, "Failed to build a conf filename.");
393         return -1;
394     }
395
396     /* Register UNIT_TEST variable */
397     msre_engine_variable_register(modsecurity->msre,
398         "UNIT_TEST",
399         VAR_SIMPLE,
400         0, 0,
401         NULL,
402         NULL,
403         VAR_DONT_CACHE,
404         PHASE_REQUEST_HEADERS
405     );
406
407     /* Create a ruleset/rule */
408     data->ruleset = msre_ruleset_create(modsecurity->msre, g_mp);
409     if (data->ruleset == NULL) {
410         *errmsg = apr_psprintf(g_mp, "Failed to create ruleset for action \"%s\".", name);
411         return -1;
412     }
413     data->rule = msre_rule_create(data->ruleset, RULE_TYPE_NORMAL, conf_fn, 1, "UNIT_TEST", "@unconditionalMatch", action_string, errmsg);
414     if (data->rule == NULL) {
415         *errmsg = apr_psprintf(g_mp, "Failed to create rule for action \"%s\": %s", name, *errmsg);
416         return -1;
417     }
418
419     /* Get the actionset/action */
420     data->actionset = data->rule->actionset;
421     if (data->actionset == NULL) {
422         *errmsg = apr_psprintf(g_mp, "Failed to fetch actionset for action \"%s\"", name);
423         return -1;
424     }
425     data->action = (msre_action *)apr_table_get(data->actionset->actions, name);
426     if (data->action == NULL) {
427         *errmsg = apr_psprintf(g_mp, "Failed to fetch action for action \"%s\"", name);
428         return -1;
429     }
430
431     return 0;
432 }
433
434 static int test_action(action_data_t *data, char **errmsg)
435 {
436     int rc = -1;
437
438     *errmsg = NULL;
439
440     /* Execute the action */
441     if (data->action->metadata->execute != NULL) {
442         rc = data->action->metadata->execute(g_msr, g_mp, data->rule, data->action);
443         if (rc < 0) {
444             *errmsg = apr_psprintf(g_mp, "Failed to execute action \"%s\": %d", data->name, rc);
445         }
446     }
447
448     return rc;
449 }
450
451
452 /* Initialization */
453 static void init_msr(void)
454 {
455     directory_config *dcfg = NULL;
456     request_rec *r = NULL;
457     r = (request_rec *)apr_pcalloc(g_mp, sizeof(request_rec));
458
459     dcfg = (directory_config *)apr_pcalloc(g_mp, sizeof(directory_config));
460     dcfg->is_enabled = 0;
461     dcfg->reqbody_access = 0;
462     dcfg->reqbody_buffering = 0;
463     dcfg->reqbody_inmemory_limit = REQUEST_BODY_DEFAULT_INMEMORY_LIMIT;
464     dcfg->reqbody_limit = REQUEST_BODY_DEFAULT_LIMIT;
465     dcfg->reqbody_no_files_limit = REQUEST_BODY_NO_FILES_DEFAULT_LIMIT;
466     dcfg->resbody_access = 0;
467     dcfg->of_limit = RESPONSE_BODY_DEFAULT_LIMIT;
468     dcfg->of_limit_action = RESPONSE_BODY_LIMIT_ACTION_REJECT;
469     dcfg->debuglog_fd = NOT_SET_P;
470     dcfg->debuglog_name = "msc-test-debug.log";
471     dcfg->debuglog_level = debuglog_level;
472     dcfg->cookie_format = 0;
473     dcfg->argument_separator = '&';
474     dcfg->rule_inheritance = 0;
475     dcfg->auditlog_flag = 0;
476     dcfg->auditlog_type = AUDITLOG_SERIAL;
477     dcfg->auditlog_fd = NULL;
478     dcfg->auditlog2_fd = NULL;
479     dcfg->auditlog_name = NULL;
480     dcfg->auditlog2_name = NULL;
481     dcfg->auditlog_storage_dir = NULL;
482     dcfg->auditlog_parts = "ABCFHZ";
483     dcfg->auditlog_relevant_regex = NULL;
484     dcfg->tmp_dir = guess_tmp_dir(g_mp);
485     dcfg->upload_dir = NULL;
486     dcfg->upload_keep_files = KEEP_FILES_OFF;
487     dcfg->upload_validates_files = 0;
488     dcfg->data_dir = ".";
489     dcfg->webappid = "default";
490     dcfg->content_injection_enabled = 0;
491     dcfg->pdfp_enabled = 0;
492     dcfg->pdfp_secret = NULL;
493     dcfg->pdfp_timeout = 10;
494     dcfg->pdfp_token_name = "PDFPTOKEN";
495     dcfg->pdfp_only_get = 1;
496     dcfg->pdfp_method = PDF_PROTECT_METHOD_TOKEN_REDIRECTION;
497     dcfg->geo = NULL;
498     dcfg->cache_trans = MODSEC_CACHE_ENABLED;
499     dcfg->cache_trans_min = 15;
500     dcfg->cache_trans_max = 0;
501     dcfg->request_encoding = NULL;
502
503     g_msr = (modsec_rec *)apr_pcalloc(g_mp, sizeof(modsec_rec));
504     g_msr->modsecurity = modsecurity;
505     g_msr->mp = g_mp;
506     g_msr->r = r;
507     g_msr->r_early = r;
508     g_msr->request_time = apr_time_now();
509     g_msr->dcfg1 = NULL;
510     g_msr->usercfg = NULL;
511     g_msr->txcfg = dcfg;
512     g_msr->txid = "FAKE-TXID";
513     g_msr->error_messages = NULL;
514     g_msr->alerts = NULL;
515     g_msr->server_software = "FAKE-SERVER-SOFTWARE";
516     g_msr->local_addr = "127.0.0.1";
517     g_msr->local_port = 80;
518     g_msr->remote_addr = "127.0.0.1";
519     g_msr->remote_port = 1080;
520     g_msr->request_line = "GET /unit-tests HTTP/1.1";
521     g_msr->request_uri = "http://localhost/unit-tests";
522     g_msr->request_method = "GET";
523     g_msr->query_string = "";
524     g_msr->request_protocol = "HTTP/1.1";
525     g_msr->request_headers = NULL;
526     g_msr->hostname = "localhost";
527     g_msr->msc_rule_mptmp = g_mp;
528     g_msr->tx_vars = apr_table_make(g_mp, 1);
529     g_msr->collections_original = apr_table_make(g_mp, 1);
530     g_msr->collections = apr_table_make(g_mp, 1);
531     g_msr->collections_dirty = apr_table_make(g_mp, 1);
532 }
533
534 /**
535  * Usage text.
536  */
537 static void usage(void)
538 {
539     fprintf(stderr, "ModSecurity Unit Tester v%s\n", MODSEC_VERSION);
540     fprintf(stderr, "  Usage: msc_test [options]\n");
541     fprintf(stderr, "\n");
542     fprintf(stderr, "  Options:\n");
543     fprintf(stderr, "    -t        Type (required)\n");
544     fprintf(stderr, "    -n        Name (required)\n");
545     fprintf(stderr, "    -p        Parameter (required)\n");
546     fprintf(stderr, "    -P        Prerun (optional for actions)\n");
547     fprintf(stderr, "    -r        Function return code (required for some types)\n");
548     fprintf(stderr, "    -I        Iterations (default 1)\n");
549     fprintf(stderr, "    -D        Debug log level (default 0)\n");
550     fprintf(stderr, "    -N        No input on stdin.\n\n");
551     fprintf(stderr, "    -h        This help\n\n");
552     fprintf(stderr, "\n");
553     fprintf(stderr, "Input is from stdin unless -N is used.\n");
554 }
555
556
557
558 /* Main */
559
560 int main(int argc, const char * const argv[])
561 {
562     apr_getopt_t *opt;
563     apr_file_t *fd;
564     apr_size_t nbytes = 0;
565     const char *type = NULL;
566     const char *name = NULL;
567     unsigned char *param = NULL;
568     unsigned char *prerun = NULL;
569     const char *returnval = NULL;
570     int iterations = 1;
571     char *errmsg = NULL;
572     unsigned char *out = NULL;
573     apr_size_t param_len = 0;
574     apr_size_t prerun_len = 0;
575     apr_size_t out_len = 0;
576     int noinput = 0;
577     int rc = 0;
578     int result = 0;
579     int ec = 0;
580     int i;
581     apr_time_t T0 = 0;
582     apr_time_t T1 = 0;
583     tfn_data_t tfn_data;
584     op_data_t op_data;
585     action_data_t action_data;
586     int ret = 0;
587
588     memset(&tfn_data, 0, sizeof(tfn_data_t));
589     memset(&op_data, 0, sizeof(op_data_t));
590     memset(&action_data, 0, sizeof(action_data_t));
591
592     apr_app_initialize(&argc, &argv, NULL);
593     atexit(apr_terminate);
594
595     apr_pool_create(&g_mp, NULL);
596
597     rc = apr_getopt_init(&opt, g_mp, argc, argv);
598     if (rc != APR_SUCCESS) {
599         fprintf(stderr, "Failed to initialize.\n\n");
600         usage();
601         exit(1);
602     }
603
604     do {
605         char  ch;
606         const char *val;
607         rc = apr_getopt(opt, CMDLINE_OPTS, &ch, &val);
608         switch (rc) {
609         case APR_SUCCESS:
610             switch (ch) {
611                 case 't':
612                     type = val;
613                     break;
614                 case 'n':
615                     name = val;
616                     break;
617                 case 'p':
618                     param_len = strlen(val);
619                     param = apr_pmemdup(g_mp, val, param_len + 1);
620                     unescape_inplace(param, &param_len);
621                     break;
622                 case 'P':
623                     prerun_len = strlen(val);
624                     prerun = apr_pmemdup(g_mp, val, prerun_len + 1);
625                     unescape_inplace(prerun, &prerun_len);
626                     break;
627                 case 'r':
628                     returnval = val;
629                     break;
630                 case 'I':
631                     iterations = atoi(val);
632                     break;
633                 case 'D':
634                     debuglog_level = atoi(val);
635                     break;
636                 case 'N':
637                     noinput = 1;
638                     break;
639                 case 'h':
640                     usage();
641                     exit(0);
642             }
643             break;
644         case APR_BADCH:
645         case APR_BADARG:
646             usage();
647             exit(1);
648         }
649     } while (rc != APR_EOF);
650
651     rc = apr_getopt_init(&opt, g_mp, argc, argv);
652     if (!type || !name || !param) {
653         usage();
654         exit(1);
655     }
656
657     modsecurity = modsecurity_create(g_mp, MODSEC_OFFLINE);
658     test_name = apr_psprintf(g_mp, "%s/%s", type, name);
659
660     if (noinput == 0) {
661         if (apr_file_open_stdin(&fd, g_mp) != APR_SUCCESS) {
662             fprintf(stderr, "Failed to open stdin\n");
663             exit(1);
664         }
665
666         /* Read in the input */
667         nbytes = BUFLEN;
668         memset(buf, 0, nbytes);
669         rc = apr_file_read(fd, buf, &nbytes);
670         if ((rc != APR_EOF) && (rc != APR_SUCCESS)) {
671             fprintf(stderr, "Failed to read data\n");
672             exit(1);
673         }
674
675         if (nbytes < 0) {
676             fprintf(stderr, "Error reading data\n");
677             exit(1);
678         }
679
680         apr_file_close(fd);
681     }
682
683     if (strcmp("tfn", type) == 0) {
684         ret = returnval ? atoi(returnval) : -8888;
685
686         rc = init_tfn(&tfn_data, name, buf, nbytes, &errmsg);
687         if (rc < 0) {
688             fprintf(stderr, "ERROR: %s\n", errmsg);
689             result = RESULT_ERROR;
690         }
691     }
692     else if (strcmp("op", type) == 0) {
693         if (!returnval) {
694             fprintf(stderr, "Return value required for type \"%s\"\n", type);
695             exit(1);
696         }
697         ret = atoi(returnval);
698
699         init_msr();
700
701         rc = init_op(&op_data, name, (const char *)param, buf, nbytes, &errmsg);
702         if (rc < 0) {
703             fprintf(stderr, "ERROR: %s\n", errmsg);
704             result = RESULT_ERROR;
705         }
706     }
707     else if (strcmp("action", type) == 0) {
708         if (!returnval) {
709             fprintf(stderr, "Return value required for type \"%s\"\n", type);
710             exit(1);
711         }
712         ret = atoi(returnval);
713
714         init_msr();
715
716         if (prerun) {
717             action_data_t paction_data;
718             char *pname = apr_pstrdup(g_mp, (const char *)prerun);
719             char *pparam = NULL;
720
721             if ((pparam = strchr((const char *)pname, ':'))) {
722                 pparam[0] = '\0';
723                 pparam++;
724             }
725
726             rc = init_action(&paction_data, pname, (const char *)pparam, &errmsg);
727             if (rc < 0) {
728                 fprintf(stderr, "ERROR: prerun - %s\n", errmsg);
729                 exit(1);
730             }
731
732             rc = test_action(&paction_data, &errmsg);
733             if (rc < 0) {
734                 fprintf(stderr, "ERROR: prerun - %s\n", errmsg);
735                 exit(1);
736             }
737         }
738
739         rc = init_action(&action_data, name, (const char *)param, &errmsg);
740         if (rc < 0) {
741             fprintf(stderr, "ERROR: %s\n", errmsg);
742             result = RESULT_ERROR;
743         }
744     }
745
746     if (iterations > 1) {
747         apr_time_clock_hires (g_mp);
748         T0 = apr_time_now();
749     }
750
751     for (i = 1; i <= iterations; i++) {
752         #ifdef VERBOSE
753         if (i % 100 == 0) {
754             if (i == 100) {
755                 fprintf(stderr, "Iterations/100: .");
756             }
757             else {
758                 fprintf(stderr, ".");
759             }
760         }
761         #endif
762
763         if (strcmp("tfn", type) == 0) {
764             /* Transformations */
765             rc = test_tfn(&tfn_data, &out, &out_len, &errmsg);
766             if (rc < 0) {
767                 fprintf(stderr, "ERROR: %s\n", errmsg);
768                 result = RESULT_ERROR;
769             }
770             else if ((ret != -8888) && (rc != ret)) {
771                 fprintf(stderr, "Returned %d (expected %d)\n", rc, ret);
772                 result = RESULT_WRONGRET;
773             }
774             else if (param_len != out_len) {
775                 fprintf(stderr, "Lenth %" APR_SIZE_T_FMT " (expected %" APR_SIZE_T_FMT ")\n", out_len, param_len);
776                 result = RESULT_WRONGSIZE;
777             }
778             else {
779                 result = memcmp(param, out, param_len) ? RESULT_MISMATCHED : RESULT_SUCCESS;
780             }
781
782             if (result != RESULT_SUCCESS) {
783                 apr_size_t s0len = nbytes;
784                 const char *s0 = escape(buf, &s0len);
785                 apr_size_t s1len = out_len;
786                 const char *s1 = escape(out, &s1len);
787                 apr_size_t s2len = param_len;
788                 const char *s2 = escape(param, &s2len);
789
790                 fprintf(stderr, " Input: '%s' len=%" APR_SIZE_T_FMT "\n"
791                                 "Output: '%s' len=%" APR_SIZE_T_FMT "\n"
792                                 "Expect: '%s' len=%" APR_SIZE_T_FMT "\n",
793                                 s0, nbytes, s1, out_len, s2, param_len);
794                 ec = 1;
795             }
796         }
797         else if (strcmp("op", type) == 0) {
798             /* Operators */
799             rc = test_op(&op_data, &errmsg);
800             if (rc < 0) {
801                 fprintf(stderr, "ERROR: %s\n", errmsg);
802                 result = RESULT_ERROR;
803             }
804             else if (rc != ret) {
805                 fprintf(stderr, "Returned %d (expected %d)\n", rc, ret);
806                 result = RESULT_WRONGRET;
807             }
808             else {
809                 result = RESULT_SUCCESS;
810             }
811
812             if (result != RESULT_SUCCESS) {
813                 apr_size_t s0len = nbytes;
814                 const char *s0 = escape(buf, &s0len);
815
816                 fprintf(stderr, " Test: '@%s %s'\n"
817                                 "Input: '%s' len=%" APR_SIZE_T_FMT "\n",
818                                 name, param, s0, nbytes);
819                 ec = 1;
820             }
821         }
822         else if (strcmp("action", type) == 0) {
823             /* Actions */
824             int n;
825             const apr_array_header_t *arr;
826             apr_table_entry_t *te;
827
828             rc = test_action(&action_data, &errmsg);
829             if (rc < 0) {
830                 fprintf(stderr, "ERROR: %s\n", errmsg);
831                 result = RESULT_ERROR;
832             }
833             else if (rc != ret) {
834                 fprintf(stderr, "Returned %d (expected %d)\n", rc, ret);
835                 result = RESULT_WRONGRET;
836             }
837             else {
838                 result = RESULT_SUCCESS;
839             }
840
841             if (result != RESULT_SUCCESS) {
842                 fprintf(stderr, "  Test: '%s:%s'\n"
843                                 "Prerun: '%s'\n",
844                                 name, param, (prerun ? (const char *)prerun : ""));
845                 ec = 1;
846             }
847
848             /* Store any collections that were initialized and changed */
849             arr = apr_table_elts(g_msr->collections);
850             te = (apr_table_entry_t *)arr->elts;
851             for (n = 0; n < arr->nelts; n++) {
852                 apr_table_t *col = (apr_table_t *)te[n].val;
853 //                apr_table_t *orig_col = NULL;
854
855                 if (g_msr->txcfg->debuglog_level >= 9) {
856                     msr_log(g_msr, 9, "Found loaded collection: %s", te[n].key);
857                 }
858                 /* Only store those collections that changed. */
859                 if (apr_table_get(g_msr->collections_dirty, te[n].key)) {
860                     int x = collection_store(g_msr, col);
861
862                     if (g_msr->txcfg->debuglog_level >= 9) {
863                         msr_log(g_msr, 9, "Stored collection: %s (%d)", te[n].key, x);
864                     }
865                 }
866 #if 0
867                 /* Re-populate the original values with the new ones. */
868                 if ((orig_col = (apr_table_t *)apr_table_get(g_msr->collections_original, te[n].key)) != NULL) {
869                     const apr_array_header_t *orig_arr = apr_table_elts(orig_col);
870                     apr_table_entry_t *orig_te = (apr_table_entry_t *)orig_arr->elts;
871                     int m;
872
873                     for (m = 0; m < orig_arr->nelts; m++) {
874                         msc_string *mstr = (msc_string *)apr_table_get(col, orig_te[m].key);
875
876                         if (g_msr->txcfg->debuglog_level >= 9) {
877                             msr_log(g_msr, 9, "Updating original collection: %s.%s=%s", te[n].key, mstr->name, mstr->value);
878                         }
879                         //apr_table_setn(orig_col, orig_te[m].key, (void *)mstr );
880                         collection_original_setvar(g_msr, te[n].key, mstr);
881
882                         
883                     }
884                 }
885 #endif
886             }
887             apr_table_clear(g_msr->collections_dirty);
888             apr_table_clear(g_msr->collections_original);
889         }
890         else {
891             fprintf(stderr, "Unknown type: \"%s\"\n", type);
892             exit(1);
893         }
894
895         if (ec != 0) {
896             fprintf(stdout, "%s\n", errmsg ? errmsg : "");
897             return ec;
898         }
899     }
900
901     if (iterations > 1) {
902         double dT;
903         T1 = apr_time_now();
904
905         dT = apr_time_as_msec(T1 - T0);
906
907         #ifdef VERBOSE
908         if (i >= 100) {
909             fprintf(stderr, "\n");
910         }
911         #endif
912
913         fprintf(stdout, "%d @ %.4f msec per iteration.\n", iterations, dT / iterations);
914     }
915     fprintf(stdout, "%s\n", errmsg ? errmsg : "");
916
917     return ec;
918 }
919
920