1 /**
2  * Copyright 2006 Christian Liesch
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 /**
18  * @file
19  *
20  * @Author christian liesch <liesch@gmx.ch>
21  *
22  * Implementation of the HTTP Test Tool worker.
23  */
24 
25 /************************************************************************
26  * Includes
27  ***********************************************************************/
28 #ifdef HAVE_CONFIG_H
29 #include <config.h>
30 #endif
31 #include <apr_version.h>
32 #include "defines.h"
33 
34 #include <apr.h>
35 #include <apr_lib.h>
36 #include <apr_errno.h>
37 #include <apr_strings.h>
38 #include <apr_network_io.h>
39 #include <apr_thread_proc.h>
40 #include <apr_thread_cond.h>
41 #include <apr_thread_mutex.h>
42 #include <apr_portable.h>
43 #include <apr_hash.h>
44 #include <apr_base64.h>
45 #include <apr_hooks.h>
46 #include <apr_env.h>
47 
48 #if APR_HAVE_UNISTD_H
49 #include <unistd.h> /* for getpid() */
50 #endif
51 
52 #include "defines.h"
53 #include "util.h"
54 #include "replacer.h"
55 #include "regex.h"
56 #include "file.h"
57 #include "transport.h"
58 #include "socket.h"
59 #include "worker.h"
60 #include "module.h"
61 #include "eval.h"
62 #include "tcp_module.h"
63 
64 
65 /************************************************************************
66  * Definitions
67  ***********************************************************************/
68 
69 typedef struct write_buf_to_file_s {
70   char *buf;
71   apr_size_t len;
72   apr_file_t *fp;
73 } write_buf_to_file_t;
74 
75 typedef struct tunnel_s {
76   sockreader_t *sockreader;
77   socket_t *sendto;
78 } tunnel_t;
79 
80 typedef struct flush_s {
81 #define FLUSH_DO_NONE 0
82 #define FLUSH_DO_SKIP 1
83   int flags;
84 } flush_t;
85 
86 typedef struct replacer_s {
87   int unresolved;
88   apr_pool_t *ptmp;
89   worker_t *worker;
90 } replacer_t;
91 
92 #define RECORDER_CONFIG "RECORDER"
93 typedef struct recorder_s {
94   int on;
95 #define RECORDER_OFF 0
96 #define RECORDER_RECORD 1
97 #define RECORDER_PLAY 2
98   int flags;
99 #define RECORDER_RECORD_NONE 0
100 #define RECORDER_RECORD_STATUS 1
101 #define RECORDER_RECORD_HEADERS 2
102 #define RECORDER_RECORD_BODY 4
103 #define RECORDER_RECORD_ALL RECORDER_RECORD_STATUS|RECORDER_RECORD_HEADERS|RECORDER_RECORD_BODY
104   sockreader_t *sockreader;
105 } recorder_t;
106 
107 #define SH_CONFIG "SH"
108 typedef struct sh_s {
109   apr_pool_t *pool;
110   apr_file_t *tmpf;
111 } sh_t;
112 
113 #define EXEC_CONFIG "EXEC"
114 typedef struct exec_s {
115   apr_pool_t *pool;
116   apr_proc_t *proc;
117 } exec_t;
118 
119 
120 /************************************************************************
121  * Globals
122  ***********************************************************************/
123 extern int success;
124 
125 /************************************************************************
126  * Implementation
127  ***********************************************************************/
128 
worker_get_file_and_line(worker_t * worker)129 const char *worker_get_file_and_line(worker_t *worker) {
130   if (worker && worker->lines) {
131     apr_table_entry_t *e =
132       (apr_table_entry_t *) apr_table_elts(worker->lines)->elts;
133     if (worker->cmd < apr_table_elts(worker->lines)->nelts) {
134       return e[worker->cmd].key;
135     }
136   }
137   return NULL;
138 }
139 
140 /**
141  * checked lock function, will exit FAILED if status not ok
142  *
143  * @param mutex IN mutex
144  */
lock(apr_thread_mutex_t * mutex)145 void lock(apr_thread_mutex_t *mutex) {
146   apr_status_t status;
147   if ((status = apr_thread_mutex_lock(mutex)) != APR_SUCCESS) {
148     apr_pool_t *ptmp;
149     HT_POOL_CREATE(&ptmp);
150     success = 0;
151     fprintf(stderr, "could not lock: %s(%d)\n",
152 	    my_status_str(ptmp, status), status);
153     exit(1);
154   }
155 }
156 
157 /**
158  * checked unlock function, will exit FAILED if status not ok
159  *
160  * @param mutex IN mutex
161  */
unlock(apr_thread_mutex_t * mutex)162 void unlock(apr_thread_mutex_t *mutex) {
163   apr_status_t status;
164   if ((status = apr_thread_mutex_unlock(mutex)) != APR_SUCCESS) {
165     apr_pool_t *ptmp;
166     HT_POOL_CREATE(&ptmp);
167     success = 0;
168     fprintf(stderr, "could not unlock: %s(%d)\n",
169 	    my_status_str(ptmp, status), status);
170     exit(1);
171   }
172 }
173 
174 /**
175  * Get recorder struct from worker config
176  * @param worker IN thread object
177  * @return recoder
178  */
worker_get_recorder(worker_t * worker)179 static recorder_t *worker_get_recorder(worker_t *worker) {
180   recorder_t *recorder = module_get_config(worker->config, RECORDER_CONFIG);
181   if (!recorder) {
182     recorder = apr_pcalloc(worker->pbody, sizeof(recorder_t));
183     module_set_config(worker->config, RECORDER_CONFIG, recorder);
184   }
185   return recorder;
186 }
187 
188 /**
189  * Set a variable either as local or global,
190  * make a copy of passed value and replace existing value.
191  * Always adds the terminating zero to the stored value.
192  * So if you want to store an already zero terminated string "myStr", then pass "strlen(myStr)" in len.
193  *
194  * @param worker IN thread object
195  * @param var IN variable name
196  * @param val IN zero terminated or not terminated string to store
197  * @param len IN length of val string (without terminating zero)
198  */
worker_var_set_and_zero_terminate(worker_t * worker,const char * var,const char * val,apr_size_t len)199 void worker_var_set_and_zero_terminate(worker_t * worker, const char *var, const char *val, apr_size_t len) {
200   const char *ret;
201 
202   /* do mapping from ret var to var */
203   if ((ret = store_get(worker->retvars, var))) {
204 	  store_set_and_zero_terminate(worker->vars, ret, val, len);
205     return;
206   }
207 
208   /* if not test if local */
209   if (store_get(worker->locals, var)) {
210 	  store_set_and_zero_terminate(worker->locals, var, val, len);
211     return;
212   }
213 
214   /* params can be shadowed by locals so this after locals */
215   if (store_get(worker->params, var)) {
216 	  store_set_and_zero_terminate(worker->params, var, val, len);
217     return;
218   }
219 
220   /* test if there are globals at all to avoid locking  */
221   if (worker->global->shared) {
222     /* test if this variable is a global one */
223     apr_thread_mutex_lock(worker->mutex);
224     if (store_get(worker->global->shared, var)) {
225       store_set_and_zero_terminate(worker->global->shared, var, val, len);
226       apr_thread_mutex_unlock(worker->mutex);
227       return;
228     }
229     apr_thread_mutex_unlock(worker->mutex);
230   }
231 
232   /* if there is no var at all stored it in thread global vars */
233   store_set_and_zero_terminate(worker->vars, var, val, len);
234 }
235 
236 
237 /**
238  * set a variable either as local or global
239  * make a copy and replace existing
240  *
241  * @param worker IN thread object
242  * @param var IN variable name
243  * @param val IN zero terminated string value to set
244  */
worker_var_set(worker_t * worker,const char * var,const char * val)245 void worker_var_set(worker_t * worker, const char *var, const char *val) {
246   apr_size_t len = (val == NULL) ? 0 : strlen(val);
247   worker_var_set_and_zero_terminate(worker, var, val, len);
248 }
249 
250 /**
251  * get a variable either as local or global
252  *
253  * @param worker IN thread object
254  * @param var IN variable name
255  *
256  * @return value
257  */
worker_var_get(worker_t * worker,const char * var)258 const char *worker_var_get(worker_t* worker, const char *var) {
259   const char *val = NULL;
260 
261   /* first test locals */
262   if ((val = store_get(worker->locals, var))) {
263     return val;
264   }
265 
266   /* next are params */
267   if ((val = store_get(worker->params, var))) {
268     return val;
269   }
270 
271   /* next are thread globals */
272   if ((val = store_get(worker->vars, var))) {
273     return val;
274   }
275 
276   /* last test globals */
277   if (worker->global->shared) {
278     apr_thread_mutex_lock(worker->mutex);
279     val = store_get(worker->global->shared, var);
280     apr_thread_mutex_unlock(worker->mutex);
281   }
282   return val;
283 }
284 
285 /**
286  * resolve vars
287  * @param worker IN callee
288  * @param name IN name to lookup
289  * @param ptmp IN temp pool
290  * @return value
291  */
worker_resolve_var(worker_t * worker,const char * name,apr_pool_t * ptmp)292 const char * worker_resolve_var(worker_t *worker, const char *name, apr_pool_t *ptmp) {
293   const char *val = NULL;
294 
295   if (strchr(name, '(')) {
296     int log_mode;
297     char *command = apr_pstrdup(ptmp, name);
298     int i = 0;
299     while (command[i] != 0) {
300       if (command[i] == '(' || command[i] == ')') {
301 	command[i] = ' ';
302       }
303       ++i;
304     }
305     command = apr_pstrcat(ptmp, command, " __INLINE_RET", NULL);
306     /** call it */
307     log_mode = logger_get_mode(worker->logger);
308     logger_set_mode(worker->logger, 0);
309     if (command_CALL(NULL, worker, command, ptmp) == APR_SUCCESS) {
310       val = worker_var_get(worker, "__INLINE_RET");
311     }
312     logger_set_mode(worker->logger, log_mode);
313   }
314 
315   if (!val) {
316     val = worker_var_get(worker, name);
317   }
318 
319   return val;
320 }
321 
322 /**
323  * replace vars upcall function
324  * @param udata IN void pointer to replacer_t object
325  * @param name IN name to lookup
326  * @return value
327  */
replacer_upcall(void * udata,const char * name)328 static const char * replacer_upcall(void *udata, const char *name) {
329   const char *val = NULL;
330   replacer_t *hook = udata;
331 
332   val = worker_resolve_var(hook->worker, name, hook->ptmp);
333   if (!val) {
334     hook->unresolved = 1;
335   }
336   return val;
337 }
338 
339 /**
340  * Replace vars with store, inline call and env vars
341  * @param udata IN void pointer to replacer_t object
342  * @param name IN variable name
343  * @return value
344  */
replacer_env_upcall(void * udata,const char * name)345 static const char * replacer_env_upcall(void *udata, const char *name) {
346   const char *val = NULL;
347   replacer_t *hook = udata;
348   int unresolved = hook->unresolved;
349 
350   val = replacer_upcall(udata, name);
351   if (!val) {
352     char *env;
353     hook->unresolved = unresolved;
354     if (apr_env_get(&env, name, hook->ptmp) == APR_SUCCESS) {
355       val = env;
356     }
357     if (!val) {
358       hook->unresolved = 1;
359     }
360   }
361   return val;
362 }
363 
364 /**
365  * replace variables in a line
366  *
367  * @param worker IN thread data object
368  * @param line IN line to replace in
369  *
370  * @return new line
371  */
worker_replace_vars(worker_t * worker,char * line,int * unresolved,apr_pool_t * ptmp)372 char * worker_replace_vars(worker_t * worker, char *line, int *unresolved,
373                            apr_pool_t *ptmp) {
374   char *new_line;
375   replacer_t *upcall_hook = apr_pcalloc(ptmp, sizeof(*upcall_hook));
376 
377   upcall_hook->worker = worker;
378   upcall_hook->ptmp = ptmp;
379   new_line = replacer(ptmp, line, upcall_hook, replacer_env_upcall);
380 
381   if (unresolved) {
382     *unresolved = upcall_hook->unresolved;
383   }
384   return new_line;
385 }
386 
387 /**
388  * client thread
389  *
390  * @param thread IN thread object
391  * @param selfv IN void pointer to tunnel object
392  *
393  * @return NULL
394  */
streamer(apr_thread_t * thread,void * selfv)395 static void * APR_THREAD_FUNC streamer(apr_thread_t * thread, void *selfv) {
396   apr_status_t status;
397   char buf[BLOCK_MAX];
398   apr_size_t len;
399 
400   tunnel_t *tunnel = selfv;
401 
402   do {
403     /* read polled from and send to */
404     len = BLOCK_MAX - 1;
405     status = sockreader_read_block(tunnel->sockreader, buf, &len);
406     if (APR_STATUS_IS_EOF(status) && len > 0) {
407       status = APR_SUCCESS;
408     }
409     else if (APR_STATUS_IS_TIMEUP(status)) {
410       status = APR_SUCCESS;
411     }
412     if (status == APR_SUCCESS) {
413       status = transport_write(tunnel->sendto->transport, buf, len);
414     }
415   } while (status == APR_SUCCESS);
416 
417   if (APR_STATUS_IS_EOF(status)) {
418     status = APR_SUCCESS;
419   }
420   apr_thread_exit(thread, APR_SUCCESS);
421   return NULL;
422 }
423 
424 /**
425  * local file write
426  *
427  * @param sockett IN socket
428  * @param buf IN buffer to send
429  * @param len IN no bytes of buffer to send
430  *
431  * @return apr status
432  */
file_write(apr_file_t * file,char * buf,apr_size_t len)433 static apr_status_t file_write(apr_file_t *file, char *buf,
434                                apr_size_t len) {
435   apr_status_t status = APR_SUCCESS;
436   apr_size_t total = len;
437   apr_size_t count = 0;
438 
439   while (total != count) {
440     len = total - count;
441     if ((status = apr_file_write(file, &buf[count], &len))
442 	!= APR_SUCCESS) {
443       goto error;
444     }
445     count += len;
446   }
447 error:
448   return status;
449 }
450 
451 /**
452  * Buffer converter depends on the worker->flags
453  *
454  * @param worker IN thread data object
455  * @param buf INOUT buffer to rewrite
456  * @param len INOUT buffer len
457  */
worker_buf_convert(worker_t * self,char ** buf,apr_size_t * len)458 static void worker_buf_convert(worker_t *self, char **buf, apr_size_t *len) {
459   int j;
460   char *hexbuf;
461   apr_pool_t *pool;
462 
463   if (!(*buf)) {
464     return;
465   }
466 
467   if (self->flags & FLAGS_ONLY_PRINTABLE) {
468     for (j = 0; j < *len; j++) {
469       if ((*buf)[j] < 32) {
470 	(*buf)[j] = ' ';
471       }
472     }
473   }
474 
475   if (self->flags & FLAGS_PRINT_HEX) {
476     HT_POOL_CREATE(&pool);
477     hexbuf = NULL;
478     for (j = 0; j < *len; j++) {
479       if (hexbuf == NULL) {
480 	 hexbuf = apr_psprintf(pool, "%02X", (*buf)[j]);
481       }
482       else {
483 	 hexbuf = apr_psprintf(pool, "%s %02X", hexbuf, (*buf)[j]);
484       }
485     }
486     *buf = apr_pstrdup(self->pbody, hexbuf);
487     *len = strlen(*buf);
488     apr_pool_destroy(pool);
489   }
490 }
491 
492 /**
493  * pipe buf to workers running process
494  *
495  * @param worker IN thread data object
496  * @param buf IN buffer to rewrite
497  * @param len IN buffer len
498  */
worker_buf_pipe_exec(worker_t * worker,char * buf,apr_size_t len)499 static apr_status_t worker_buf_pipe_exec(worker_t *worker, char *buf,
500                                          apr_size_t len) {
501   apr_status_t status = APR_SUCCESS;
502   apr_exit_why_e exitwhy;
503   int exitcode;
504   exec_t *exec = module_get_config(worker->config, EXEC_CONFIG);
505 
506   if ((status = file_write(exec->proc->in, buf, len))
507       != APR_SUCCESS) {
508     return status;
509   }
510   apr_file_close(exec->proc->in);
511   apr_proc_wait(exec->proc, &exitcode, &exitwhy, APR_WAIT);
512   if (exitcode != 0) {
513     status = APR_EGENERAL;
514   }
515   module_set_config(worker->config, EXEC_CONFIG, NULL);
516   apr_pool_destroy(exec->pool);
517   return status;
518 }
519 
520 /**
521  * write buf to file pointer
522  *
523  * @param thread IN thread pointer
524  * @param selfv IN void pointer of type write_buf_to_file_t
525  *
526  * @return NULL
527  */
worker_write_buf_to_file(apr_thread_t * thread,void * selfv)528 static void * APR_THREAD_FUNC worker_write_buf_to_file(apr_thread_t * thread, void *selfv) {
529   write_buf_to_file_t *wbtf = selfv;
530   apr_size_t len;
531 
532   len = wbtf->len;
533   file_write(wbtf->fp, wbtf->buf, len);
534   apr_file_close(wbtf->fp);
535 
536   apr_thread_exit(thread, APR_SUCCESS);
537   return NULL;
538 }
539 
540 /**
541  * do filter buf with workers process in/out
542  *
543  * @param worker IN thread data object
544  * @param ptmp IN temporary pool to alloc thread
545  * @param buf INOUT buffer to rewrite
546  * @param len INOUT buffer len
547  */
worker_buf_filter_exec(worker_t * worker,apr_pool_t * ptmp,char ** buf,apr_size_t * len)548 static apr_status_t worker_buf_filter_exec(worker_t *worker, apr_pool_t *ptmp,
549                                            char **buf, apr_size_t *len) {
550   apr_status_t status;
551   apr_status_t tmp_status;
552   write_buf_to_file_t write_buf_to_file;
553   apr_threadattr_t *tattr;
554   apr_thread_t *thread;
555   bufreader_t *br;
556   apr_exit_why_e exitwhy;
557   int exitcode;
558   exec_t *exec = module_get_config(worker->config, EXEC_CONFIG);
559 
560   worker_log(worker, LOG_DEBUG, "write to stdin, read from stdout");
561   /* start write thread */
562   write_buf_to_file.buf = *buf;
563   write_buf_to_file.len = *len;
564   write_buf_to_file.fp = exec->proc->in;
565   if ((status = apr_threadattr_create(&tattr, ptmp)) != APR_SUCCESS) {
566     goto out_err;
567   }
568   if ((status = apr_threadattr_stacksize_set(tattr, DEFAULT_THREAD_STACKSIZE))
569       != APR_SUCCESS) {
570     goto out_err;
571   }
572   if ((status = apr_threadattr_detach_set(tattr, 1)) != APR_SUCCESS) {
573     goto out_err;
574   }
575   if ((status =
576        apr_thread_create(&thread, tattr, worker_write_buf_to_file,
577 			 &write_buf_to_file, worker->pbody)) != APR_SUCCESS) {
578     goto out_err;
579   }
580   /* read from worker->proc.out to buf */
581   if ((status = bufreader_new(&br, exec->proc->out, worker->pbody)) == APR_SUCCESS) {
582     bufreader_read_eof(br, buf, len);
583   }
584   if (status == APR_EOF) {
585     status = APR_SUCCESS;
586   }
587   apr_thread_join(&tmp_status, thread);
588   apr_proc_wait(exec->proc, &exitcode, &exitwhy, APR_WAIT);
589   if (exitcode != 0) {
590     status = APR_EGENERAL;
591     goto out_err;
592   }
593 out_err:
594   module_set_config(worker->config, EXEC_CONFIG, NULL);
595   apr_pool_destroy(exec->pool);
596   return status;
597 }
598 
599 
600 /**
601  * Test socket state
602  *
603  * @param worker IN thread data object
604  *
605  * @return APR_SUCCESS or APR_ECONNABORTED
606  */
worker_sockstate(worker_t * worker)607 apr_status_t worker_sockstate(worker_t * worker) {
608   apr_status_t status = APR_SUCCESS;
609   apr_size_t len = 1;
610 
611   if (!worker->socket) {
612     return APR_ENOSOCKET;
613   }
614 
615   if ((status = transport_set_timeout(worker->socket->transport, 1000))
616       != APR_SUCCESS) {
617     return status;
618   }
619 
620   status = transport_read(worker->socket->transport,
621 			  &worker->socket->peek[worker->socket->peeklen], &len);
622   if (APR_STATUS_IS_TIMEUP(status)) {
623     status = APR_SUCCESS;
624   }
625 
626   if (APR_STATUS_IS_EOF(status)) {
627     status = APR_ECONNABORTED;
628     goto go_out;
629   }
630   else if (status != APR_SUCCESS) {
631     status = APR_ECONNABORTED;
632     goto go_out;
633   }
634   else {
635     worker->socket->peeklen += len;
636     status = APR_SUCCESS;
637     goto go_out;
638   }
639 
640 go_out:
641   transport_set_timeout(worker->socket->transport, worker->socktmo);
642 
643   return status;
644 }
645 
646 /**
647  * gets values from data and store it in the variable table
648  *
649  * @param worker IN thread data object
650  * @param htt_regexs IN table of regular expressions to get the values from data
651  * @param data IN data to match
652  *
653  * @return APR_SUCCESS
654  */
worker_match(worker_t * worker,apr_table_t * htt_regexs,const char * data,apr_size_t len)655 apr_status_t worker_match(worker_t * worker, apr_table_t * htt_regexs,
656                           const char *data, apr_size_t len) {
657   apr_table_entry_t *e;
658   apr_table_entry_t *v;
659   regmatch_t regmatch[11];
660   int i;
661   int j;
662   char *val;
663   char *last;
664   char *var;
665   char *tmp;
666   apr_table_t *vtbl;
667   int n;
668   apr_pool_t *pool;
669   apr_status_t status = APR_SUCCESS;
670 
671   if (!data) {
672     return APR_SUCCESS;
673   }
674 
675   HT_POOL_CREATE(&pool);
676   vtbl = apr_table_make(pool, 2);
677 
678   e = (apr_table_entry_t *) apr_table_elts(htt_regexs)->elts;
679   for (i = 0; i < apr_table_elts(htt_regexs)->nelts; ++i) {
680     /* prepare vars if multiple */
681     apr_table_clear(vtbl);
682     tmp = apr_pstrdup(pool, e[i].key);
683     var = apr_strtok(tmp, " ", &last);
684     while (var) {
685       apr_table_set(vtbl, var, var);
686       var = apr_strtok(NULL, " ", &last);
687     }
688 
689     n = apr_table_elts(vtbl)->nelts;
690     if (n > 10) {
691       worker_log(worker, LOG_ERR, "Too many vars defined for _MATCH statement, max 10 vars allowed");
692       status = APR_EINVAL;
693       goto error;
694     }
695 
696     if (e[i].val
697         && htt_regexec((htt_regex_t *) e[i].val, data, len, n + 1, regmatch,
698                    PCRE_MULTILINE) == 0) {
699       v = (apr_table_entry_t *) apr_table_elts(vtbl)->elts;
700       for (j = 0; j < n; j++) {
701 	val =
702 	  apr_pstrndup(pool, &data[regmatch[j + 1].rm_so],
703 		       regmatch[j + 1].rm_eo - regmatch[j + 1].rm_so);
704 	worker_var_set(worker, v[j].key, val);
705 	if (worker->match_seq) {
706 	  /* if there is a defined match sequence do more checks */
707 	  if (strstr(worker->match_seq, v[j].key)) {
708 	   if (strncmp(v[j].key, worker->match_seq, strlen(v[j].key)) == 0) {
709 	     char *last;
710 	     /* remove the first var in the var sequence */
711              apr_strtok(worker->match_seq, " ", &last);
712 	     worker->match_seq = last;
713 	   }
714 	  }
715 	}
716       }
717     }
718   }
719 
720 error:
721   apr_pool_destroy(pool);
722   return status;
723 }
724 
725 /**
726  * checks if data contains a given pattern
727  *
728  * @param self IN thread data object
729  * @param htt_regexs IN table of regular expressions
730  * @param data IN data to check
731  *
732  * @return APR_SUCCESS
733  */
worker_expect(worker_t * self,apr_table_t * htt_regexs,const char * data,apr_size_t len)734 apr_status_t worker_expect(worker_t * self, apr_table_t * htt_regexs,
735                            const char *data, apr_size_t len) {
736   apr_table_entry_t *e;
737   int i;
738 
739   if (!data) {
740     return APR_SUCCESS;
741   }
742 
743   e = (apr_table_entry_t *) apr_table_elts(htt_regexs)->elts;
744   for (i = 0; i < apr_table_elts(htt_regexs)->nelts; ++i) {
745     if (e[i].val
746         && htt_regexec((htt_regex_t *) e[i].val, data, len, 0, NULL,
747                    PCRE_MULTILINE) == 0) {
748     }
749   }
750 
751   return APR_SUCCESS;
752 }
753 
754 /**
755  * Throws assertions if specified match did have noch hit.
756  * @param worker IN
757  * @param match IN table of all specified matchs
758  * @param namespace IN the namespace of this matchs
759  * @param status IN current status of earlier calls
760  * @return new status
761  */
worker_assert_match(worker_t * worker,apr_table_t * match,char * namespace,apr_status_t status)762 apr_status_t worker_assert_match(worker_t *worker, apr_table_t *match,
763                                  char *namespace, apr_status_t status) {
764   apr_table_entry_t *e;
765   int i;
766   apr_pool_t *pool;
767 
768   e = (apr_table_entry_t *) apr_table_elts(match)->elts;
769   for (i = 0; i < apr_table_elts(match)->nelts; ++i) {
770     htt_regex_t *htt_regex = (htt_regex_t *) e[i].val;
771     if (!htt_regexhits(htt_regex)) {
772       worker_log(worker, LOG_ERR, "%s: Did expect %s", namespace, htt_regexpattern(htt_regex));
773       if (status == APR_SUCCESS) {
774         status = APR_EINVAL;
775       }
776     }
777   }
778   apr_table_clear(match);
779   pool = module_get_config(worker->config, namespace);
780   module_set_config(worker->config, namespace, NULL);
781   if (pool) {
782     apr_pool_destroy(pool);
783   }
784   return status;
785 }
786 
787 /**
788  * Throws assertions if specified expect did have noch hit.
789  * @param worker IN
790  * @param expect IN table of all specified expects
791  * @param namespace IN the namespace of this expects
792  * @param status IN current status of earlier calls
793  * @return new status
794  */
worker_assert_expect(worker_t * worker,apr_table_t * expect,char * namespace,apr_status_t status)795 apr_status_t worker_assert_expect(worker_t *worker, apr_table_t *expect,
796                                   char *namespace, apr_status_t status) {
797   apr_table_entry_t *e;
798   int i;
799   apr_pool_t *pool;
800 
801   e = (apr_table_entry_t *) apr_table_elts(expect)->elts;
802   for (i = 0; i < apr_table_elts(expect)->nelts; ++i) {
803     htt_regex_t *htt_regex = (htt_regex_t *) e[i].val;
804     if (e[i].key[0] != '!' && !htt_regexhits(htt_regex)) {
805       worker_log(worker, LOG_ERR, "%s: Did expect \"%s\"", namespace,
806 	         htt_regexpattern(htt_regex));
807       if (status == APR_SUCCESS) {
808         status = APR_EINVAL;
809       }
810     }
811     if (e[i].key[0] == '!' && htt_regexhits((htt_regex_t *) e[i].val)) {
812       worker_log(worker, LOG_ERR, "%s: Did not expect \"%s\"", namespace,
813 	         &e[i].key[1]);
814       if (status == APR_SUCCESS) {
815         status = APR_EINVAL;
816       }
817     }
818   }
819   apr_table_clear(expect);
820   pool = module_get_config(worker->config, namespace);
821   module_set_config(worker->config, namespace, NULL);
822   if (pool) {
823     apr_pool_destroy(pool);
824   }
825   return status;
826 }
827 
828 
829 /**
830  * Grep do not have an assertion at all, actually.
831  * @param worker IN
832  * @param expect IN table of all specified expects
833  * @param namespace IN the namespace of this expects
834  * @param status IN current status of earlier calls
835  * @return new status
836  */
worker_assert_grep(worker_t * worker,apr_table_t * grep,char * namespace,apr_status_t status)837 static apr_status_t worker_assert_grep(worker_t * worker, apr_table_t *grep,
838                                        char *namespace, apr_status_t status) {
839   apr_pool_t *pool;
840 
841   apr_table_clear(grep);
842   pool = module_get_config(worker->config, namespace);
843   module_set_config(worker->config, namespace, NULL);
844   if (pool) {
845     apr_pool_destroy(pool);
846   }
847   return status;
848 }
849 
850 /**
851  * Do check for if all defined expects are handled
852  *
853  * @param worker IN worker thread object
854  * @param status IN current status
855  *
856  * @return current status or APR_EINVAL if there are unhandled expects
857  */
worker_assert(worker_t * worker,apr_status_t status)858 apr_status_t worker_assert(worker_t * worker, apr_status_t status) {
859   status = worker_assert_match(worker, worker->match.dot, "MATCH .",
860                                status);
861   status = worker_assert_match(worker, worker->match.headers, "MATCH headers",
862                                status);
863   status = worker_assert_match(worker, worker->match.body, "MATCH body",
864                                status);
865   status = worker_assert_expect(worker, worker->expect.dot, "EXPECT .",
866                                status);
867   status = worker_assert_expect(worker, worker->expect.headers, "EXPECT headers",
868                                status);
869   status = worker_assert_expect(worker, worker->expect.body, "EXPECT body",
870                                 status);
871   status = worker_assert_grep(worker, worker->grep.dot, "GREP .",
872                               status);
873   status = worker_assert_grep(worker, worker->grep.headers, "GREP headers",
874                               status);
875   status = worker_assert_grep(worker, worker->grep.body, "GREP body",
876                               status);
877   /* check if match sequence is empty */
878 
879   if (worker->match_seq && worker->match_seq[0] != 0) {
880     worker_log(worker, LOG_ERR, "The following match sequence \"%s\" was not in correct order", worker->match_seq);
881     status = APR_EINVAL;
882     goto exit;
883   }
884 exit:
885   {
886     apr_pool_t *pool;
887     pool = module_get_config(worker->config, "MATCH_SEQ");
888     if (pool) {
889       module_set_config(worker->config, apr_pstrdup(pool, "MATCH_SEQ"), NULL);
890       apr_pool_destroy(pool);
891     }
892   }
893   return status;
894 }
895 
896 /**
897  * Check for error expects handling
898  *
899  * @param worker IN worker thread object
900  * @param status IN current status
901  *
902  * @return current status or APR_INVAL
903  */
worker_check_error(worker_t * worker,apr_status_t status)904 apr_status_t worker_check_error(worker_t *worker, apr_status_t status) {
905   char *error;
906   apr_table_entry_t *e;
907   int i;
908 
909   /* nothing to do in this case */
910   if (status == APR_SUCCESS) {
911     return status;
912   }
913 
914   /* handle special case (break loop) */
915   if (status == -1) {
916     return status;
917   }
918 
919   error = apr_psprintf(worker->pbody, "%s(%d)",
920 		     my_status_str(worker->pbody, status), status);
921 
922   worker_match(worker, worker->match.error, error, strlen(error));
923   worker_match(worker, worker->grep.error, error, strlen(error));
924   worker_expect(worker, worker->expect.error, error, strlen(error));
925 
926   if (apr_table_elts(worker->expect.error)->nelts) {
927     status = APR_SUCCESS;
928     e = (apr_table_entry_t *) apr_table_elts(worker->expect.error)->elts;
929     for (i = 0; i < apr_table_elts(worker->expect.error)->nelts; ++i) {
930       if (e[i].key[0] != '!' && !htt_regexhits((htt_regex_t *) e[i].val)) {
931 	worker_log(worker, LOG_ERR, "EXPECT: Did expect error \"%s\"", e[i].key);
932 	status = APR_EINVAL;
933 	goto error;
934       }
935       if (e[i].key[0] == '!' && htt_regexhits((htt_regex_t *) e[i].val)) {
936 	worker_log(worker, LOG_ERR, "EXPECT: Did not expect error \"%s\"", &e[i].key[1]);
937 	status = APR_EINVAL;
938 	goto error;
939       }
940     }
941     apr_table_clear(worker->expect.error);
942   }
943 
944   if (apr_table_elts(worker->match.error)->nelts) {
945     status = APR_SUCCESS;
946     e = (apr_table_entry_t *) apr_table_elts(worker->match.error)->elts;
947     for (i = 0; i < apr_table_elts(worker->match.error)->nelts; ++i) {
948       if (!htt_regexhits((htt_regex_t *) e[i].val)) {
949 	worker_log(worker, LOG_ERR, "MATCH error: Did expect %s", e[i].key);
950 	status = APR_EINVAL;
951       }
952     }
953     apr_table_clear(worker->match.error);
954   }
955 
956 error:
957   if (status == APR_SUCCESS) {
958     worker_log(worker, LOG_INFO, "%s %s", worker->name, error);
959   }
960   else {
961     worker_log(worker, LOG_ERR, "%s %s", worker->name, error);
962   }
963   return status;
964 }
965 
966 /**
967  * Test for unused expects and matchs
968  * @param worker IN thread data object
969  * @return APR_SUCCESS or APR_EGENERAL
970  */
worker_test_reset(worker_t * worker)971 void worker_test_reset(worker_t * worker) {
972   apr_table_clear(worker->match.dot);
973   apr_table_clear(worker->match.headers);
974   apr_table_clear(worker->match.body);
975   apr_table_clear(worker->match.error);
976   apr_table_clear(worker->expect.dot);
977   apr_table_clear(worker->expect.headers);
978   apr_table_clear(worker->expect.body);
979   apr_table_clear(worker->expect.error);
980 }
981 
982 /**
983  * Test for unused expects and matchs
984  * @param worker IN thread data object
985  * @return APR_SUCCESS or APR_EGENERAL
986  */
worker_test_unused(worker_t * worker)987 apr_status_t worker_test_unused(worker_t * worker) {
988   if (apr_table_elts(worker->match.dot)->nelts) {
989     worker_log(worker, LOG_ERR, "There are unused MATCH .");
990     return APR_EGENERAL;
991   }
992   if (apr_table_elts(worker->match.headers)->nelts) {
993     worker_log(worker, LOG_ERR, "There are unused MATCH headers");
994     return APR_EGENERAL;
995   }
996   if (apr_table_elts(worker->match.body)->nelts) {
997     worker_log(worker, LOG_ERR, "There are unused MATCH body");
998     return APR_EGENERAL;
999   }
1000   if (apr_table_elts(worker->match.exec)->nelts) {
1001     worker_log(worker, LOG_ERR, "There are unused MATCH exec");
1002     return APR_EGENERAL;
1003   }
1004   if (apr_table_elts(worker->expect.dot)->nelts) {
1005     worker_log(worker, LOG_ERR, "There are unused EXPECT .");
1006     return APR_EGENERAL;
1007   }
1008   if (apr_table_elts(worker->expect.headers)->nelts) {
1009     worker_log(worker, LOG_ERR, "There are unused EXPECT headers");
1010     return APR_EGENERAL;
1011   }
1012   if (apr_table_elts(worker->expect.body)->nelts) {
1013     worker_log(worker, LOG_ERR, "There are unused EXPECT body");
1014     return APR_EGENERAL;
1015   }
1016 
1017   return APR_SUCCESS;
1018 }
1019 
1020 /**
1021  * Test for unused expects errors and matchs
1022  *
1023  * @param worker IN thread data object
1024  *
1025  * @return APR_SUCCESS or APR_EGENERAL
1026  */
worker_test_unused_errors(worker_t * worker)1027 apr_status_t worker_test_unused_errors(worker_t * worker) {
1028   if (apr_table_elts(worker->expect.error)->nelts) {
1029     worker_log(worker, LOG_ERR, "There are unused EXPECT ERROR");
1030     return APR_EGENERAL;
1031   }
1032 
1033   if (apr_table_elts(worker->match.error)->nelts) {
1034     worker_log(worker, LOG_ERR, "There are unused MATCH ERROR");
1035     return APR_EGENERAL;
1036   }
1037 
1038   return APR_SUCCESS;
1039 }
1040 
1041 /**
1042  * Close current socket
1043  *
1044  * @param self IN thread data object
1045  *
1046  * @return apr status
1047  */
worker_conn_close(worker_t * self,char * info)1048 apr_status_t worker_conn_close(worker_t * self, char *info) {
1049   apr_status_t status;
1050 
1051   if (!self->socket) {
1052     return APR_ENOSOCKET;
1053   }
1054 
1055   if (self->socket->socket_state == SOCKET_CLOSED) {
1056     return APR_SUCCESS;
1057   }
1058 
1059   if ((status = htt_run_pre_close(self)) != APR_SUCCESS) {
1060     return status;
1061   }
1062 
1063   if ((status = htt_run_close(self, info, &info)) != APR_SUCCESS) {
1064     if (APR_STATUS_IS_EINTR(status)) {
1065       return APR_SUCCESS;
1066     }
1067     return status;
1068   }
1069 
1070   if (!info || !info[0] || strcmp(info, "TCP") == 0) {
1071     tcp_close(self);
1072     self->socket->socket_state = SOCKET_CLOSED;
1073   }
1074 
1075   sockreader_destroy(&self->socket->sockreader);
1076 
1077   return APR_SUCCESS;
1078 }
1079 
1080 /**
1081  * Close all sockets for this worker
1082  *
1083  * @param self IN thread data object
1084  *
1085  * @return apr status
1086  */
worker_conn_close_all(worker_t * self)1087 void worker_conn_close_all(worker_t *self) {
1088   apr_hash_index_t *hi;
1089   void *s;
1090 
1091   socket_t *cur = self->socket;
1092 
1093   for (hi = apr_hash_first(self->pbody, self->sockets); hi; hi = apr_hash_next(hi)) {
1094     apr_hash_this(hi, NULL, NULL, &s);
1095     self->socket = s;
1096     worker_conn_close(self, NULL);
1097   }
1098   self->socket = cur;
1099   if (self->listener) {
1100     apr_socket_close(self->listener);
1101   }
1102 }
1103 
1104 /**
1105  * Convertion and/or pipe to executable and/or read from executable and check
1106  * _EXPECT and MATCH.
1107  *
1108  * @param worker IN worker object
1109  * @param buf IN buffer to handle
1110  * @param len IN length of buffer
1111  *
1112  * @return apr status
1113  */
worker_handle_buf(worker_t * worker,apr_pool_t * pool,char * buf,apr_size_t size)1114 apr_status_t worker_handle_buf(worker_t *worker, apr_pool_t *pool, char *buf,
1115                                apr_size_t size) {
1116   apr_status_t status = APR_SUCCESS;
1117   char *tmpbuf = buf;
1118   apr_size_t len = size;
1119 
1120   if (tmpbuf) {
1121     worker_buf_convert(worker, &tmpbuf, &len);
1122     if (worker->flags & FLAGS_PIPE_IN) {
1123       worker->flags &= ~FLAGS_PIPE_IN;
1124       if ((status = worker_buf_pipe_exec(worker, tmpbuf, len)) != APR_SUCCESS) {
1125 	return status;
1126       }
1127     }
1128     else if (worker->flags & FLAGS_FILTER) {
1129       worker->flags &= ~FLAGS_FILTER;
1130       if ((status =  worker_buf_filter_exec(worker, pool, &tmpbuf, &len)) != APR_SUCCESS) {
1131         return status;
1132       }
1133     }
1134     if (tmpbuf) {
1135         worker_log_buf(worker, LOG_INFO, '<', tmpbuf, len);
1136         worker_match(worker, worker->match.dot, tmpbuf, len);
1137         worker_match(worker, worker->match.body, tmpbuf, len);
1138         worker_match(worker, worker->grep.dot, tmpbuf, len);
1139         worker_match(worker, worker->grep.body, tmpbuf, len);
1140         worker_expect(worker, worker->expect.dot, tmpbuf, len);
1141         worker_expect(worker, worker->expect.body, tmpbuf, len);
1142     }
1143   }
1144   return status;
1145 }
1146 
1147 /**
1148  * Store all cookies in the header table of worker in a cookie line
1149  *
1150  * @param worker IN thread data object
1151  */
worker_set_cookie(worker_t * worker)1152 static void worker_set_cookie(worker_t *worker) {
1153   int i;
1154   apr_table_entry_t *e;
1155 
1156   if (!worker->socket) {
1157     return;
1158   }
1159 
1160   if (!worker->socket->cookies) {
1161     worker->socket->cookies = apr_table_make(worker->pbody, 5);
1162   }
1163 
1164   e = (apr_table_entry_t *) apr_table_elts(worker->headers)->elts;
1165   for (i = 0; i < apr_table_elts(worker->headers)->nelts; ++i) {
1166     if (strcmp(e[i].key, "Set-Cookie") == 0) {
1167       char *last;
1168       char *key;
1169       char *value;
1170       char *cookie = apr_pstrdup(worker->pbody, e[i].val);
1171       key = apr_strtok(cookie, "=", &last);
1172       value = apr_strtok(NULL, ";", &last);
1173       apr_table_set(worker->socket->cookies, key, value);
1174     }
1175   }
1176 
1177   worker->socket->cookie = NULL;
1178   e = (apr_table_entry_t *) apr_table_elts(worker->socket->cookies)->elts;
1179   for (i = 0; i < apr_table_elts(worker->socket->cookies)->nelts; ++i) {
1180     if (worker->socket->cookie) {
1181       worker->socket->cookie = apr_pstrcat(worker->pbody,
1182 					   worker->socket->cookie, "; ",
1183 					   e[i].key, "=", e[i].val, NULL);
1184     }
1185     else {
1186       worker->socket->cookie = apr_pstrcat(worker->pbody, "Cookie: ", e[i].key, "=",
1187 					   e[i].val, NULL);
1188     }
1189   }
1190 }
1191 
1192 /**
1193  * Get value from a given param
1194  * @param worker IN thread data object
1195  * @param param IN resolved param or VAR param
1196  * @return final resolved value
1197  */
worker_get_value_from_param(worker_t * worker,const char * param,apr_pool_t * ptmp)1198 const char *worker_get_value_from_param(worker_t *worker, const char *param, apr_pool_t *ptmp) {
1199   const char *val = NULL;
1200 
1201   if (strncmp(param, "VAR(", 4) == 0) {
1202     char *var = apr_pstrdup(ptmp, param + 4);
1203     apr_size_t len = strlen(var);
1204     if (len > 0) {
1205       var[len-1] = 0;
1206     }
1207     val = store_get(worker->vars, var);
1208     if (!val) {
1209       val = store_get(worker->locals, var);
1210     }
1211     if (!val) {
1212       val = param;
1213     }
1214   }
1215   else {
1216     val = param;
1217   }
1218   return val;
1219 }
1220 
1221 /**
1222  * CALL command calls a defined block
1223  *
1224  * @param self IN command
1225  * @param worker IN thread data object
1226  * @param data IN name of calling block
1227  *
1228  * @return block status or APR_EINVAL
1229  */
command_CALL(command_t * self,worker_t * worker,char * data,apr_pool_t * ptmp)1230 apr_status_t command_CALL(command_t *self, worker_t *worker, char *data,
1231                           apr_pool_t *ptmp) {
1232   apr_status_t status;
1233   char *copy;
1234   const char *block_name;
1235   char *last;
1236   worker_t *block, *call;
1237   apr_table_t *lines = NULL;
1238   int cmd;
1239   apr_pool_t *call_pool;
1240   char *module;
1241   apr_hash_t *blocks;
1242   store_t *params;
1243   store_t *retvars;
1244   store_t *locals;
1245 
1246   /** a pool for this call */
1247   HT_POOL_CREATE(&call_pool);
1248 
1249   /** temporary tables for param, local vars and return vars */
1250   params = store_make(call_pool);
1251   retvars = store_make(call_pool);
1252   locals = store_make(call_pool);
1253 
1254   while (*data == ' ') ++data;
1255   copy = apr_pstrdup(call_pool, data);
1256   copy = worker_replace_vars(worker, copy, NULL, call_pool);
1257   worker_log(worker, LOG_CMD, "%s", copy);
1258 
1259   /** get args from copy */
1260   my_get_args(copy, params, call_pool);
1261   block_name = store_get(params, "0");
1262   module = apr_pstrdup(call_pool, block_name);
1263 
1264   /** get module worker */
1265   if ((last = strchr(block_name, ':'))) {
1266     module = apr_strtok(module, ":", &last);
1267     if (*module == '_') {
1268       module++;
1269       block_name = apr_pstrcat(call_pool, "_", last, NULL);
1270     }
1271     else {
1272       block_name = apr_pstrdup(call_pool, last);
1273     }
1274     if (!(blocks = apr_hash_get(worker->modules, module, APR_HASH_KEY_STRING))) {
1275       worker_log(worker, LOG_ERR, "Could not find module \"%s\"", module);
1276       return APR_EINVAL;
1277     }
1278   }
1279   else {
1280     blocks = worker->blocks;
1281   }
1282 
1283   /** get block from module */
1284   /* CR BEGIN */
1285   apr_thread_mutex_lock(worker->mutex);
1286   if (!(block = apr_hash_get(blocks, block_name, APR_HASH_KEY_STRING))) {
1287     worker_log(worker, LOG_ERR, "Could not find block %s", block_name);
1288     /* CR END */
1289     apr_thread_mutex_unlock(worker->mutex);
1290     status = APR_ENOENT;
1291     goto error;
1292   }
1293   else {
1294     int log_mode;
1295     int i;
1296     int j;
1297     char *index;
1298     const char *arg;
1299     const char *val;
1300     char *all = "";
1301 
1302     /** prepare call */
1303     /* iterate over indexed params and resolve VAR(foo) stuff*/
1304     for (i = 1; i < store_get_size(params); i++) {
1305       index = apr_itoa(ptmp, i);
1306       if ((val = store_get(params, index))) {
1307         val = worker_get_value_from_param(worker, val, ptmp);
1308         store_set(params, index, val);
1309       }
1310     }
1311 
1312     for (i = 1; i < store_get_size(params); i++) {
1313       index = apr_itoa(ptmp, i);
1314       if ((val = store_get(params, index))) {
1315         all = apr_pstrcat(ptmp, all, val, " ", NULL);
1316       }
1317     }
1318 
1319     /* handle parameters first */
1320     for (i = 1; i < store_get_size(block->params); i++) {
1321       index = apr_itoa(ptmp, i);
1322       if (!(arg = store_get(block->params, index))) {
1323         worker_log(worker, LOG_ERR, "Param missmatch for block \"%s\"", block->name);
1324         apr_thread_mutex_unlock(worker->mutex);
1325         status = APR_EGENERAL;
1326         goto error;
1327       }
1328       if (!(val = store_get(params, index))) {
1329         worker_log(worker, LOG_ERR, "Param missmatch for block \"%s\"", block->name);
1330         apr_thread_mutex_unlock(worker->mutex);
1331         status = APR_EGENERAL;
1332         goto error;
1333       }
1334       if (arg && val) {
1335         val = worker_get_value_from_param(worker, val, ptmp);
1336         store_set(params, arg, val);
1337       }
1338     }
1339 
1340     /* handle return variables second */
1341     j = i;
1342     for (i = 0; i < store_get_size(block->retvars); i++, j++) {
1343       index = apr_itoa(call_pool, j);
1344       if (!(arg = store_get(block->retvars, index))) {
1345         worker_log(worker, LOG_ERR, "Return variables missmatch for block \"%s\"", block->name);
1346         apr_thread_mutex_unlock(worker->mutex);
1347         status = APR_EGENERAL;
1348         goto error;
1349       }
1350       if (!(val = store_get(params, index))) {
1351         worker_log(worker, LOG_ERR, "Return variables missmatch for block \"%s\"", block->name);
1352         apr_thread_mutex_unlock(worker->mutex);
1353         status = APR_EGENERAL;
1354         goto error;
1355       }
1356       if (arg && val) {
1357         store_set(retvars, arg, val);
1358       }
1359     }
1360 
1361     if (block->lines) {
1362       lines = my_table_deep_copy(call_pool, block->lines);
1363     }
1364     else {
1365       lines = worker->lines;
1366     }
1367     apr_thread_mutex_unlock(worker->mutex);
1368     /* CR END */
1369 
1370     call = apr_pcalloc(call_pool, sizeof(*call));
1371     memcpy(call, worker, sizeof(*call));
1372     call->block = block;
1373     call->params = params;
1374     call->retvars = retvars;
1375     call->locals = locals;
1376     call->lines = lines;
1377     log_mode = logger_get_mode(call->logger);
1378     if (log_mode == LOG_CMD) {
1379       logger_set_mode(call->logger, LOG_INFO);
1380     }
1381     status = block->interpret(call, worker, call_pool);
1382 
1383     /** get infos from call back to worker */
1384     logger_set_mode(call->logger, log_mode);
1385     cmd = worker->cmd;
1386     lines = worker->lines;
1387     params = worker->params;
1388     retvars = worker->retvars;
1389     locals = worker->locals;
1390     memcpy(worker, call, sizeof(*worker));
1391     store_merge(worker->vars, call->retvars);
1392     worker->params = params;
1393     worker->retvars = retvars;
1394     worker->locals = locals;
1395     worker->lines = lines;
1396     worker->cmd = cmd;
1397     worker->block = NULL;
1398 
1399     goto error;
1400   }
1401 
1402 error:
1403   /** all ends here */
1404   apr_pool_destroy(call_pool);
1405   return status;
1406 }
1407 
1408 /**
1409  * log formated wrapper
1410  * @param worker IN thread data object
1411  * @param mode IN log mode
1412  *                LOG_DEBUG for a lot of infos
1413  *                LOG_INFO for much infos
1414  *                LOG_ERR for only very few infos
1415  * @param fmt IN printf format string
1416  * @param ... IN params for format strings
1417  */
worker_log(worker_t * worker,int mode,char * fmt,...)1418 void worker_log(worker_t * worker, int mode, char *fmt, ...) {
1419   va_list va;
1420   va_start(va, fmt);
1421   logger_log_va(worker->logger, mode, worker_get_file_and_line(worker), fmt, va);
1422   va_end(va);
1423 }
1424 
1425 /**
1426  * log buffer wrapper
1427  * @param logger IN thread data object
1428  * @param mode IN log mode
1429  *                LOG_DEBUG for a lot of infos
1430  *                LOG_INFO for much infos
1431  *                LOG_ERR for only very few infos
1432  * @param dir IN <,>,+,=
1433  * @param buf IN buf to print (binary data allowed)
1434  * @param len IN buf len
1435  */
worker_log_buf(worker_t * worker,int mode,char dir,const char * buf,apr_size_t len)1436 void worker_log_buf(worker_t * worker, int mode, char dir, const char *buf,
1437                     apr_size_t len) {
1438   if (buf == NULL) {
1439     len = 0;
1440   }
1441   logger_log_buf(worker->logger, mode, dir, buf, len);
1442 }
1443 
1444 
1445 /**
1446  * Read headers from transport
1447  * @param worker IN thread data object
1448  * @param sockreader IN reader
1449  * @return apr status
1450  */
worker_get_headers(worker_t * worker,sockreader_t * sockreader)1451 static apr_status_t worker_get_headers(worker_t *worker,
1452                                        sockreader_t *sockreader) {
1453   apr_status_t status;
1454   char *line;
1455   char *last;
1456   char *key = NULL;
1457   const char *val = "";
1458   recorder_t *recorder = worker_get_recorder(worker);
1459 
1460   /** get headers */
1461   while ((status = sockreader_read_line(sockreader, &line)) == APR_SUCCESS &&
1462          line[0] != 0) {
1463     if ((status = htt_run_read_header(worker, line)) != APR_SUCCESS) {
1464       return status;
1465     }
1466     if (recorder->on == RECORDER_RECORD &&
1467         recorder->flags & RECORDER_RECORD_HEADERS) {
1468       sockreader_push_line(recorder->sockreader, line);
1469     }
1470     worker_log_buf(worker, LOG_INFO, '<', line, strlen(line));
1471     worker_match(worker, worker->match.dot, line, strlen(line));
1472     worker_match(worker, worker->match.headers, line, strlen(line));
1473     worker_match(worker, worker->grep.dot, line, strlen(line));
1474     worker_match(worker, worker->grep.headers, line, strlen(line));
1475     worker_expect(worker, worker->expect.dot, line, strlen(line));
1476     worker_expect(worker, worker->expect.headers, line, strlen(line));
1477 
1478     /* headers */
1479     key = apr_strtok(line, ":", &last);
1480     val = last;
1481     while (*val == ' ') ++val;
1482     if (worker->headers_allow) {
1483       if (!apr_table_get(worker->headers_allow, key)) {
1484         worker_log(worker, LOG_ERR, "%s header not allowed", key);
1485         return APR_EGENERAL;
1486       }
1487     }
1488     if (worker->headers_filter) {
1489       if (!apr_table_get(worker->headers_filter, key)) {
1490                                 apr_table_add(worker->headers, key, val);
1491       }
1492     }
1493     else {
1494       apr_table_add(worker->headers, key, val);
1495     }
1496   }
1497   if (status == APR_SUCCESS && line[0] == 0) {
1498     worker_log_buf(worker, LOG_INFO, '<', NULL, 0);
1499   }
1500   return status;
1501 }
1502 
1503 /**
1504  * Wait for data (same as command_recv)
1505  * @param self IN command object
1506  * @param worker IN thread data object
1507  * @param data IN <number> or variable name
1508  * @return an apr status
1509  */
command_WAIT(command_t * self,worker_t * worker,char * data,apr_pool_t * ptmp)1510 apr_status_t command_WAIT(command_t * self, worker_t * worker,
1511                           char *data, apr_pool_t *ptmp) {
1512   char *copy;
1513   char *line;
1514   char *buf;
1515   apr_status_t status;
1516   sockreader_t *sockreader;
1517   char *var = NULL;
1518   const char *val = "";
1519   apr_size_t len;
1520   apr_ssize_t recv_len = -1;
1521   apr_size_t peeklen;
1522 
1523   recorder_t *recorder = worker_get_recorder(worker);
1524   buf = NULL;
1525   len = 0;
1526 
1527   COMMAND_OPTIONAL_ARG;
1528 
1529   if ((status = worker_flush(worker, ptmp)) != APR_SUCCESS) {
1530     goto out_err;
1531   }
1532 
1533   if (apr_isdigit(copy[0])) {
1534     recv_len = apr_atoi64(copy);
1535   }
1536   else {
1537     if (copy[0]) {
1538       var = copy;
1539       apr_collapse_spaces(var, var);
1540     }
1541     recv_len = -1;
1542   }
1543 
1544   if (recorder->on == RECORDER_PLAY) {
1545     worker->socket->sockreader = recorder->sockreader;
1546   }
1547 
1548   /**
1549    * Give modules a chance to setup stuff before _WAIT read from network
1550    */
1551   if ((status = htt_run_WAIT_begin(worker)) != APR_SUCCESS) {
1552     goto out_err;
1553   }
1554 
1555   if (worker->socket->sockreader == NULL) {
1556     peeklen = worker->socket->peeklen;
1557     worker->socket->peeklen = 0;
1558     if ((status = sockreader_new(&worker->socket->sockreader,
1559                                  worker->socket->transport,
1560 				 worker->socket->peek, peeklen))
1561         != APR_SUCCESS) {
1562       goto out_err;
1563     }
1564   }
1565   sockreader = worker->socket->sockreader;
1566 
1567   /* bodies were read but not store */
1568   if (worker->flags & FLAGS_IGNORE_BODY) {
1569     sockreader_set_options(sockreader, SOCKREADER_OPTIONS_IGNORE_BODY);
1570   }
1571   else {
1572     sockreader_set_options(sockreader, SOCKREADER_OPTIONS_NONE);
1573   }
1574 
1575   if (worker->headers) {
1576     apr_table_clear(worker->headers);
1577   }
1578   else {
1579     worker->headers = apr_table_make(worker->pbody, 5);
1580   }
1581 
1582   if (worker->headers_add) {
1583     int i;
1584     apr_table_entry_t *e;
1585 
1586     e = (apr_table_entry_t *) apr_table_elts(worker->headers_add)->elts;
1587     for (i = 0; i < apr_table_elts(worker->headers_add)->nelts; ++i) {
1588       apr_table_add(worker->headers, e[i].key, e[i].val);
1589     }
1590     apr_table_clear(worker->headers_add);
1591   }
1592 
1593   /**
1594    * Give modules the possibility to expect/grep/match there own stuff
1595    */
1596   if ((status = htt_run_read_pre_headers(worker)) != APR_SUCCESS) {
1597     goto out_err;
1598   }
1599 
1600   /** Status line, make that a little fuzzy in reading trailing empty lines of last
1601    *  request */
1602   while ((status = sockreader_read_line(sockreader, &line)) == APR_SUCCESS &&
1603       line[0] == 0);
1604   if (line[0] != 0) {
1605     if ((status = htt_run_read_status_line(worker, line)) != APR_SUCCESS) {
1606       goto out_err;
1607     }
1608     if (recorder->on == RECORDER_RECORD &&
1609 	recorder->flags & RECORDER_RECORD_STATUS) {
1610       sockreader_push_line(recorder->sockreader, line);
1611     }
1612     worker_log_buf(worker, LOG_INFO, '<', line, strlen(line));
1613     worker_match(worker, worker->match.dot, line, strlen(line));
1614     worker_match(worker, worker->match.headers, line, strlen(line));
1615     worker_match(worker, worker->grep.dot, line, strlen(line));
1616     worker_match(worker, worker->grep.headers, line, strlen(line));
1617     worker_expect(worker, worker->expect.dot, line, strlen(line));
1618     worker_expect(worker, worker->expect.headers, line, strlen(line));
1619 
1620     if (!strstr(line, "HTTP/") && !strstr(line, "ICAP/")) {
1621       worker_log(worker, LOG_DEBUG, "Not HTTP or ICAP version in \"%s\", must be HTTP/0.9", line);
1622       apr_table_add(worker->headers, "Connection", "close");
1623       status = sockreader_push_line(sockreader, line);
1624       goto http_0_9;
1625     }
1626   }
1627   else {
1628     worker_log_buf(worker, LOG_INFO, '<', line, strlen(line));
1629     worker_log(worker, LOG_ERR, "No status line received");
1630     status = APR_EINVAL;
1631     goto out_err;
1632   }
1633 
1634   status = worker_get_headers(worker, sockreader);
1635 
1636 http_0_9:
1637   if (status == APR_SUCCESS) {
1638     int doreadtrailing = 0;
1639     /* if recv len is specified use this */
1640     if (recv_len > 0) {
1641       len = recv_len;
1642       if ((status = worker_check_error(worker, content_length_reader(sockreader, &buf, &len, val)))
1643           != APR_SUCCESS) {
1644         goto out_err;
1645       }
1646     }
1647     else if (recv_len == 0) {
1648       buf = NULL;
1649     }
1650     /* else get transfer type */
1651     else if ((val = apr_table_get(worker->headers, "Content-Length"))) {
1652       len = apr_atoi64(val);
1653       if ((status = worker_check_error(worker, content_length_reader(sockreader, &buf, &len, val)))
1654           != APR_SUCCESS) {
1655         goto out_err;
1656       }
1657     }
1658     else if ((val = apr_table_get(worker->headers, "Transfer-Encoding"))) {
1659       if ((status = transfer_enc_reader(sockreader, &buf, &len, val)) == APR_SUCCESS) {
1660         if (strcmp(val, "chunked") == 0) {
1661           doreadtrailing = 1;
1662         }
1663       }
1664       else if ((status = worker_check_error(worker, status)) != APR_SUCCESS) {
1665         goto out_err;
1666       }
1667     }
1668     else if ((val = apr_table_get(worker->headers, "Encapsulated"))) {
1669       if ((status = worker_check_error(worker, encapsulated_reader(sockreader, &buf, &len, val, apr_table_get(worker->headers, "Preview"))))
1670           != APR_SUCCESS) {
1671         goto out_err;
1672       }
1673     }
1674     else if (worker->flags & FLAGS_CLIENT &&
1675 	     (val = apr_table_get(worker->headers, "Connection"))) {
1676       if ((status = worker_check_error(worker, eof_reader(sockreader, &buf, &len, val)))
1677           != APR_SUCCESS) {
1678         goto out_err;
1679       }
1680     }
1681     if ((status = htt_run_read_buf(worker, buf, len)) != APR_SUCCESS) {
1682       goto out_err;
1683     }
1684     if ((status = worker_handle_buf(worker, ptmp, buf, len)) != APR_SUCCESS) {
1685       goto out_err;
1686     }
1687     if (recorder->on == RECORDER_RECORD &&
1688         recorder->flags & RECORDER_RECORD_BODY) {
1689       sockreader_push_line(recorder->sockreader, "");
1690       sockreader_push_back(recorder->sockreader, buf, len);
1691     }
1692     if (var) {
1693       worker_var_set_and_zero_terminate(worker, var, buf, len);
1694     }
1695     if (doreadtrailing) {
1696       /* read trailing headers */
1697       if ((status = worker_get_headers(worker, sockreader)) != APR_SUCCESS) {
1698         worker_log(worker, LOG_ERR, "Missing trailing empty header(s) after chunked encoded body");
1699       }
1700     }
1701 
1702     if (worker->flags & FLAGS_AUTO_CLOSE) {
1703       val = apr_table_get(worker->headers, "Connection");
1704       if (val && strcasecmp(val, "close") == 0) {
1705         command_CLOSE(self, worker, "do not test expects", ptmp);
1706       }
1707     }
1708     if (worker->flags & FLAGS_AUTO_COOKIE) {
1709       /* get all set cookie and store them in cookie line */
1710       worker_set_cookie(worker);
1711     }
1712   }
1713 
1714 out_err:
1715   if (recorder->on == RECORDER_PLAY) {
1716     sockreader_destroy(&recorder->sockreader);
1717     recorder->on = RECORDER_OFF;
1718     worker->socket->sockreader = NULL;
1719   }
1720   else {
1721     ++worker->req_cnt;
1722   }
1723   status = worker_assert(worker, status);
1724 
1725   /**
1726    * Give modules a chance to cleanup stuff after _WAIT
1727    */
1728   htt_run_WAIT_end(worker, status);
1729   return status;
1730 }
1731 
1732 /**
1733  * Bind to socket and wait for data (same as command_RES and command_WAIT).
1734  * Ignores TCP connections not sending any data (open/close).
1735  *
1736  * @param self IN command object
1737  * @param worker IN thread data object
1738  * @param data IN not used
1739  *
1740  * @return an apr status
1741  */
command_RESWAIT(command_t * self,worker_t * worker,char * data,apr_pool_t * ptmp)1742 apr_status_t command_RESWAIT(command_t * self, worker_t * worker, char * data,
1743                              apr_pool_t *ptmp) {
1744   apr_status_t status;
1745   do {
1746     status = command_RES(self, worker, "", ptmp);
1747     if(status != APR_SUCCESS) {
1748       return status;
1749     }
1750     status = command_WAIT(self, worker, "", ptmp);
1751     if(status == APR_EOF) {
1752       /* EOF = client failed to send data */
1753       command_CLOSE(self, worker, "do not test expects", ptmp);
1754     }
1755   } while(status == APR_EOF);
1756   return status;
1757 }
1758 
1759 /****
1760  * Scriptable commands
1761  ****/
1762 
1763 /**
1764  * Get socket from hash or add a new one
1765  *
1766  * @param self IN thread data object
1767  * @param hostname IN host name
1768  * @param portname IN port as ascii string
1769  *
1770  */
worker_get_socket(worker_t * self,const char * hostname,const char * portname)1771 void worker_get_socket(worker_t *self, const char *hostname,
1772                        const char *portname) {
1773   socket_t *socket;
1774   char *tag;
1775   apr_pool_t *pool;
1776 
1777   HT_POOL_CREATE(&pool);
1778   socket =
1779     apr_hash_get(self->sockets, apr_pstrcat(self->pbody, hostname, portname,
1780 	                                    NULL),
1781 	         APR_HASH_KEY_STRING);
1782 
1783   if (!socket) {
1784     socket = apr_pcalloc(self->pbody, sizeof(*socket));
1785     socket->config = apr_hash_make(self->pbody);
1786     socket->socket_state = SOCKET_CLOSED;
1787     tag = apr_pstrdup(self->pbody, portname);
1788     apr_hash_set(self->sockets, apr_pstrcat(self->pbody, hostname, tag,
1789 	                                    NULL),
1790 	         APR_HASH_KEY_STRING, socket);
1791   }
1792 
1793   self->socket = socket;
1794   apr_pool_destroy(pool);
1795 }
1796 
1797 /**
1798  * Setup a connection to host
1799  *
1800  * @param self IN command object
1801  * @param worker IN thread data object
1802  * @param data IN aditional data
1803  *
1804  * @return an apr status
1805  */
command_REQ(command_t * self,worker_t * worker,char * data,apr_pool_t * ptmp)1806 apr_status_t command_REQ(command_t * self, worker_t * worker,
1807                          char *data, apr_pool_t *ptmp) {
1808   apr_status_t status;
1809   char *portname;
1810   char *hostname;
1811   char *last;
1812   char *copy;
1813 
1814   COMMAND_NEED_ARG("Need hostname and port");
1815 
1816   if ((status = worker_flush(worker, ptmp)) != APR_SUCCESS) {
1817     return status;
1818   }
1819 
1820   if ((status = worker_test_unused(worker)) != APR_SUCCESS) {
1821     return status;
1822   }
1823 
1824   hostname = apr_strtok(copy, " ", &last);
1825   portname = apr_strtok(NULL, " ", &last);
1826 
1827   /* use hostname and portname for unique id of sockets, portname may also have
1828    * additional tags and infos which are possible resolved in the following
1829    * hook
1830    */
1831   worker_log(worker, LOG_DEBUG, "get socket \"%s:%s\"", hostname, portname);
1832   worker_get_socket(worker, hostname, portname);
1833 
1834   if ((status = htt_run_client_port_args(worker, portname, &portname, last)) != APR_SUCCESS) {
1835     return status;
1836   }
1837 
1838   if (worker->socket->socket_state == SOCKET_CLOSED) {
1839     if ((status = htt_run_pre_connect(worker)) != APR_SUCCESS) {
1840       return status;
1841     }
1842     if ((status = tcp_connect(worker, hostname, portname)) != APR_SUCCESS) {
1843       return status;
1844     }
1845     if ((status = htt_run_connect(worker)) != APR_SUCCESS) {
1846       return status;
1847     }
1848     if ((status = htt_run_post_connect(worker)) != APR_SUCCESS) {
1849       return status;
1850     }
1851     worker->socket->socket_state = SOCKET_CONNECTED;
1852   }
1853 
1854   worker_test_reset(worker);
1855 
1856   return APR_SUCCESS;
1857 }
1858 
1859 /**
1860  * Setup a connection to host
1861  *
1862  * @param self IN command object
1863  * @param worker IN thread data object
1864  * @param data IN unused
1865  *
1866  * @return an apr status
1867  */
command_RES(command_t * self,worker_t * worker,char * data,apr_pool_t * ptmp)1868 apr_status_t command_RES(command_t * self, worker_t * worker,
1869                          char *data, apr_pool_t *ptmp) {
1870   apr_status_t status;
1871   char *copy;
1872   int ignore_monitors = 0;
1873   char *cur;
1874   char *last;
1875   char *reasemble = NULL;
1876 
1877   COMMAND_OPTIONAL_ARG;
1878 
1879   if ((status = worker_flush(worker, ptmp)) != APR_SUCCESS) {
1880     return status;
1881   }
1882 
1883   if ((status = worker_test_unused(worker)) != APR_SUCCESS) {
1884     return status;
1885   }
1886 
1887   worker_get_socket(worker, "Default", "0");
1888 
1889   cur = apr_strtok(copy, " ", &last);
1890   while (cur) {
1891     if (strcmp("IGNORE_MONITORS", cur) == 0) {
1892       ignore_monitors = 1;
1893     }
1894     else {
1895       reasemble = apr_pstrcat(ptmp, (reasemble ? reasemble : ""), (reasemble ? " " : ""), cur, NULL);
1896     }
1897     cur = apr_strtok(NULL, " ", &last);
1898   }
1899   if (!reasemble) {
1900     reasemble = apr_pstrdup(ptmp, "");
1901   }
1902 
1903   while (worker->socket->socket_state == SOCKET_CLOSED) {
1904     int interim;
1905 
1906     if ((status = tcp_accept(worker)) != APR_SUCCESS) {
1907       worker_log(worker, LOG_ERR, "Accept TCP connection aborted");
1908       return status;
1909     }
1910 
1911     interim = htt_run_accept(worker, reasemble);
1912 
1913     if (ignore_monitors) {
1914       if (interim == APR_SUCCESS) {
1915         worker->socket->peeklen = 1;
1916         status = transport_read(worker->socket->transport, worker->socket->peek, &worker->socket->peeklen);
1917         if (status != APR_SUCCESS && status != APR_EOF) {
1918           worker_log(worker, LOG_ERR, "Peek abort");
1919           return status;
1920         }
1921         else if (status == APR_SUCCESS) {
1922           worker->socket->socket_state = SOCKET_CONNECTED;
1923         }
1924       }
1925       else {
1926         worker_conn_close(worker, NULL);
1927       }
1928     }
1929     else if (interim == APR_SUCCESS) {
1930       worker->socket->socket_state = SOCKET_CONNECTED;
1931     }
1932     else {
1933       return interim;
1934     }
1935   }
1936 
1937   worker_test_reset(worker);
1938 
1939   return APR_SUCCESS;
1940 }
1941 
1942 /**
1943  * Close socket
1944  *
1945  * @param self IN command object
1946  * @param worker IN thread data object
1947  * @param data IN unused
1948  *
1949  * @return an apr status
1950  */
command_CLOSE(command_t * self,worker_t * worker,char * data,apr_pool_t * ptmp)1951 apr_status_t command_CLOSE(command_t * self, worker_t * worker,
1952                            char *data, apr_pool_t *ptmp) {
1953   apr_status_t status;
1954   char *copy;
1955 
1956   COMMAND_OPTIONAL_ARG;
1957 
1958   if ((status = worker_flush(worker, ptmp)) != APR_SUCCESS) {
1959     worker_conn_close(worker, NULL);
1960     return status;
1961   }
1962 
1963   if (strcmp(copy, "do not test expects") != 0) {
1964     if ((status = worker_test_unused(worker)) != APR_SUCCESS) {
1965       worker_conn_close(worker, NULL);
1966       return status;
1967     }
1968   }
1969   else {
1970     /* do not test expects and remove this string */
1971     copy = NULL;
1972   }
1973 
1974   if ((status = worker_conn_close(worker, copy)) != APR_SUCCESS) {
1975     return status;
1976   }
1977 
1978   return APR_SUCCESS;
1979 }
1980 
1981 /**
1982  * Specify a timeout for socket operations (ms)
1983  *
1984  * @param self IN command object
1985  * @param worker IN thread data object
1986  * @param data IN time in ms
1987  *
1988  * @return an apr status
1989  */
command_TIMEOUT(command_t * self,worker_t * worker,char * data,apr_pool_t * ptmp)1990 apr_status_t command_TIMEOUT(command_t * self, worker_t * worker,
1991                              char *data, apr_pool_t *ptmp) {
1992   apr_time_t tmo;
1993   char *copy;
1994 
1995   COMMAND_NEED_ARG("Time not specified");
1996 
1997   tmo = apr_atoi64(copy);
1998   worker->socktmo = tmo * 1000;
1999 
2000   return APR_SUCCESS;
2001 }
2002 
2003 /**
2004  * Define an expect
2005  *
2006  * @param self IN command object
2007  * @param worker IN thread data object
2008  * @param data IN "%s %s" type match
2009  *
2010  * @return an apr status
2011  */
command_EXPECT(command_t * self,worker_t * worker,char * data,apr_pool_t * ptmp)2012 apr_status_t command_EXPECT(command_t * self, worker_t * worker,
2013                             char *data, apr_pool_t *ptmp) {
2014   char *last;
2015   char *type;
2016   char *match;
2017   htt_regex_t *compiled;
2018   const char *err;
2019   int off;
2020   char *copy;
2021   char *interm;
2022   apr_pool_t *pool;
2023 
2024   COMMAND_NEED_ARG("Type and htt_regex not specified");
2025 
2026   type = apr_strtok(copy, " ", &last);
2027 
2028   interm = my_unescape(last, &last);
2029 
2030   if (last) {
2031     while (*last == ' ') ++last;
2032     if (*last != 0) {
2033       worker_log(worker, LOG_ERR, "there is more stuff behind last quote");
2034       return APR_EGENERAL;
2035     }
2036   }
2037 
2038   if (!type) {
2039     worker_log(worker, LOG_ERR, "Type not specified");
2040     return APR_EGENERAL;
2041   }
2042 
2043   pool = module_get_config(worker->config, apr_pstrcat(ptmp, "EXPECT ", type, NULL));
2044   if (!pool) {
2045     /* create a pool for match */
2046     HT_POOL_CREATE(&pool);
2047     module_set_config(worker->config, apr_pstrcat(pool, "EXPECT ", type, NULL), pool);
2048   }
2049   match = apr_pstrdup(pool, interm);
2050 
2051   if (!match) {
2052     worker_log(worker, LOG_ERR, "Regex not specified");
2053     return APR_EGENERAL;
2054   }
2055 
2056   if (interm[0] == '!') {
2057     ++interm;
2058   }
2059 
2060   if (!(compiled = htt_regexcomp(pool, interm, &err, &off))) {
2061     worker_log(worker, LOG_ERR, "EXPECT regcomp failed: \"%s\"", last);
2062     return APR_EINVAL;
2063   }
2064 
2065   if (strcmp(type, ".") == 0) {
2066     apr_table_addn(worker->expect.dot, match, (char *) compiled);
2067   }
2068   else if (strcasecmp(type, "Headers") == 0) {
2069     apr_table_addn(worker->expect.headers, match, (char *) compiled);
2070   }
2071   else if (strcasecmp(type, "Body") == 0) {
2072     apr_table_addn(worker->expect.body, match, (char *) compiled);
2073   }
2074   else if (strcasecmp(type, "Exec") == 0) {
2075     apr_table_addn(worker->expect.exec, match, (char *) compiled);
2076   }
2077   else if (strcasecmp(type, "Error") == 0) {
2078     apr_table_addn(worker->expect.error, match, (char *) compiled);
2079   }
2080   else if (strncasecmp(type, "Var(", 4) == 0) {
2081     const char *val;
2082     char *var;
2083 
2084     var= apr_strtok(type, "(", &last);
2085     var = apr_strtok(NULL, ")", &last);
2086     val = worker_var_get(worker, var);
2087     if (val) {
2088       apr_table_t *tmp_table;
2089       tmp_table = apr_table_make(ptmp, 1);
2090       apr_table_addn(tmp_table, match, (char *) compiled);
2091       worker_expect(worker, tmp_table, val, strlen(val));
2092       return worker_assert_expect(worker, tmp_table, "EXPECT var",
2093 	                          APR_SUCCESS);
2094     }
2095     else {
2096       worker_log(worker, LOG_ERR, "Variable \"%s\" does not exist", var);
2097       return APR_EINVAL;
2098     }
2099   }
2100   else {
2101     worker_log(worker, LOG_ERR, "EXPECT type \"%s\" unknown", type);
2102     return APR_EINVAL;
2103   }
2104 
2105   return APR_SUCCESS;
2106 }
2107 
2108 /**
2109  * Define an expect
2110  *
2111  * @param self IN command object
2112  * @param worker IN thread data object
2113  * @param data IN "%s %s %s" type match variable
2114  *
2115  * @return an apr status
2116  */
command_MATCH(command_t * self,worker_t * worker,char * data,apr_pool_t * ptmp)2117 apr_status_t command_MATCH(command_t * self, worker_t * worker,
2118                            char *data, apr_pool_t *ptmp) {
2119   char *tmp;
2120   char *last;
2121   char *type;
2122   char *match;
2123   char *vars;
2124   htt_regex_t *compiled;
2125   const char *err;
2126   int off;
2127   char *copy;
2128   apr_pool_t *pool;
2129 
2130   COMMAND_NEED_ARG("Type, htt_regex and variable not specified");
2131 
2132   type = apr_strtok(copy, " ", &last);
2133 
2134   match = my_unescape(last, &last);
2135 
2136   tmp = apr_strtok(NULL, "", &last);
2137 
2138   if (!type) {
2139     worker_log(worker, LOG_ERR, "Type not specified");
2140     return APR_EGENERAL;
2141   }
2142 
2143   pool = module_get_config(worker->config, apr_pstrcat(ptmp, "MATCH ", type, NULL));
2144   if (!pool) {
2145     /* create a pool for match */
2146     HT_POOL_CREATE(&pool);
2147     module_set_config(worker->config, apr_pstrcat(pool, "MATCH ", type, NULL), pool);
2148   }
2149   vars = apr_pstrdup(pool, tmp);
2150 
2151   if (!match) {
2152     worker_log(worker, LOG_ERR, "Regex not specified");
2153     return APR_EGENERAL;
2154   }
2155 
2156   if (!vars) {
2157     worker_log(worker, LOG_ERR, "Variable not specified");
2158     return APR_EGENERAL;
2159   }
2160 
2161   if (vars) {
2162     ++vars;
2163   }
2164 
2165   if (!vars) {
2166     return APR_EINVAL;
2167   }
2168 
2169   if (!(compiled = htt_regexcomp(pool, match, &err, &off))) {
2170     worker_log(worker, LOG_ERR, "MATCH regcomp failed: %s", last);
2171     return APR_EINVAL;
2172   }
2173   if (strcmp(type, ".") == 0) {
2174     apr_table_addn(worker->match.dot, vars, (char *) compiled);
2175   }
2176   else if (strcasecmp(type, "Headers") == 0) {
2177     apr_table_addn(worker->match.headers, vars, (char *) compiled);
2178   }
2179   else if (strcasecmp(type, "Body") == 0) {
2180     apr_table_addn(worker->match.body, vars, (char *) compiled);
2181   }
2182   else if (strcasecmp(type, "Error") == 0) {
2183     apr_table_addn(worker->match.error, vars, (char *) compiled);
2184   }
2185   else if (strcasecmp(type, "Exec") == 0) {
2186     apr_table_addn(worker->match.exec, vars, (char *) compiled);
2187   }
2188   else if (strncasecmp(type, "Var(", 4) == 0) {
2189     const char *val;
2190     char *var;
2191 
2192     var= apr_strtok(type, "(", &last);
2193     var = apr_strtok(NULL, ")", &last);
2194     val = worker_var_get(worker, var);
2195     if (val) {
2196       apr_table_t *tmp_table;
2197       tmp_table = apr_table_make(ptmp, 1);
2198       apr_table_addn(tmp_table, vars, (char *) compiled);
2199       worker_match(worker, tmp_table, val, strlen(val));
2200       return worker_assert_match(worker, tmp_table, "MATCH var",
2201 	                         APR_SUCCESS);
2202     }
2203     else {
2204       /* this should cause an error? */
2205     }
2206   }
2207   else {
2208     worker_log(worker, LOG_ERR, "Match type %s does not exist", type);
2209     return APR_ENOENT;
2210   }
2211 
2212   return APR_SUCCESS;
2213 }
2214 
2215 /**
2216  * Define an grep
2217  *
2218  * @param self IN command object
2219  * @param worker IN thread data object
2220  * @param data IN "%s %s %s" type grep variable
2221  *
2222  * @return an apr status
2223  */
command_GREP(command_t * self,worker_t * worker,char * data,apr_pool_t * ptmp)2224 apr_status_t command_GREP(command_t * self, worker_t * worker,
2225                           char *data, apr_pool_t *ptmp) {
2226   char *tmp;
2227   char *last;
2228   char *type;
2229   char *grep;
2230   char *vars;
2231   htt_regex_t *compiled;
2232   const char *err;
2233   int off;
2234   char *copy;
2235   apr_pool_t *pool;
2236 
2237   COMMAND_NEED_ARG("Type, htt_regex and variable not specified");
2238 
2239   type = apr_strtok(copy, " ", &last);
2240 
2241   grep = my_unescape(last, &last);
2242 
2243   tmp = apr_strtok(NULL, "", &last);
2244 
2245   if (!type) {
2246     worker_log(worker, LOG_ERR, "Type not specified");
2247     return APR_EGENERAL;
2248   }
2249 
2250   pool = module_get_config(worker->config, apr_pstrcat(ptmp, "GREP ", type, NULL));
2251   if (!pool) {
2252     /* create a pool for match */
2253     HT_POOL_CREATE(&pool);
2254     module_set_config(worker->config, apr_pstrcat(pool, "GREP ", type, NULL), pool);
2255   }
2256   vars = apr_pstrdup(pool, tmp);
2257 
2258   if (!grep) {
2259     worker_log(worker, LOG_ERR, "Regex not specified");
2260     return APR_EGENERAL;
2261   }
2262 
2263   if (!vars) {
2264     worker_log(worker, LOG_ERR, "Variable not specified");
2265     return APR_EGENERAL;
2266   }
2267 
2268   if (vars) {
2269     ++vars;
2270   }
2271 
2272   if (!vars) {
2273     return APR_EINVAL;
2274   }
2275 
2276   if (!(compiled = htt_regexcomp(pool, grep, &err, &off))) {
2277     worker_log(worker, LOG_ERR, "MATCH regcomp failed: %s", last);
2278     return APR_EINVAL;
2279   }
2280   if (strcmp(type, ".") == 0) {
2281     apr_table_addn(worker->grep.dot, vars, (char *) compiled);
2282   }
2283   else if (strcasecmp(type, "Headers") == 0) {
2284     apr_table_addn(worker->grep.headers, vars, (char *) compiled);
2285   }
2286   else if (strcasecmp(type, "Body") == 0) {
2287     apr_table_addn(worker->grep.body, vars, (char *) compiled);
2288   }
2289   else if (strcasecmp(type, "Error") == 0) {
2290     apr_table_addn(worker->grep.error, vars, (char *) compiled);
2291   }
2292   else if (strcasecmp(type, "Exec") == 0) {
2293     apr_table_addn(worker->grep.exec, vars, (char *) compiled);
2294   }
2295   else if (strncasecmp(type, "Var(", 4) == 0) {
2296     const char *val;
2297     char *var;
2298 
2299     var= apr_strtok(type, "(", &last);
2300     var = apr_strtok(NULL, ")", &last);
2301     val = worker_var_get(worker, var);
2302     if (val) {
2303       apr_table_t *tmp_table;
2304       tmp_table = apr_table_make(ptmp, 1);
2305       apr_table_addn(tmp_table, vars, (char *) compiled);
2306       worker_match(worker, tmp_table, val, strlen(val));
2307     }
2308     else {
2309       /* this should cause an error? */
2310     }
2311   }
2312   else {
2313     worker_log(worker, LOG_ERR, "Grep type %s does not exist", type);
2314     return APR_ENOENT;
2315   }
2316 
2317   return APR_SUCCESS;
2318 }
2319 
2320 /**
2321  * assert command
2322  *
2323  * @param self IN command object
2324  * @param worker IN thread data object
2325  * @param data IN expression
2326  *
2327  * @return an apr status
2328  */
command_ASSERT(command_t * self,worker_t * worker,char * data,apr_pool_t * ptmp)2329 apr_status_t command_ASSERT(command_t * self, worker_t * worker,
2330                             char *data, apr_pool_t *ptmp) {
2331   apr_status_t status;
2332   char *copy;
2333   char **argv;
2334   apr_size_t len;
2335   long val;
2336   math_eval_t *math = math_eval_make(ptmp);
2337 
2338   COMMAND_NEED_ARG("expression");
2339 
2340   my_tokenize_to_argv(copy, &argv, ptmp, 0);
2341 
2342   if (!argv[0]) {
2343     worker_log(worker, LOG_ERR, "Need an expression");
2344     return APR_EINVAL;
2345   }
2346 
2347   len = strlen(argv[0]);
2348   if (len < 1) {
2349     worker_log(worker, LOG_ERR, "Empty expression");
2350     return APR_EINVAL;
2351   }
2352 
2353   if (strcmp(argv[0], "_STRING_EQUAL") == 0) {
2354     if (argv[1] == 0) {
2355       worker_log(worker, LOG_ERR, "Need two strings, got none");
2356       return APR_EINVAL;
2357     }
2358     else if (argv[2] == 0) {
2359       worker_log(worker, LOG_ERR, "Need two strings, got one");
2360       return APR_EINVAL;
2361     }
2362     else if (strcmp(argv[1], argv[2]) != 0) {
2363       worker_log(worker, LOG_ERR, "Strings not equal");
2364       return APR_EINVAL;
2365     }
2366     val = 1;
2367   }
2368   else {
2369     if ((status = math_evaluate(math, argv[0], &val)) != APR_SUCCESS) {
2370       worker_log(worker, LOG_ERR, "Invalid expression");
2371       return status;
2372     }
2373   }
2374 
2375   if (!val) {
2376     worker_log(worker, LOG_ERR, "Did expect \"%s\"", argv[0]);
2377     return APR_EINVAL;
2378   }
2379 
2380   return APR_SUCCESS;
2381 }
2382 
2383 /**
2384  * Single line variable
2385  * @param copy IN copy of variable=value
2386  * @param value OUT value
2387  * @return variable
2388  */
single_line_variable(worker_t * worker,char * copy,char ** value)2389 static const char *single_line_variable(worker_t *worker, char *copy,
2390                                         char **value) {
2391   return apr_strtok(copy, "=", value);
2392 }
2393 
2394 /**
2395  * Single line variable
2396  * @param copy IN copy variable=value
2397  * @param value OUT value
2398  * @param ptmp IN temp pool
2399  * @return variable
2400  */
multi_line_variable(worker_t * worker,char * copy,char ** value,apr_pool_t * ptmp)2401 static const char *multi_line_variable(worker_t *worker, char *copy,
2402                                        char **value, apr_pool_t *ptmp) {
2403   char *delimiter;
2404   char *var;
2405   char *val;
2406   apr_table_entry_t *e ;
2407   int to;
2408   int delim_found = 0;
2409   int store_cmd = worker->cmd;
2410 
2411   var = apr_strtok(copy, "<", &delimiter);
2412   apr_collapse_spaces(delimiter, delimiter);
2413 
2414   /* read line until delimiter, delimiter can be indented! */
2415   e = (apr_table_entry_t *) apr_table_elts(worker->lines)->elts;
2416 
2417   to = worker->cmd_to ? worker->cmd_to : apr_table_elts(worker->lines)->nelts;
2418 
2419   val = NULL;
2420   for (worker->cmd = worker->cmd_from + 1; worker->cmd < to; worker->cmd++) {
2421     int i;
2422     char *line = e[worker->cmd].val;
2423     for (i = 0; line[i] == ' '; i++);
2424     if (strcmp(delimiter, &line[i]) == 0) {
2425       delim_found = 1;
2426       break;
2427     }
2428     else {
2429       val = val ? apr_pstrcat(ptmp, val, "\n", line, NULL) : apr_pstrdup(ptmp, line);
2430     }
2431   }
2432 
2433   if (!delim_found) {
2434     int old_cmd = worker->cmd;
2435     worker->cmd = store_cmd;
2436     worker_log(worker, LOG_ERR,
2437                "No ending delimiter \"%s\" found for multiline variable",
2438                delimiter);
2439     return NULL;
2440     worker->cmd = old_cmd;
2441   }
2442 
2443   if (val == NULL) {
2444     val = apr_pstrdup(ptmp, "");
2445   }
2446 
2447   *value = val;
2448 
2449   return var;
2450 }
2451 
2452 /**
2453  * set command
2454  *
2455  * @param self IN command object
2456  * @param worker IN thread data object
2457  * @param data IN key=value
2458  *
2459  * @return an apr status
2460  */
command_SET(command_t * self,worker_t * worker,char * data,apr_pool_t * ptmp)2461 apr_status_t command_SET(command_t * self, worker_t * worker, char *data,
2462                          apr_pool_t *ptmp) {
2463   const char *vars_key;
2464   char *vars_val;
2465   char *copy;
2466   int i;
2467 
2468   COMMAND_NEED_ARG("Variable and value not specified");
2469 
2470   for (i = 0; copy[i] != 0 && strchr(VAR_ALLOWED_CHARS, copy[i]); i++);
2471 
2472   if (copy[i] == '=') {
2473     /* single line */
2474     vars_key = single_line_variable(worker, copy, &vars_val);
2475   }
2476   else if (copy[i] == '<') {
2477     /* multi line */
2478     vars_key = multi_line_variable(worker, copy, &vars_val, ptmp);
2479   }
2480   else {
2481     vars_key = apr_strtok(copy, "=<", &vars_val);
2482     worker_log(worker, LOG_ERR, "Char '%c' is not allowed in variable \"%s\"", copy[i], vars_key);
2483   }
2484 
2485   if (!vars_key) {
2486     worker_log(worker, LOG_ERR, "Key not specified");
2487     return APR_EGENERAL;
2488   }
2489 
2490   if (!vars_val) {
2491     worker_log(worker, LOG_ERR, "Value not specified");
2492     return APR_EGENERAL;
2493   }
2494 
2495   vars_val = worker_replace_vars(worker, vars_val, NULL, ptmp); \
2496   worker_var_set(worker, vars_key, vars_val);
2497 
2498   return APR_SUCCESS;
2499 }
2500 
2501 /**
2502  * unset command
2503  *
2504  * @param self IN command object
2505  * @param worker IN thread data object
2506  * @param data IN key
2507  *
2508  * @return an apr status
2509  */
command_UNSET(command_t * self,worker_t * worker,char * data,apr_pool_t * ptmp)2510 apr_status_t command_UNSET(command_t * self, worker_t * worker,
2511                            char *data, apr_pool_t *ptmp) {
2512   const char *var;
2513   char *copy;
2514   int i;
2515 
2516   COMMAND_NEED_ARG("Variable and value not specified");
2517 
2518   var = copy;
2519   for (i = 0; var[i] != 0 && strchr(VAR_ALLOWED_CHARS, var[i]); i++);
2520   if (var[i] != 0) {
2521     worker_log(worker, LOG_ERR, "Char '%c' is not allowed in \"%s\"", var[i], var);
2522     return APR_EINVAL;
2523   }
2524 
2525   if (store_get(worker->locals, var)) {
2526     store_unset(worker->locals, var);
2527   }
2528   else {
2529     store_unset(worker->vars, var);
2530   }
2531 
2532   return APR_SUCCESS;
2533 }
2534 
2535 /**
2536  * Send data
2537  *
2538  * @param self IN command object
2539  * @param worker IN thread data object
2540  * @param data IN data to send
2541  *
2542  * @return an apr status
2543  */
command_DATA(command_t * self,worker_t * worker,char * data,apr_pool_t * ptmp)2544 apr_status_t command_DATA(command_t * self, worker_t * worker,
2545                           char *data, apr_pool_t *ptmp) {
2546   char *copy;
2547   int unresolved;
2548 
2549   if (!worker->socket) {
2550     return APR_ENOSOCKET;
2551   }
2552 
2553   copy = apr_pstrdup(ptmp, data);
2554   copy = worker_replace_vars(worker, copy, &unresolved, ptmp);
2555   worker_log(worker, LOG_CMD, "%s%s", self->name, copy);
2556 
2557 
2558   if (strncasecmp(copy, "Content-Length: AUTO", 20) == 0) {
2559     apr_table_add(worker->cache, "Content-Length", "Content-Length");
2560   }
2561   else if (strncasecmp(copy, "Encapsulated: ", 14) == 0 && strstr(copy, "AUTO")) {
2562     apr_table_add(worker->cache, "Encapsulated", copy);
2563   }
2564   else if (strncasecmp(copy, "Cookie: AUTO", 12) == 0) {
2565     apr_table_add(worker->cache, "Cookie", "Cookie");
2566   }
2567   else if (strncasecmp(copy, "Expect: 100-Continue", 20) == 0) {
2568     apr_table_add(worker->cache, "100-Continue", copy);
2569   }
2570   else {
2571     if (unresolved) {
2572       apr_table_add(worker->cache, "PLAIN;resolve", copy);
2573     }
2574     else {
2575       apr_table_add(worker->cache, "PLAIN", copy);
2576     }
2577   }
2578 
2579   return APR_SUCCESS;
2580 }
2581 
2582 /**
2583  * Flush data
2584  *
2585  * @param self IN command object
2586  * @param worker IN thread data object
2587  * @param data IN unused
2588  *
2589  * @return an apr status
2590  */
command_FLUSH(command_t * self,worker_t * worker,char * data,apr_pool_t * ptmp)2591 apr_status_t command_FLUSH(command_t * self, worker_t * worker,
2592                            char *data, apr_pool_t *ptmp) {
2593   apr_status_t status;
2594 
2595   COMMAND_NO_ARG;
2596 
2597   if ((status = worker_flush(worker, ptmp)) != APR_SUCCESS) {
2598     return status;
2599   }
2600 
2601   return APR_SUCCESS;
2602 }
2603 
2604 /**
2605  * Chunk info
2606  *
2607  * @param self IN command object
2608  * @param worker IN thread data object
2609  * @param data IN unused
2610  *
2611  * @return an apr status
2612  */
command_CHUNK(command_t * self,worker_t * worker,char * data,apr_pool_t * ptmp)2613 apr_status_t command_CHUNK(command_t * self, worker_t * worker,
2614                            char *data, apr_pool_t *ptmp) {
2615   apr_status_t status;
2616 
2617   COMMAND_NO_ARG;
2618 
2619   apr_table_add(worker->cache, "CHUNKED", "CHUNKED");
2620 
2621   if ((status = worker_flush(worker, ptmp)) != APR_SUCCESS) {
2622     return status;
2623   }
2624 
2625   return APR_SUCCESS;
2626 }
2627 
2628 /**
2629  * read from file descriptor and write it to the HTTP stream
2630  *
2631  * @param worker IN thread data object
2632  * @param file IN open file descriptor for reading
2633  * @param flags IN FLAGS_CHUNKED or FLAGS_NONE
2634  *
2635  * @return APR_SUCCESS or an apr error status
2636  */
worker_file_to_http(worker_t * worker,apr_file_t * file,int flags,apr_pool_t * ptmp)2637 apr_status_t worker_file_to_http(worker_t *worker, apr_file_t *file, int flags, apr_pool_t *ptmp) {
2638   apr_status_t status;
2639   apr_size_t len;
2640   char *buf;
2641 
2642   while (1) {
2643     if (flags & FLAGS_CHUNKED) {
2644       len = worker->chunksize;
2645     }
2646     else {
2647       len = BLOCK_MAX;
2648     }
2649     buf = apr_pcalloc(worker->pcache, len + 1);
2650     if ((status = apr_file_read(file, buf, &len)) != APR_SUCCESS) {
2651       break;
2652     }
2653     buf[len] = 0;
2654     apr_table_addn(worker->cache,
2655 		   apr_psprintf(worker->pcache, "NOCRLF:%"APR_SIZE_T_FMT, len), buf);
2656     if (flags & FLAGS_CHUNKED) {
2657       worker_log(worker, LOG_DEBUG, "--- chunk size: %"APR_SIZE_T_FMT, len);
2658       apr_table_add(worker->cache, "CHUNKED", "CHUNKED");
2659       if ((status = worker_flush(worker, ptmp)) != APR_SUCCESS) {
2660 	return status;
2661       }
2662     }
2663   }
2664 
2665   if (APR_STATUS_IS_EOF(status)) {
2666     return APR_SUCCESS;
2667   }
2668   else {
2669     return status;
2670   }
2671 }
2672 
2673 /**
2674  * Report error of a child if any
2675  *
2676  * @param pool IN
2677  * @param err IN
2678  * @param description IN error description
2679  */
child_errfn(apr_pool_t * pool,apr_status_t err,const char * description)2680 static void child_errfn(apr_pool_t *pool, apr_status_t err,
2681                         const char *description) {
2682   fprintf(stderr, "\nChild error occure: %s", description);
2683   fflush(stderr);
2684 }
2685 
2686 /**
2687  * Execute an external program
2688  *
2689  * @param self IN command object
2690  * @param worker IN thread data object
2691  * @param data IN external program call with arguments
2692  *
2693  * @return an apr status
2694  */
command_EXEC(command_t * self,worker_t * worker,char * data,apr_pool_t * ptmp)2695 apr_status_t command_EXEC(command_t * self, worker_t * worker,
2696                           char *data, apr_pool_t *ptmp) {
2697   char *copy;
2698   apr_status_t status;
2699   apr_procattr_t *attr;
2700   bufreader_t *br;
2701   const char *progname;
2702   const char * const*args;
2703   apr_exit_why_e exitwhy;
2704   int exitcode;
2705   int flags;
2706 
2707   COMMAND_NEED_ARG("Need a shell command");
2708 
2709   flags = worker->flags;
2710   worker->flags &= ~FLAGS_PIPE;
2711   worker->flags &= ~FLAGS_CHUNKED;
2712   worker->flags &= ~FLAGS_PIPE_IN;
2713   worker->flags &= ~FLAGS_FILTER;
2714   /* pipe http to shell */
2715   if (copy[0] == '|') {
2716     ++copy;
2717     worker->flags |= FLAGS_PIPE_IN;
2718   }
2719   /* filter http */
2720   else if (copy[0] == '<') {
2721     ++copy;
2722     worker->flags |= FLAGS_FILTER;
2723   }
2724 
2725   my_tokenize_to_argv(copy, (char ***)&args, ptmp, 1);
2726   progname = args[0];
2727 
2728   if (!progname) {
2729     worker_log(worker, LOG_ERR, "No program name specified");
2730     return APR_EGENERAL;
2731   }
2732 
2733   if ((status = apr_procattr_create(&attr, worker->pbody)) != APR_SUCCESS) {
2734     return status;
2735   }
2736 
2737   if ((status = apr_procattr_cmdtype_set(attr, APR_SHELLCMD_ENV)) != APR_SUCCESS) {
2738     return status;
2739   }
2740 
2741   if ((status = apr_procattr_detach_set(attr, 0)) != APR_SUCCESS) {
2742     return status;
2743   }
2744 
2745   if ((status = apr_procattr_error_check_set(attr, 1)) != APR_SUCCESS) {
2746     return status;
2747   }
2748 
2749   if ((status = apr_procattr_child_errfn_set(attr, child_errfn))
2750       != APR_SUCCESS) {
2751     return status;
2752   }
2753 
2754   if ((status = apr_procattr_io_set(attr,  APR_FULL_BLOCK, APR_FULL_BLOCK,
2755 				    APR_NO_PIPE))
2756       != APR_SUCCESS) {
2757     return status;
2758   }
2759 
2760   {
2761     apr_pool_t *pool;
2762     apr_proc_t *proc;
2763 
2764     HT_POOL_CREATE(&pool);
2765     proc = apr_pcalloc(pool, sizeof(*proc));
2766     if ((status = apr_proc_create(proc, progname, args, NULL, attr,
2767             worker->pbody)) != APR_SUCCESS) {
2768       goto exit;
2769     }
2770 
2771     if (flags & FLAGS_PIPE) {
2772       worker_log(worker, LOG_DEBUG, "write stdout to http: %s", progname);
2773       if ((status = worker_file_to_http(worker, proc->out, flags, ptmp))
2774           != APR_SUCCESS) {
2775         goto exit;
2776       }
2777     }
2778     else if (worker->flags & FLAGS_PIPE_IN || worker->flags & FLAGS_FILTER) {
2779       /* do not wait for proc termination here */
2780       exec_t *exec = apr_pcalloc(pool, sizeof(*exec));
2781       exec->pool = pool;
2782       exec->proc = proc;
2783       module_set_config(worker->config, apr_pstrdup(pool, "EXEC"), exec);
2784       return status;
2785     }
2786     else {
2787       apr_size_t len = 0;
2788       char *buf = NULL;
2789 
2790       worker_log(worker, LOG_DEBUG, "read stdin: %s", progname);
2791       status = bufreader_new(&br, proc->out, worker->pbody);
2792       if (status == APR_SUCCESS || APR_STATUS_IS_EOF(status)) {
2793         status = APR_SUCCESS;
2794         bufreader_read_eof(br, &buf, &len);
2795       }
2796       else {
2797         goto exit;
2798       }
2799 
2800       if (buf) {
2801         worker_log_buf(worker, LOG_INFO, '<', buf, len);
2802         worker_match(worker, worker->match.exec, buf, len);
2803         worker_match(worker, worker->grep.exec, buf, len);
2804         worker_expect(worker, worker->expect.exec, buf, len);
2805       }
2806 
2807       status = worker_assert_match(worker, worker->match.exec, "MATCH exec",
2808           status);
2809       status = worker_assert_expect(worker, worker->expect.exec, "EXPECT exec",
2810           status);
2811       status = worker_assert_grep(worker, worker->grep.exec, "GREP exec",
2812           status);
2813     }
2814 
2815     worker_log(worker, LOG_DEBUG, "wait for: %s", progname);
2816     apr_proc_wait(proc, &exitcode, &exitwhy, APR_WAIT);
2817 
2818     apr_file_close(proc->in);
2819     apr_file_close(proc->out);
2820 
2821     if (exitcode != 0) {
2822       status = APR_EGENERAL;
2823     }
2824 
2825 exit:
2826     apr_pool_destroy(pool);
2827     return status;
2828   }
2829 }
2830 
2831 /**
2832  * Send file
2833  *
2834  * @param self IN command object
2835  * @param worker IN thread data object
2836  * @param data IN file
2837  *
2838  * @return an apr status
2839  */
command_SENDFILE(command_t * self,worker_t * worker,char * data,apr_pool_t * ptmp)2840 apr_status_t command_SENDFILE(command_t * self, worker_t * worker,
2841                               char *data, apr_pool_t *ptmp) {
2842   char *copy;
2843   char **argv;
2844   apr_status_t status;
2845   int flags;
2846   apr_file_t *fp;
2847   int i;
2848 
2849   COMMAND_NEED_ARG("Need a file name");
2850 
2851   my_tokenize_to_argv(copy, &argv, ptmp, 0);
2852 
2853   flags = worker->flags;
2854   worker->flags &= ~FLAGS_PIPE;
2855   worker->flags &= ~FLAGS_CHUNKED;
2856 
2857   for (i = 0; argv[i]; i++) {
2858     if ((status =
2859          apr_file_open(&fp, argv[i], APR_READ, APR_OS_DEFAULT,
2860                        ptmp)) != APR_SUCCESS) {
2861       worker_log(worker, LOG_ERR, "\nCan not send file: File \"%s\" not found", copy);
2862       return APR_ENOENT;
2863     }
2864 
2865     if ((status = worker_file_to_http(worker, fp, flags, ptmp))
2866                                 != APR_SUCCESS) {
2867       return status;
2868     }
2869 
2870     apr_file_close(fp);
2871   }
2872 
2873   return APR_SUCCESS;
2874 }
2875 
2876 /**
2877  * Declare a pipe
2878  *
2879  * @param self IN command object
2880  * @param worker IN thread data object
2881  * @param data IN not used
2882  *
2883  * @return an apr status
2884  */
command_PIPE(command_t * self,worker_t * worker,char * data,apr_pool_t * ptmp)2885 apr_status_t command_PIPE(command_t * self, worker_t * worker,
2886                           char *data, apr_pool_t *ptmp) {
2887   char *copy;
2888   char *last;
2889   char *add;
2890   char *val;
2891 
2892   COMMAND_OPTIONAL_ARG;
2893 
2894   add = apr_strtok(copy, " ", &last);
2895   if (add) {
2896     val = apr_strtok(NULL, " ", &last);
2897   }
2898   else {
2899     val = NULL;
2900   }
2901 
2902   worker_log(worker, LOG_DEBUG, "additional: %s, value: %s", add, val);
2903 
2904   if (add && strncasecmp(add, "chunked", 7) == 0) {
2905     worker->chunksize = val ? apr_atoi64(val) : BLOCK_MAX;
2906     worker->flags |= FLAGS_CHUNKED;
2907   }
2908 
2909   worker->flags |= FLAGS_PIPE;
2910 
2911   return APR_SUCCESS;
2912 }
2913 
2914 /**
2915  * Send data without a CRLF
2916  *
2917  * @param self IN command object
2918  * @param worker IN thread data object
2919  * @param data IN data to send
2920  *
2921  * @return an apr status
2922  */
command_NOCRLF(command_t * self,worker_t * worker,char * data,apr_pool_t * ptmp)2923 apr_status_t command_NOCRLF(command_t * self, worker_t * worker,
2924                             char *data, apr_pool_t *ptmp) {
2925   char *copy;
2926   int unresolved;
2927 
2928   copy = apr_pstrdup(ptmp, data);
2929   copy = worker_replace_vars(worker, copy, &unresolved, ptmp);
2930   worker_log(worker, LOG_CMD, "%s%s", self->name, copy);
2931 
2932   if (unresolved) {
2933     apr_table_add(worker->cache, "NOCRLF;resolve", copy);
2934   }
2935   else {
2936     apr_table_add(worker->cache, "NOCRLF", copy);
2937   }
2938 
2939   return APR_SUCCESS;
2940 }
2941 
2942 /**
2943  * Send data without a CRLF
2944  *
2945  * @param self IN command object
2946  * @param worker IN thread data object
2947  * @param data IN data to send
2948  *
2949  * @return an apr status
2950  */
command_SOCKSTATE(command_t * self,worker_t * worker,char * data,apr_pool_t * ptmp)2951 apr_status_t command_SOCKSTATE(command_t * self, worker_t * worker,
2952                                char *data, apr_pool_t *ptmp) {
2953   char *copy;
2954 
2955   COMMAND_NEED_ARG("Need a variable name");
2956 
2957   if (!worker->socket) {
2958     worker_var_set(worker, copy, "UNDEF");
2959   }
2960 
2961   if (worker_sockstate(worker) == APR_SUCCESS) {
2962     worker_var_set(worker, copy, "CONNECTED");
2963   }
2964   else {
2965     worker_var_set(worker, copy, "CLOSED");
2966   }
2967 
2968   return APR_SUCCESS;
2969 }
2970 
2971 /**
2972  * HEADER command
2973  *
2974  * @param self IN command
2975  * @param worker IN thread data object
2976  * @param data IN header name (spaces are possible)
2977  *
2978  * @return APR_SUCCESS
2979  */
command_HEADER(command_t * self,worker_t * worker,char * data,apr_pool_t * ptmp)2980 apr_status_t command_HEADER(command_t *self, worker_t *worker, char *data,
2981                             apr_pool_t *ptmp) {
2982   char *copy;
2983   char *method;
2984   char *header;
2985   char *last;
2986 
2987   COMMAND_NEED_ARG("Need method ALLOW or FILTER and a header name");
2988 
2989   method = apr_strtok(copy, " ", &last);
2990   header = apr_strtok(NULL, " ", &last);
2991 
2992   if (strcasecmp(method, "ALLOW") == 0) {
2993     if (!worker->headers_allow) {
2994       worker->headers_allow = apr_table_make(worker->pbody, 10);
2995     }
2996     apr_table_add(worker->headers_allow, header, method);
2997   }
2998   else if (strcasecmp(method, "FILTER") == 0) {
2999     if (!worker->headers_filter) {
3000       worker->headers_filter = apr_table_make(worker->pbody, 5);
3001     }
3002     apr_table_add(worker->headers_filter, header, method);
3003   }
3004   else {
3005     return APR_ENOTIMPL;
3006   }
3007 
3008   return APR_SUCCESS;
3009 }
3010 
3011 /**
3012  * DEBUG command
3013  *
3014  * @param self IN command
3015  * @param worker IN thread data object
3016  * @param data IN string to print on stderr
3017  *
3018  * @return APR_SUCCESS
3019  */
command_DEBUG(command_t * self,worker_t * worker,char * data,apr_pool_t * ptmp)3020 apr_status_t command_DEBUG(command_t *self, worker_t *worker, char *data,
3021                            apr_pool_t *ptmp) {
3022   char *copy;
3023 
3024   COMMAND_OPTIONAL_ARG;
3025 
3026   /* Using LOG_NONE so this prints, regardless of httest internal log level */
3027   worker_log(worker, LOG_NONE, "%s", copy);
3028 
3029   return APR_SUCCESS;
3030 }
3031 
3032 /**
3033  * Setup listener
3034  *
3035  * @param worker IN thread data object
3036  *
3037  * @return APR_SUCCESS
3038  */
worker_listener_up(worker_t * worker,apr_int32_t backlog)3039 apr_status_t worker_listener_up(worker_t *worker, apr_int32_t backlog) {
3040   apr_status_t status = APR_SUCCESS;
3041 
3042   worker_get_socket(worker, "Default", "0");
3043 
3044   status = tcp_listen(worker, backlog);
3045 
3046   worker->socket->socket_state = SOCKET_CLOSED;
3047 
3048   return status;
3049 }
3050 
3051 /**
3052  * UP command bind a listener socket
3053  *
3054  * @param self IN command
3055  * @param worker IN thread data object
3056  * @param data IN unused
3057  *
3058  * @return APR_SUCCESS
3059  */
command_UP(command_t * self,worker_t * worker,char * data,apr_pool_t * ptmp)3060 apr_status_t command_UP(command_t *self, worker_t *worker, char *data,
3061                         apr_pool_t *ptmp) {
3062   char *copy;
3063 
3064   apr_int32_t backlog = LISTENBACKLOG_DEFAULT;
3065 
3066   COMMAND_OPTIONAL_ARG;
3067 
3068   if (copy[0] != '\0') {
3069     backlog = apr_atoi64(copy);
3070   }
3071 
3072   return worker_listener_up(worker, backlog);
3073 }
3074 
3075 /**
3076  * DOWN command shuts down listener
3077  *
3078  * @param self IN command
3079  * @param worker IN thread data object
3080  * @param data IN unused
3081  *
3082  * @return APR_SUCCESS
3083  */
command_DOWN(command_t * self,worker_t * worker,char * data,apr_pool_t * ptmp)3084 apr_status_t command_DOWN(command_t *self, worker_t *worker, char *data,
3085                           apr_pool_t *ptmp) {
3086   apr_status_t status;
3087 
3088   COMMAND_NO_ARG;
3089 
3090   if (!worker->listener) {
3091     worker_log(worker, LOG_ERR, "Server allready down", self->name);
3092     return APR_EGENERAL;
3093   }
3094 
3095   if ((status = apr_socket_close(worker->listener)) != APR_SUCCESS) {
3096     return status;
3097   }
3098   worker->listener = NULL;
3099   return status;
3100 }
3101 
3102 /**
3103  * LOG_LEVEL command sets log level
3104  *
3105  * @param self IN command
3106  * @param worker IN thread data object
3107  * @param data IN number 0-4
3108  *
3109  * @return APR_SUCCESS
3110  */
command_LOG_LEVEL_SET(command_t * self,worker_t * worker,char * data,apr_pool_t * ptmp)3111 apr_status_t command_LOG_LEVEL_SET(command_t *self, worker_t *worker, char *data,
3112                                    apr_pool_t *ptmp) {
3113   char *copy;
3114 
3115   COMMAND_NEED_ARG("Need a number between 0 and 4");
3116 
3117   logger_set_mode(worker->logger, apr_atoi64(copy));
3118 
3119   return APR_SUCCESS;
3120 }
3121 
3122 /**
3123  * GET_LOG_LEVEL command sets log level
3124  *
3125  * @param self IN command
3126  * @param worker IN thread data object
3127  * @param data IN number 0-4
3128  *
3129  * @return APR_SUCCESS
3130  */
command_LOG_LEVEL_GET(command_t * self,worker_t * worker,char * data,apr_pool_t * ptmp)3131 apr_status_t command_LOG_LEVEL_GET(command_t *self, worker_t *worker, char *data,
3132                                    apr_pool_t *ptmp) {
3133   char *copy;
3134 
3135   COMMAND_NEED_ARG("<variable>");
3136 
3137   worker_var_set(worker, copy, apr_itoa(ptmp, logger_get_mode(worker->logger)));
3138   return APR_SUCCESS;
3139 }
3140 
3141 /**
3142  * RECV command
3143  *
3144  * @param self IN command
3145  * @param worker IN thread data object
3146  * @param data IN either POLL or number of bytes
3147  *
3148  * @return APR_SUCCESS
3149  */
command_RECV(command_t * self,worker_t * worker,char * data,apr_pool_t * ptmp)3150 apr_status_t command_RECV(command_t *self, worker_t *worker, char *data,
3151                           apr_pool_t *ptmp) {
3152   char *copy;
3153   apr_status_t status;
3154   apr_size_t recv_len;
3155   apr_size_t peeklen;
3156   sockreader_t *sockreader;
3157   char *last;
3158   char *val;
3159 
3160   char *buf = NULL;
3161   int poll = 0;
3162 
3163   COMMAND_NEED_ARG("Need a number or POLL");
3164 
3165   /* get first value, can be either POLL or a number */
3166   val = apr_strtok(copy, " ", &last);
3167   if (strcasecmp(val, "POLL") == 0) {
3168     poll = 1;
3169     /* recv_len to max and timeout to min */
3170     recv_len = BLOCK_MAX;
3171     /* set timout to specified socket tmo */
3172     if ((status = transport_set_timeout(worker->socket->transport,
3173                                         worker->socktmo)) != APR_SUCCESS) {
3174       return status;
3175     }
3176   }
3177   else if (strcasecmp(val, "CHUNKED") == 0) {
3178     recv_len = 0;
3179   }
3180   else if (strcasecmp(val, "CLOSE") == 0) {
3181     recv_len = 0;
3182   }
3183   else {
3184     /* must be a number */
3185     recv_len = apr_atoi64(val);
3186   }
3187 
3188   if (worker->socket->sockreader == NULL) {
3189     peeklen = worker->socket->peeklen;
3190     worker->socket->peeklen = 0;
3191     if ((status = sockreader_new(&worker->socket->sockreader,
3192                                  worker->socket->transport,
3193 				 worker->socket->peek, peeklen))
3194         != APR_SUCCESS) {
3195       goto out_err;
3196     }
3197   }
3198   sockreader = worker->socket->sockreader;
3199 
3200   if (strcasecmp(val, "CHUNKED") == 0) {
3201     if ((status = transfer_enc_reader(sockreader, &buf, &recv_len, "chunked")) != APR_SUCCESS) {
3202       goto out_err;
3203     }
3204   }
3205   else if (strcasecmp(val, "CLOSE") == 0) {
3206     if ((status = eof_reader(sockreader, &buf, &recv_len, "close")) != APR_SUCCESS) {
3207       goto out_err;
3208     }
3209   }
3210   else {
3211     if ((status = content_length_reader(sockreader, &buf, &recv_len, "")) != APR_SUCCESS) {
3212       if (poll && APR_STATUS_IS_INCOMPLETE(status)) {
3213 	status = APR_SUCCESS;
3214       }
3215       else {
3216 	goto out_err;
3217       }
3218     }
3219   }
3220 
3221   if ((status = worker_handle_buf(worker, ptmp, buf, recv_len))
3222       != APR_SUCCESS) {
3223     goto out_err;
3224   }
3225 
3226 out_err:
3227   if (strcasecmp(last, "DO_NOT_CHECK") != 0) {
3228     status = worker_assert(worker, status);
3229   }
3230 
3231   return status;
3232 }
3233 
3234 /**
3235  * READLINE command
3236  *
3237  * @param self IN command
3238  * @param worker IN thread data object
3239  * @param data IN optional parameter DO_NOT_CHECK to avoid expect checking
3240  *
3241  * @return APR_SUCCESS
3242  */
command_READLINE(command_t * self,worker_t * worker,char * data,apr_pool_t * ptmp)3243 apr_status_t command_READLINE(command_t *self, worker_t *worker, char *data,
3244                               apr_pool_t *ptmp) {
3245   apr_status_t status;
3246   apr_size_t peeklen;
3247   apr_size_t len;
3248   sockreader_t *sockreader;
3249   char *copy;
3250   char *buf = NULL;
3251 
3252   COMMAND_OPTIONAL_ARG;
3253 
3254   if (worker->socket->sockreader == NULL) {
3255     peeklen = worker->socket->peeklen;
3256     worker->socket->peeklen = 0;
3257     if ((status = sockreader_new(&worker->socket->sockreader,
3258                                  worker->socket->transport,
3259 				 worker->socket->peek, peeklen))
3260         != APR_SUCCESS) {
3261       goto out_err;
3262     }
3263   }
3264   sockreader = worker->socket->sockreader;
3265 
3266   if ((status = sockreader_read_line(sockreader, &buf)) != APR_SUCCESS) {
3267     goto out_err;
3268   }
3269 
3270   if (buf) {
3271     len = strlen(buf);
3272     if ((status = worker_handle_buf(worker, ptmp, buf, len))
3273 	!= APR_SUCCESS) {
3274       goto out_err;
3275     }
3276   }
3277 
3278 out_err:
3279   if (strcasecmp(copy, "DO_NOT_CHECK") != 0) {
3280     status = worker_assert(worker, status);
3281   }
3282 
3283   return status;
3284 }
3285 
3286 /**
3287  * CHECK command
3288  *
3289  * @param self IN command
3290  * @param worker IN thread data object
3291  * @param data IN optional check match and expects
3292  *
3293  * @return APR_SUCCESS
3294  */
command_CHECK(command_t * self,worker_t * worker,char * data,apr_pool_t * ptmp)3295 apr_status_t command_CHECK(command_t *self, worker_t *worker, char *data,
3296                            apr_pool_t *ptmp) {
3297   apr_status_t status = worker_assert(worker, APR_SUCCESS);
3298   return status;
3299 }
3300 
3301 /**
3302  * WHICH command
3303  *
3304  * @param self IN command
3305  * @param worker IN thread data object
3306  * @param data IN varname
3307  *
3308  * @return APR_SUCCESS or apr error code
3309  */
command_WHICH(command_t * self,worker_t * worker,char * data,apr_pool_t * ptmp)3310 apr_status_t command_WHICH(command_t *self, worker_t *worker, char *data,
3311                            apr_pool_t *ptmp) {
3312   char *copy;
3313   char *result;
3314 
3315   COMMAND_NEED_ARG("<variable> expected");
3316 
3317   result  = apr_psprintf(ptmp, "%d", worker->which);
3318   worker_var_set(worker, copy, result);
3319 
3320   return APR_SUCCESS;
3321 }
3322 
3323 /**
3324  * ONLY_PRINTABLE command
3325  *
3326  * @param self IN command
3327  * @param worker IN thread data object
3328  * @param data IN on|off
3329  *
3330  * @return APR_SUCCESS or apr error code
3331  */
command_ONLY_PRINTABLE(command_t * self,worker_t * worker,char * data,apr_pool_t * ptmp)3332 apr_status_t command_ONLY_PRINTABLE(command_t *self, worker_t *worker, char *data,
3333                                     apr_pool_t *ptmp) {
3334   char *copy;
3335 
3336   COMMAND_NEED_ARG("Need on|off");
3337 
3338   if (strcasecmp(copy, "on") == 0) {
3339     worker->flags |= FLAGS_ONLY_PRINTABLE;
3340   }
3341   else {
3342     worker->flags &= ~FLAGS_ONLY_PRINTABLE;
3343   }
3344   return APR_SUCCESS;
3345 }
3346 
3347 /**
3348  * PRINT_HEX command
3349  * @param self IN command
3350  * @param worker IN thread data object
3351  * @param data IN on|off
3352  *
3353  * @return APR_SUCCESS or apr error code
3354  */
command_PRINT_HEX(command_t * self,worker_t * worker,char * data,apr_pool_t * ptmp)3355 apr_status_t command_PRINT_HEX(command_t *self, worker_t *worker, char *data,
3356                                apr_pool_t *ptmp) {
3357   char *copy;
3358 
3359   COMMAND_NEED_ARG("Need on|off");
3360 
3361   if (strcasecmp(copy, "on") == 0) {
3362     worker->flags |= FLAGS_PRINT_HEX;
3363   }
3364   else {
3365     worker->flags &= ~FLAGS_PRINT_HEX;
3366   }
3367   return APR_SUCCESS;
3368 }
3369 
3370 /**
3371  * SH command
3372  *
3373  * @param self IN command
3374  * @param worker IN thread data object
3375  * @param data IN
3376  *
3377  * @return APR_SUCCESS or apr error code
3378  */
command_SH(command_t * self,worker_t * worker,char * data,apr_pool_t * ptmp)3379 apr_status_t command_SH(command_t *self, worker_t *worker, char *data,
3380                         apr_pool_t *ptmp) {
3381   char *copy;
3382   apr_size_t len;
3383 
3384 #ifdef _WINDOWS
3385   char *name = apr_pstrdup(worker->pbody, "httXXXXXX.bat");
3386   char *exec_prefix = "";
3387   int has_apr_file_perms_set = 0;
3388 #else
3389   char *name = apr_pstrdup(worker->pbody, "httXXXXXX");
3390   char *exec_prefix = "./";
3391   int has_apr_file_perms_set = 1;
3392 #endif
3393   sh_t *sh = module_get_config(worker->config, SH_CONFIG);
3394 
3395   apr_status_t status = APR_SUCCESS;
3396 
3397   COMMAND_NEED_ARG("Either shell commands or END");
3398 
3399   if (strcasecmp(copy, "END")== 0) {
3400     if (sh) {
3401       if ((status = apr_file_name_get((const char **)&name, sh->tmpf)) != APR_SUCCESS) {
3402         return status;
3403       }
3404 
3405       if (has_apr_file_perms_set && (status = apr_file_perms_set(name, 0x700)) != APR_SUCCESS) {
3406         return status;
3407       }
3408 
3409       /* close file */
3410       apr_file_close(sh->tmpf);
3411 
3412       /* exec file */
3413       status = command_EXEC(self, worker, apr_pstrcat(worker->pbody, exec_prefix, name, NULL), sh->pool);
3414 
3415       apr_file_remove(name, sh->pool);
3416       module_set_config(worker->config, apr_pstrdup(sh->pool, SH_CONFIG), NULL);
3417       apr_pool_destroy(sh->pool);
3418     }
3419   }
3420   else {
3421     if (!sh) {
3422       apr_pool_t *pool;
3423       HT_POOL_CREATE(&pool);
3424       sh = apr_pcalloc(pool, sizeof(*sh));
3425       sh->pool = pool;
3426       if ((status = apr_file_mktemp(&sh->tmpf, name,
3427 	                            APR_CREATE | APR_READ | APR_WRITE |
3428 				    APR_EXCL, pool))
3429 	  != APR_SUCCESS) {
3430 	worker_log(worker, LOG_ERR, "Could not mk temp file %s(%d)",
3431 	           my_status_str(ptmp, status), status);
3432 	return status;
3433       }
3434     }
3435     module_set_config(worker->config, apr_pstrdup(sh->pool, SH_CONFIG), sh);
3436 
3437     len = strlen(copy);
3438     if ((status = file_write(sh->tmpf, copy, len)) != APR_SUCCESS) {
3439       worker_log(worker, LOG_ERR, "Could not write to temp file");
3440       return status;
3441     }
3442     len = 1;
3443     if ((status = file_write(sh->tmpf, "\n", len)) != APR_SUCCESS) {
3444       worker_log(worker, LOG_ERR, "Could not write to temp file");
3445       return status;
3446     }
3447   }
3448 
3449   return status;
3450 }
3451 
3452 /**
3453  * ADD_HEADER command
3454  *
3455  * @param self IN command
3456  * @param worker IN thread data object
3457  * @param data IN header value
3458  *
3459  * @return APR_SUCCESS or apr error code
3460  */
command_ADD_HEADER(command_t * self,worker_t * worker,char * data,apr_pool_t * ptmp)3461 apr_status_t command_ADD_HEADER(command_t *self, worker_t *worker, char *data,
3462                                 apr_pool_t *ptmp) {
3463   char *copy;
3464   char **argv;
3465 
3466   COMMAND_NEED_ARG("<header> <value>");
3467 
3468   if (!worker->headers_add) {
3469     worker->headers_add = apr_table_make(worker->pbody, 12);
3470   }
3471 
3472   my_tokenize_to_argv(copy, &argv, ptmp, 0);
3473   apr_table_add(worker->headers_add, argv[0], argv[1]);
3474 
3475   return APR_SUCCESS;
3476 }
3477 
3478 /**
3479  * TUNNEL command
3480  *
3481  * @param self IN command
3482  * @param worker IN thread data object
3483  * @param data IN <host> [SSL:]<port>
3484  *
3485  * @return APR_SUCCESS or APR_EGENERAL on wrong parameters
3486  */
command_TUNNEL(command_t * self,worker_t * worker,char * data,apr_pool_t * ptmp)3487 apr_status_t command_TUNNEL(command_t *self, worker_t *worker, char *data,
3488                             apr_pool_t *ptmp) {
3489   apr_status_t status;
3490   apr_threadattr_t *tattr;
3491   apr_thread_t *client_thread;
3492   apr_thread_t *backend_thread;
3493   tunnel_t client;
3494   tunnel_t backend;
3495   apr_size_t peeklen;
3496 
3497   if (!(worker->flags & FLAGS_SERVER)) {
3498     worker_log(worker, LOG_ERR, "This command is only valid in a SERVER");
3499     return APR_EGENERAL;
3500   }
3501 
3502   if (worker->socket->socket_state == SOCKET_CLOSED) {
3503     worker_log(worker, LOG_ERR, "Socket to client is closed\n");
3504     return APR_ECONNREFUSED;
3505   }
3506 
3507   worker_log(worker, LOG_DEBUG, "--- tunnel\n");
3508 
3509   /* client side */
3510   if ((status = transport_set_timeout(worker->socket->transport, 100000))
3511       != APR_SUCCESS) {
3512     goto error1;
3513   }
3514   if (worker->socket->sockreader == NULL) {
3515     peeklen = worker->socket->peeklen;
3516     worker->socket->peeklen = 0;
3517     status = sockreader_new(&client.sockreader, worker->socket->transport,
3518 	   	            worker->socket->peek, peeklen);
3519     if (status != APR_SUCCESS && !APR_STATUS_IS_TIMEUP(status)) {
3520       goto error1;
3521     }
3522   }
3523   else {
3524     client.sockreader = worker->socket->sockreader;
3525   }
3526   backend.sendto = worker->socket;
3527 
3528   /* backend side */
3529   if ((status = command_REQ(self, worker, data, ptmp)) != APR_SUCCESS) {
3530     goto error1;
3531   }
3532   if ((status = transport_set_timeout(worker->socket->transport, 100000))
3533       != APR_SUCCESS) {
3534     goto error2;
3535   }
3536   status = sockreader_new(&backend.sockreader, worker->socket->transport,
3537 		          NULL, 0);
3538   if (status != APR_SUCCESS && !APR_STATUS_IS_TIMEUP(status)) {
3539     goto error2;
3540   }
3541   client.sendto = worker->socket;
3542 
3543   /* need two threads reading/writing from/to backend */
3544   if ((status = apr_threadattr_create(&tattr, worker->pbody)) != APR_SUCCESS) {
3545     goto error2;
3546   }
3547 
3548   if ((status = apr_threadattr_stacksize_set(tattr, DEFAULT_THREAD_STACKSIZE))
3549       != APR_SUCCESS) {
3550     goto error2;
3551   }
3552 
3553   if ((status = apr_threadattr_detach_set(tattr, 0)) != APR_SUCCESS) {
3554     goto error2;
3555   }
3556 
3557   if ((status = apr_thread_create(&client_thread, tattr, streamer,
3558 	                          &client, worker->pbody)) != APR_SUCCESS) {
3559     goto error2;
3560   }
3561 
3562   if ((status = apr_thread_create(&backend_thread, tattr, streamer,
3563 	                          &backend, worker->pbody)) != APR_SUCCESS) {
3564     goto error2;
3565   }
3566 
3567   apr_thread_join(&status, client_thread);
3568   if (status != APR_SUCCESS) {
3569     goto error2;
3570   }
3571   apr_thread_join(&status, backend_thread);
3572   if (status != APR_SUCCESS) {
3573     goto error2;
3574   }
3575 
3576 error2:
3577   command_CLOSE(self, worker, "do not test expects", ptmp);
3578 error1:
3579   worker_get_socket(worker, "Default", "0");
3580   sockreader_destroy(&client.sockreader);
3581   sockreader_destroy(&backend.sockreader);
3582   worker_log(worker, LOG_DEBUG, "--- tunnel end\n");
3583   return status;
3584 }
3585 
3586 /**
3587  * BREAK command
3588  *
3589  * @param self IN command
3590  * @param worker IN thread data object
3591  * @param data IN unused
3592  *
3593  * @return APR_SUCCESS or APR_EGENERAL on wrong parameters
3594  */
command_BREAK(command_t * self,worker_t * worker,char * data,apr_pool_t * ptmp)3595 apr_status_t command_BREAK(command_t *self, worker_t *worker, char *data,
3596                            apr_pool_t *ptmp) {
3597   /* singal break for loop */
3598   COMMAND_NO_ARG;
3599   return -1;
3600 }
3601 
3602 /**
3603  * AUTO_CLOSE command
3604  *
3605  * @param self IN command
3606  * @param worker IN thread data object
3607  * @param data IN
3608  *
3609  * @return APR_SUCCESS or apr error code
3610  */
command_AUTO_CLOSE(command_t * self,worker_t * worker,char * data,apr_pool_t * ptmp)3611 apr_status_t command_AUTO_CLOSE(command_t *self, worker_t *worker, char *data,
3612                                 apr_pool_t *ptmp) {
3613   char *copy;
3614   COMMAND_NEED_ARG("on|off, default off");
3615 
3616   if (strcasecmp(copy, "on") == 0) {
3617     worker->flags |= FLAGS_AUTO_CLOSE;
3618   }
3619   else {
3620     worker->flags &= ~FLAGS_AUTO_CLOSE;
3621   }
3622   return APR_SUCCESS;
3623 }
3624 
3625 /**
3626  * AUTO_COOKIE command
3627  *
3628  * @param self IN command
3629  * @param worker IN thread data object
3630  * @param data IN
3631  *
3632  * @return APR_SUCCESS or apr error code
3633  */
command_AUTO_COOKIE(command_t * self,worker_t * worker,char * data,apr_pool_t * ptmp)3634 apr_status_t command_AUTO_COOKIE(command_t *self, worker_t *worker, char *data,
3635                                  apr_pool_t *ptmp) {
3636   char *copy;
3637   COMMAND_NEED_ARG("on|off, default off");
3638 
3639   if (strcasecmp(copy, "on") == 0) {
3640     worker->flags |= FLAGS_AUTO_COOKIE;
3641   }
3642   else {
3643     if (worker->socket) {
3644       apr_table_clear(worker->socket->cookies);
3645       worker->socket->cookie = NULL;
3646     }
3647     worker->flags &= ~FLAGS_AUTO_COOKIE;
3648   }
3649   return APR_SUCCESS;
3650 }
3651 
3652 /**
3653  * MATCH_SEQ command
3654  *
3655  * @param self IN command
3656  * @param worker IN thread data object
3657  * @param data IN sequence
3658  *
3659  * @return APR_SUCCESS or apr error code
3660  */
command_MATCH_SEQ(command_t * self,worker_t * worker,char * data,apr_pool_t * ptmp)3661 apr_status_t command_MATCH_SEQ(command_t *self, worker_t *worker, char *data,
3662                                apr_pool_t *ptmp) {
3663   char *copy;
3664   apr_pool_t *pool;
3665   COMMAND_NEED_ARG("<var-sequence>*");
3666 
3667   pool = module_get_config(worker->config, apr_pstrdup(ptmp, "MATCH_SEQ"));
3668   if (!pool) {
3669     /* create a pool for match */
3670     HT_POOL_CREATE(&pool);
3671     module_set_config(worker->config, apr_pstrdup(pool, "MATCH_SEQ"), pool);
3672   }
3673 
3674   worker->match_seq = apr_pstrdup(pool, copy);
3675   return APR_SUCCESS;
3676 }
3677 
3678 /**
3679  * RECORD command
3680  *
3681  * @param self IN command
3682  * @param worker IN thread data object
3683  * @param data IN "RES" ["ALL"]|[["STATUS"] ["HEADERS"] ["BODY"]]
3684  *
3685  * @return APR_SUCCESS or apr error code
3686  */
command_RECORD(command_t * self,worker_t * worker,char * data,apr_pool_t * ptmp)3687 apr_status_t command_RECORD(command_t *self, worker_t *worker, char *data,
3688                             apr_pool_t *ptmp) {
3689   char *copy;
3690   recorder_t *recorder = worker_get_recorder(worker);
3691   COMMAND_NEED_ARG("RES [ALL] {STATUS|HEADERS|BODY}*");
3692 
3693   if (strncmp(copy, "RES", 3) != 0) {
3694     worker_log(worker, LOG_ERR, "Only response recording supported yet");
3695     return APR_EINVAL;
3696   }
3697 
3698   if (strstr(copy, "ALL")) {
3699     recorder->flags = RECORDER_RECORD_ALL;
3700   }
3701   if (strstr(copy, "STATUS")) {
3702     recorder->flags |= RECORDER_RECORD_STATUS;
3703   }
3704   if (strstr(copy, "HEADERS")) {
3705     recorder->flags |= RECORDER_RECORD_HEADERS;
3706   }
3707   if (strstr(copy, "BODY")) {
3708     recorder->flags |= RECORDER_RECORD_BODY;
3709   }
3710 
3711   if (recorder->on) {
3712     /* restart the recorder by dropping the sockreader pool */
3713     sockreader_destroy(&recorder->sockreader);
3714   }
3715 
3716   /* setup a sockreader for recording */
3717   sockreader_new(&recorder->sockreader, NULL, NULL, 0);
3718 
3719   recorder->on = RECORDER_RECORD;
3720 
3721   return APR_SUCCESS;
3722 }
3723 
3724 /**
3725  * PLAY command
3726  *
3727  * @param self IN command
3728  * @param worker IN thread data object
3729  * @param data IN "BACK"|"VAR" <varname>
3730  *
3731  * @return APR_SUCCESS or apr error code
3732  */
command_PLAY(command_t * self,worker_t * worker,char * data,apr_pool_t * ptmp)3733 apr_status_t command_PLAY(command_t *self, worker_t *worker, char *data,
3734                           apr_pool_t *ptmp) {
3735   recorder_t *recorder = worker_get_recorder(worker);
3736   COMMAND_NO_ARG;
3737   /* if recorded data available do play back */
3738   if (recorder->on == RECORDER_RECORD) {
3739     recorder->on = RECORDER_PLAY;
3740   }
3741   else {
3742     worker_log(worker, LOG_ERR, "Can not play cause recorder is not in recording mode");
3743     return APR_EINVAL;
3744   }
3745   return APR_SUCCESS;
3746 }
3747 
3748 /**
3749  * LOCAL command
3750  *
3751  * @param self IN command
3752  * @param worker IN thread data object
3753  * @param data IN <var> (" " <var>)*
3754  *
3755  * @return APR_SUCCESS or apr error code
3756  */
command_LOCAL(command_t * self,worker_t * worker,char * data,apr_pool_t * ptmp)3757 apr_status_t command_LOCAL(command_t *self, worker_t *worker, char *data,
3758                            apr_pool_t *ptmp) {
3759   char *copy;
3760   char *last;
3761   char *var;
3762   COMMAND_NEED_ARG("<var> (\" \" <var>)*");
3763 
3764   var = apr_strtok(copy, " ", &last);
3765   while (var) {
3766     store_set(worker->locals, var, "");
3767     var = apr_strtok(NULL, " ", &last);
3768   }
3769 
3770   return APR_SUCCESS;
3771 }
3772 
3773 /**
3774  * USE command
3775  *
3776  * @param self IN command
3777  * @param worker IN thread data object
3778  * @param data IN <module>
3779  *
3780  * @return APR_SUCCESS or apr error code
3781  */
command_USE(command_t * self,worker_t * worker,char * data,apr_pool_t * ptmp)3782 apr_status_t command_USE(command_t *self, worker_t *worker, char *data,
3783                          apr_pool_t *ptmp) {
3784   char *copy;
3785   COMMAND_NEED_ARG("<module>");
3786 
3787   if (!(worker->blocks = apr_hash_get(worker->modules, copy, APR_HASH_KEY_STRING))) {
3788     worker_log(worker, LOG_ERR, "Could not finde module \"%s\"", copy);
3789     return APR_EINVAL;
3790   }
3791 
3792   return APR_SUCCESS;
3793 }
3794 
3795 /**
3796  * IGNORE_BODY command
3797  *
3798  * @param self IN command
3799  * @param worker IN thread data object
3800  * @param data IN unused
3801  *
3802  * @return APR_SUCCESS or apr error code
3803  */
command_IGNORE_BODY(command_t * self,worker_t * worker,char * data,apr_pool_t * ptmp)3804 apr_status_t command_IGNORE_BODY(command_t *self, worker_t *worker, char *data,
3805                                  apr_pool_t *ptmp) {
3806   char *copy;
3807   COMMAND_NEED_ARG("on|off, default off");
3808 
3809   apr_collapse_spaces(copy, copy);
3810   if (strcasecmp(copy, "on") == 0) {
3811     worker->flags |= FLAGS_IGNORE_BODY;
3812   }
3813   else if (strcasecmp(copy, "off") == 0) {
3814     worker->flags &= ~FLAGS_IGNORE_BODY;
3815   }
3816   else {
3817     worker_log(worker, LOG_ERR, "Do not understand \"%s\"", copy);
3818     return APR_EINVAL;
3819   }
3820   return APR_SUCCESS;
3821 }
3822 
3823 /**
3824  * VERSION command
3825  *
3826  * @param self IN unused
3827  * @param worker IN thread data object
3828  * @param data IN variable to store in
3829  *
3830  * @return APR_SUCCESS
3831  */
command_VERSION(command_t * self,worker_t * worker,char * data,apr_pool_t * ptmp)3832 apr_status_t command_VERSION(command_t *self, worker_t *worker, char *data,
3833                              apr_pool_t *ptmp) {
3834   char *copy;
3835   COMMAND_NEED_ARG("<variable>");
3836   worker_var_set(worker, copy, PACKAGE_VERSION);
3837   return APR_SUCCESS;
3838 }
3839 
3840 /**
3841  * DUMMY command used for opsolete commands
3842  *
3843  * @param self IN unused
3844  * @param worker IN unused
3845  * @param data IN unused
3846  *
3847  * @return APR_SUCCESS
3848  */
command_DUMMY(command_t * self,worker_t * worker,char * data,apr_pool_t * ptmp)3849 apr_status_t command_DUMMY(command_t *self, worker_t *worker, char *data,
3850                            apr_pool_t *ptmp) {
3851   return APR_SUCCESS;
3852 }
3853 
3854 /**
3855  * Object thread data
3856  */
3857 
3858 /**
3859  * New thread data object
3860  *
3861  * @param self OUT thread data object
3862  * @param log_mode IN log mode
3863  *
3864  */
worker_new(worker_t ** self,char * additional,global_t * global,interpret_f function)3865 void worker_new(worker_t ** self, char *additional, global_t *global,
3866                 interpret_f function) {
3867   if (global->mutex) apr_thread_mutex_lock(global->mutex);
3868   {
3869     apr_pool_t *p;
3870     HT_POOL_CREATE(&p);
3871     (*self) = apr_pcalloc(p, sizeof(worker_t));
3872     (*self)->global = global;
3873     (*self)->heartbeat = p;
3874     apr_pool_create(&p, (*self)->heartbeat);
3875     (*self)->pbody = p;
3876     apr_pool_create(&p, (*self)->heartbeat);
3877     (*self)->pcache = p;
3878     /* this stuff muss last until END so take pbody pool for this */
3879     p = (*self)->pbody;
3880     (*self)->interpret = function;
3881     (*self)->config = apr_hash_make(p);
3882     (*self)->filename = apr_pstrdup(p, "<none>");
3883     (*self)->socktmo = global->socktmo;
3884     (*self)->additional = apr_pstrdup(p, additional);
3885     (*self)->sync_mutex = global->sync_mutex;
3886     (*self)->mutex = global->mutex;
3887     (*self)->lines = apr_table_make(p, 20);
3888     (*self)->cache = apr_table_make((*self)->pcache, 20);
3889     (*self)->expect.dot = apr_table_make(p, 2);
3890     (*self)->expect.headers = apr_table_make(p, 2);
3891     (*self)->expect.body = apr_table_make(p, 2);
3892     (*self)->expect.exec= apr_table_make(p, 2);
3893     (*self)->expect.error = apr_table_make(p, 2);
3894     (*self)->match.dot= apr_table_make(p, 2);
3895     (*self)->match.headers = apr_table_make(p, 2);
3896     (*self)->match.body = apr_table_make(p, 2);
3897     (*self)->match.error = apr_table_make(p, 2);
3898     (*self)->match.exec = apr_table_make(p, 2);
3899     (*self)->grep.dot= apr_table_make(p, 2);
3900     (*self)->grep.headers = apr_table_make(p, 2);
3901     (*self)->grep.body = apr_table_make(p, 2);
3902     (*self)->grep.error = apr_table_make(p, 2);
3903     (*self)->grep.exec = apr_table_make(p, 2);
3904     (*self)->sockets = apr_hash_make(p);
3905     (*self)->headers_allow = NULL;
3906     (*self)->headers_filter = NULL;
3907     (*self)->params = store_make(p);
3908     (*self)->retvars = store_make(p);
3909     (*self)->locals = store_make(p);
3910     (*self)->vars = store_copy(global->vars, p);
3911     (*self)->modules = apr_hash_copy(p, global->modules);
3912     (*self)->blocks = global->blocks;
3913     (*self)->logger = global->logger;
3914     (*self)->flags = global->flags;
3915     (*self)->listener_addr = apr_pstrdup(p, APR_ANYADDR);
3916 
3917     store_set((*self)->vars, "__LOG_LEVEL", apr_itoa((*self)->pbody,
3918   		logger_get_mode(global->logger)));
3919 
3920     worker_log((*self), LOG_DEBUG,
3921   			 "worker_new: pool: %"APR_UINT64_T_HEX_FMT", pbody: %"APR_UINT64_T_HEX_FMT,
3922   			 (*self)->pbody, (*self)->pbody);
3923   }
3924   if (global->mutex) apr_thread_mutex_unlock(global->mutex);
3925 }
3926 
3927 /**
3928  * Clone thread data object
3929  *
3930  * @param self OUT thread data object
3931  * @param orig IN thread data object to copy from
3932  *
3933  * @return an apr status
3934  */
worker_clone(worker_t ** self,worker_t * orig)3935 void worker_clone(worker_t ** self, worker_t * orig) {
3936   global_t *global = orig->global;
3937 
3938   worker_new(self, orig->additional, global, orig->interpret);
3939 
3940   if (global->mutex) apr_thread_mutex_lock(global->mutex);
3941   {
3942     apr_pool_t *p;
3943     p = (*self)->pbody;
3944     (*self)->flags = orig->flags;
3945     (*self)->lines = my_table_deep_copy(p, orig->lines);
3946     (*self)->listener = NULL;
3947     (*self)->vars = store_copy(orig->vars, p);
3948     (*self)->listener_addr = apr_pstrdup(p, orig->listener_addr);
3949     (*self)->group = orig->group;
3950 
3951     worker_log((*self), LOG_DEBUG,
3952                "worker_clone: pool: %"APR_UINT64_T_HEX_FMT", pbody: %"APR_UINT64_T_HEX_FMT,
3953                (*self)->pbody, (*self)->pbody);
3954   }
3955   if (global->mutex) apr_thread_mutex_unlock(global->mutex);
3956 }
3957 
3958 /**
3959  * Destroy thread data object
3960  *
3961  * @param worker IN thread data object
3962  */
worker_destroy(worker_t * worker)3963 void worker_destroy(worker_t * worker) {
3964   worker_log(worker, LOG_DEBUG,
3965              "worker_destroy: %"APR_UINT64_T_HEX_FMT", pbody: %"APR_UINT64_T_HEX_FMT,
3966              worker->pbody, worker->pbody);
3967   apr_pool_destroy(worker->heartbeat);
3968 }
3969 
3970 /**
3971  * Clone thread data object
3972  *
3973  * @param worker IN thread data object
3974  * @param line IN command line
3975  *
3976  * @return an apr status
3977  */
worker_add_line(worker_t * worker,const char * file_and_line,char * line)3978 apr_status_t worker_add_line(worker_t * worker, const char *file_and_line,
3979                              char *line) {
3980   apr_table_addn(worker->lines, file_and_line, line);
3981   return APR_SUCCESS;
3982 }
3983 
3984 /**
3985  * Send buf with len
3986  *
3987  * @param self IN thread data object
3988  * @param buf IN buffer to send
3989  * @param len IN no bytes of buffer to send
3990  *
3991  * @return apr status
3992  */
worker_socket_send(worker_t * worker,char * buf,apr_size_t len)3993 apr_status_t worker_socket_send(worker_t *worker, char *buf,
3994                                 apr_size_t len) {
3995 
3996   worker_log(worker, LOG_DEBUG,
3997              "send socket: %"APR_UINT64_T_HEX_FMT" transport: %"APR_UINT64_T_HEX_FMT,
3998              worker->socket, worker->socket->transport);
3999   return transport_write(worker->socket->transport, buf, len);
4000 }
4001 
4002 /**
4003  * Hop over headers till empty line
4004  *
4005  * @param worker IN worker object
4006  * @param start IN start index
4007  *
4008  * @return current index
4009  */
worker_hop_over_headers(worker_t * worker,int start)4010 static int worker_hop_over_headers(worker_t *worker, int start) {
4011   int i = start;
4012   apr_table_entry_t *e =
4013     (apr_table_entry_t *) apr_table_elts(worker->cache)->elts;
4014   while (i < apr_table_elts(worker->cache)->nelts && e[i].val[0]) {
4015     ++i;
4016   }
4017   ++i;
4018   return i;
4019 }
4020 
4021 /**
4022  * get the length of the cached line
4023  *
4024  * @param worker IN worker object
4025  * @param cached_line IN a table entry of cache
4026  * @param len OUT length of cached_line
4027  *
4028  * @return length of this table entry
4029  */
worker_get_line_length(worker_t * worker,apr_table_entry_t cached_line,apr_size_t * len)4030 apr_status_t worker_get_line_length(worker_t *worker,
4031                                     apr_table_entry_t cached_line,
4032                                     apr_size_t *len) {
4033   line_t line;
4034 
4035   apr_status_t status = APR_SUCCESS;
4036   line.info = cached_line.key;
4037   line.buf = cached_line.val;
4038   *len = 0;
4039 
4040   /* if there are modules which do have their own format */
4041   if ((status = htt_run_line_get_length(worker, &line)) != APR_SUCCESS) {
4042     return status;
4043   }
4044 
4045   /* do not forget the \r\n */
4046   if (strncasecmp(line.info, "NOCRLF", 6) != 0) {
4047     *len += 2;
4048   }
4049   if (strncasecmp(line.info, "NOCRLF:", 7) == 0) {
4050     *len += apr_atoi64(&line.info[7]);
4051   }
4052   else {
4053     *len += strlen(line.buf);
4054   }
4055 
4056   return status;
4057 }
4058 
4059 /**
4060  * flush partial data
4061  *
4062  * @param worker IN worker object
4063  * @param from IN start cache line
4064  * @param to IN end cache line
4065  * @param ptmp IN temporary pool
4066  *
4067  * @return an apr status
4068  */
worker_flush_part(worker_t * worker,int from,int to,apr_pool_t * ptmp)4069 apr_status_t worker_flush_part(worker_t *worker, int from, int to,
4070                                apr_pool_t *ptmp) {
4071   int i;
4072   int len;
4073   int nocrlf = 0;
4074 
4075   apr_status_t status = APR_SUCCESS;
4076 
4077   apr_table_entry_t *e =
4078     (apr_table_entry_t *) apr_table_elts(worker->cache)->elts;
4079 
4080   /* iterate through all cached lines and send them */
4081   for (i = from; i < to; ++i) {
4082     line_t line;
4083     line.info = e[i].key;
4084     line.buf = e[i].val;
4085     /* use in this case the copied key */
4086     if (strstr(line.info, "resolve")) {
4087       int unresolved;
4088       /* do only local var resolve the only var pool which could have new vars
4089        * with values
4090        */
4091       /* replace all vars */
4092       line.buf = worker_replace_vars(worker, line.buf, &unresolved, ptmp);
4093     }
4094     if((status = htt_run_line_flush(worker, &line)) != APR_SUCCESS) {
4095       return status;
4096     }
4097     if (strncasecmp(line.info, "NOCRLF:", 7) == 0) {
4098       line.len = apr_atoi64(&line.info[7]);
4099       if (nocrlf) {
4100 	worker_log_buf(worker, LOG_INFO, '+', line.buf, line.len);
4101       }
4102       else {
4103 	worker_log_buf(worker, LOG_INFO, '>', line.buf, line.len);
4104       }
4105       nocrlf = 1;
4106     }
4107     else if (strcasecmp(line.info, "NOCRLF") == 0) {
4108       line.len = strlen(line.buf);
4109       if (nocrlf) {
4110 	worker_log_buf(worker, LOG_INFO, '+', line.buf, line.len);
4111       }
4112       else {
4113 	worker_log_buf(worker, LOG_INFO, '>', line.buf, line.len);
4114       }
4115       nocrlf = 1;
4116     }
4117     else {
4118       line.len = strlen(line.buf);
4119       if (nocrlf) {
4120 	worker_log_buf(worker, LOG_INFO, '+', line.buf, line.len);
4121       }
4122       else {
4123 	worker_log_buf(worker, LOG_INFO, '>', line.buf, line.len);
4124       }
4125       nocrlf = 0;
4126     }
4127 
4128     if ((status = worker_socket_send(worker, line.buf, line.len))
4129 	!= APR_SUCCESS) {
4130       goto error;
4131     }
4132     if((status = htt_run_line_sent(worker, &line)) != APR_SUCCESS) {
4133       return status;
4134     }
4135     worker->sent += line.len;
4136     if (strncasecmp(line.info, "NOCRLF", 6) != 0) {
4137       len = 2;
4138       if ((status = worker_socket_send(worker, "\r\n", len)) != APR_SUCCESS) {
4139 	goto error;
4140       }
4141       worker->sent += len;
4142     }
4143   }
4144 
4145 error:
4146   return status;
4147 }
4148 
4149 /**
4150  * Flush a chunk part
4151  *
4152  * @param worker IN worker object
4153  * @param chunked IN chunk info to flush before data
4154  * @param from IN start cache line
4155  * @param to IN end cache line
4156  * @param ptmp IN temporary pool
4157  *
4158  * @param apr status
4159  */
worker_flush_chunk(worker_t * worker,char * chunked,int from,int to,apr_pool_t * ptmp)4160 apr_status_t worker_flush_chunk(worker_t *worker, char *chunked, int from, int to,
4161                                 apr_pool_t *ptmp) {
4162   apr_status_t status;
4163   int len;
4164 
4165   if (chunked) {
4166     worker_log_buf(worker, LOG_INFO, '>', chunked, strlen(chunked));
4167   }
4168 
4169   if (chunked) {
4170     len = strlen(chunked);
4171     if ((status = worker_socket_send(worker, chunked, len)) != APR_SUCCESS) {
4172       return status;
4173     }
4174     worker->sent += len;
4175   }
4176 
4177   return worker_flush_part(worker, from, to, ptmp);
4178 }
4179 
4180 /**
4181  * Calculate content length
4182  *
4183  * @param worker IN worker object
4184  * @param start IN start index
4185  * @param len OUT content length
4186  *
4187  * @return apr status
4188  */
worker_get_content_length(worker_t * worker,int start,apr_size_t * len)4189 static apr_status_t worker_get_content_length(worker_t *worker, int start,
4190                                               apr_size_t *len) {
4191   apr_status_t status = APR_SUCCESS;
4192   int i = start;
4193   apr_table_entry_t *e =
4194     (apr_table_entry_t *) apr_table_elts(worker->cache)->elts;
4195 
4196   *len = 0;
4197   for (; i < apr_table_elts(worker->cache)->nelts; ++i) {
4198     apr_size_t tmp_len;
4199     if ((status = worker_get_line_length(worker, e[i], &tmp_len))
4200 	!= APR_SUCCESS) {
4201       return status;
4202     }
4203     *len += tmp_len;
4204   }
4205   return status;
4206 }
4207 
4208 /**
4209  * Do automatic 100 continue
4210  *
4211  * @param worker IN worker object
4212  * @param body_start IN index of body start
4213  * @param ptmp IN temporary pool
4214  *
4215  * @return apr status
4216  */
worker_do_auto_100_continue(worker_t * worker,int body_start,apr_pool_t * ptmp)4217 static apr_status_t worker_do_auto_100_continue(worker_t *worker,
4218                                                 int body_start,
4219                                                 apr_pool_t *ptmp) {
4220   apr_status_t status = APR_SUCCESS;
4221 
4222   /* flush headers and empty line but not body */
4223   if ((status = worker_flush_part(worker, 0, body_start, ptmp))
4224       != APR_SUCCESS) {
4225     return status;
4226   }
4227   /* wait for a 100 continue response */
4228   if ((status = command_EXPECT(NULL, worker, "headers \"HTTP/1.1 100 Continue\"", ptmp))
4229       != APR_SUCCESS) {
4230     return status;
4231   }
4232   /* do skip call flush in command _WAIT */
4233   worker->flags |= FLAGS_SKIP_FLUSH;
4234   if ((status = command_WAIT(NULL, worker, "", ptmp)) != APR_SUCCESS) {
4235     return status;
4236   }
4237   /* do not skip flush */
4238   worker->flags &= ~FLAGS_SKIP_FLUSH;
4239   /* send body then */
4240   if ((status = worker_flush_part(worker, body_start,
4241 				  apr_table_elts(worker->cache)->nelts, ptmp))
4242       != APR_SUCCESS) {
4243     return status;
4244   }
4245   return status;
4246 }
4247 
4248 /**
4249  * flush data
4250  *
4251  * @param self IN thread data object
4252  * @param ptmp IN temporary pool
4253  *
4254  * @return an apr status
4255  */
worker_flush(worker_t * self,apr_pool_t * ptmp)4256 apr_status_t worker_flush(worker_t * self, apr_pool_t *ptmp) {
4257   apr_size_t len;
4258   const char *hdr;
4259 
4260   int i = 0;
4261   int body_start = 0;
4262   int icap_body = 0;
4263   int icap_body_start = 0;
4264   int start = 0;
4265   char *chunked = NULL;
4266   int ct_len = 0;
4267 
4268   apr_status_t status = APR_SUCCESS;
4269   apr_table_entry_t *e =
4270     (apr_table_entry_t *) apr_table_elts(self->cache)->elts;
4271 
4272   /* test if we should skip it */
4273   if (self->flags & FLAGS_SKIP_FLUSH) {
4274     return APR_SUCCESS;
4275   }
4276 
4277   if (!self->socket) {
4278     goto error;
4279   }
4280 
4281   /* hop over icap headers if there are any */
4282   if (apr_table_get(self->cache, "Content-Length") &&
4283       (hdr = apr_table_get(self->cache, "Encapsulated"))) {
4284     char *nv;
4285     char *last;
4286     char *copy = apr_pstrdup(self->pbody, hdr);
4287 
4288     /* start counting till last body of ICAP message */
4289     i = 0;
4290     apr_strtok(copy, ":", &last);
4291     nv = apr_strtok(NULL, ",", &last);
4292     while (nv) {
4293       i = worker_hop_over_headers(self, i);
4294       nv = apr_strtok(NULL, ",", &last);
4295     }
4296     start = 1;
4297   }
4298 
4299   /* callculate body if Content-Length: AUTO */
4300   if (apr_table_get(self->cache, "Content-Length")) {
4301     if (!start) {
4302       i = worker_hop_over_headers(self, i);
4303     }
4304     body_start = i;
4305 
4306     if ((status = worker_get_content_length(self, i, &len)) != APR_SUCCESS) {
4307       return status;
4308     }
4309 
4310     apr_table_set(self->cache, "Content-Length",
4311                   apr_psprintf(self->pbody, "Content-Length: %"APR_SIZE_T_FMT, len));
4312 
4313     ct_len = len;
4314   }
4315 
4316   /* callculate headers and optional body of ICAP message */
4317   if ((hdr = apr_table_get(self->cache, "Encapsulated"))) {
4318     char *nv;
4319     char *last;
4320     char *res = NULL;
4321     char *copy = apr_pstrdup(self->pbody, hdr);
4322 
4323     /* restart counting */
4324     i = 0;
4325     len = 0;
4326     apr_strtok(copy, ":", &last);
4327     nv = apr_strtok(NULL, ",", &last);
4328     while (nv) {
4329       char *var;
4330       char *val;
4331 
4332       var = apr_strtok(nv, "=", &val);
4333       apr_collapse_spaces(var, var);
4334       apr_collapse_spaces(val, val);
4335       if (strstr(var, "body")) {
4336 	icap_body = 1;
4337       }
4338       if (val && strncmp(val, "AUTO", 4) == 0) {
4339         while (i < apr_table_elts(self->cache)->nelts && e[i].val[0]) {
4340 	  apr_size_t tmp_len;
4341 	  if ((status = worker_get_line_length(self, e[i], &tmp_len))
4342 	      != APR_SUCCESS) {
4343 	    return status;
4344 	  }
4345 	  len += tmp_len;
4346 	  ++i;
4347 	}
4348 	/* count also the empty line */
4349 	len += 2;
4350 	val = apr_itoa(self->pbody, len);
4351 	++i;
4352       }
4353       else {
4354 	i = worker_hop_over_headers(self, i);
4355       }
4356 
4357       if (!res) {
4358 	res = apr_pstrcat(self->pbody, var, "=", val, NULL);
4359       }
4360       else {
4361 	res = apr_pstrcat(self->pbody, res, ", ", var, "=", val, NULL);
4362       }
4363       nv = apr_strtok(NULL, ",", &last);
4364     }
4365     apr_table_setn(self->cache, "Encapsulated",
4366                    apr_psprintf(self->pbody, "Encapsulated: %s", res));
4367 
4368     /* only chunk body automatic if Content-Length: AUTO */
4369     if (icap_body && apr_table_get(self->cache, "Content-Length")) {
4370       icap_body_start = i;
4371       chunked = apr_psprintf(self->pbody, "%x\r\n", ct_len);
4372     }
4373   }
4374   else if (apr_table_get(self->cache, "Content-Length") &&
4375            apr_table_get(self->cache, "100-Continue")) {
4376     /* do this only if Content-Length and 100-Continue is set */
4377     status = worker_do_auto_100_continue(self, body_start, ptmp);
4378     goto error;
4379   }
4380 
4381   if (apr_table_get(self->cache, "Cookie")) {
4382     if (self->socket->cookie) {
4383       apr_table_set(self->cache, "Cookie", self->socket->cookie);
4384     }
4385     else {
4386       apr_table_unset(self->cache, "Cookie");
4387     }
4388   }
4389 
4390   /* this is one chunk */
4391   if (apr_table_get(self->cache, "CHUNKED")) {
4392     apr_table_unset(self->cache, "CHUNKED");
4393     len = 0;
4394     for (; i < apr_table_elts(self->cache)->nelts; ++i) {
4395       /* do not forget the \r\n */
4396       if (strncasecmp(e[i].key, "NOCRLF", 6) != 0) {
4397 	len += 2;
4398       }
4399       if (strncasecmp(e[i].key, "NOCRLF:", 7) == 0) {
4400 	len += apr_atoi64(&e[i].key[7]);
4401       }
4402       else {
4403 	len += strlen(e[i].val);
4404       }
4405     }
4406     chunked = apr_psprintf(self->pbody, "\r\n%x\r\n", (unsigned int)len);
4407   }
4408   if (icap_body) {
4409     /* send all except the req/res body */
4410     if ((status = worker_flush_part(self, 0, icap_body_start, ptmp))
4411 	!= APR_SUCCESS) {
4412       goto error;
4413     }
4414     if ((status = worker_flush_chunk(self, chunked, icap_body_start,
4415 	                             apr_table_elts(self->cache)->nelts, ptmp))
4416 	!= APR_SUCCESS) {
4417       goto error;
4418     }
4419     if (chunked) {
4420       chunked = apr_psprintf(self->pbody, "\r\n0\r\n\r\n");
4421       status = worker_flush_chunk(self, chunked, 0, 0, ptmp);
4422     }
4423   }
4424   else {
4425     status = worker_flush_chunk(self, chunked, 0,
4426 	                        apr_table_elts(self->cache)->nelts, ptmp);
4427   }
4428 
4429 error:
4430   apr_pool_clear(self->pcache);
4431   self->cache = apr_table_make(self->pcache, 20);
4432 
4433   return status;
4434 }
4435 
4436 /**
4437  * write worker data to file with worker->name
4438  *
4439  * @param worker IN thread data object
4440  *
4441  * @return an apr status
4442  */
worker_to_file(worker_t * worker)4443 apr_status_t worker_to_file(worker_t * worker) {
4444   apr_status_t status;
4445   apr_file_t *fp;
4446   apr_table_entry_t *e;
4447   char *line;
4448   char *copy;
4449   int i;
4450 
4451   if ((status =
4452        apr_file_open(&fp, worker->name, APR_CREATE | APR_WRITE, APR_OS_DEFAULT,
4453 		     worker->pbody)) != APR_SUCCESS) {
4454     return status;
4455   }
4456 
4457   e = (apr_table_entry_t *) apr_table_elts(worker->lines)->elts;
4458   for (i = 0; i < apr_table_elts(worker->lines)->nelts; i++) {
4459     line = e[i].val;
4460     copy = worker_replace_vars(worker, line, NULL, worker->pbody);
4461     apr_file_printf(fp, "%s\n", &copy[1]);
4462   }
4463 
4464   apr_file_close(fp);
4465 
4466   return APR_SUCCESS;
4467 }
4468 
4469 /**
4470  * Register transport to socket
4471  *
4472  * @param socket IN htt socket
4473  * @param transport IN transport object for read/write
4474  * @return APR_SUCCESS
4475  */
transport_register(socket_t * socket,transport_t * transport)4476 apr_status_t transport_register(socket_t *socket, transport_t *transport) {
4477   if (socket) {
4478     socket->transport = transport;
4479   }
4480   return APR_SUCCESS;
4481 }
4482 
4483 /**
4484  * Unregister transport from socket
4485  *
4486  * @param socket IN htt socket
4487  * @return APR_SUCCESS
4488  */
transport_unregister(socket_t * socket,transport_t * transport)4489 apr_status_t transport_unregister(socket_t *socket, transport_t *transport) {
4490   if (socket) {
4491     socket->transport = NULL;
4492   }
4493   return APR_SUCCESS;
4494 }
4495 
4496 /**
4497  * Get current transport from socket
4498  *
4499  * @param socket IN htt socket
4500  * @return transport
4501  */
transport_get_current(socket_t * socket)4502 transport_t *transport_get_current(socket_t *socket) {
4503   return socket->transport;
4504 }
4505 
4506 /**
4507  * builtin finally for cleanup stuff
4508  * @param worker IN thread object
4509  */
worker_finally_cleanup(worker_t * worker)4510 void worker_finally_cleanup(worker_t *worker) {
4511   sh_t *sh = module_get_config(worker->config, SH_CONFIG);
4512   if (sh && sh->tmpf) {
4513     const char *name;
4514 
4515     if (apr_file_name_get(&name, sh->tmpf) == APR_SUCCESS) {
4516       apr_file_close(sh->tmpf);
4517       apr_file_remove(name, sh->pool);
4518       module_set_config(worker->config, apr_pstrdup(sh->pool, SH_CONFIG), NULL);
4519       apr_pool_destroy(sh->pool);
4520     }
4521   }
4522 
4523 }
4524 
4525 APR_HOOK_STRUCT(
4526   APR_HOOK_LINK(line_get_length)
4527   APR_HOOK_LINK(line_flush)
4528   APR_HOOK_LINK(line_sent)
4529   APR_HOOK_LINK(client_port_args)
4530   APR_HOOK_LINK(pre_connect)
4531   APR_HOOK_LINK(connect)
4532   APR_HOOK_LINK(post_connect)
4533   APR_HOOK_LINK(accept)
4534   APR_HOOK_LINK(pre_close)
4535   APR_HOOK_LINK(close)
4536   APR_HOOK_LINK(WAIT_begin)
4537   APR_HOOK_LINK(read_pre_headers)
4538   APR_HOOK_LINK(read_status_line)
4539   APR_HOOK_LINK(read_header)
4540   APR_HOOK_LINK(read_buf)
4541   APR_HOOK_LINK(WAIT_end)
4542 )
4543 
4544 
4545 APR_IMPLEMENT_EXTERNAL_HOOK_RUN_FIRST(htt, HTT, apr_status_t, line_get_length,
4546                                       (worker_t *worker, line_t *line),
4547 				      (worker, line), APR_SUCCESS)
4548 
4549 APR_IMPLEMENT_EXTERNAL_HOOK_RUN_FIRST(htt, HTT, apr_status_t, line_flush,
4550                                       (worker_t *worker, line_t *line),
4551 				      (worker, line), APR_SUCCESS)
4552 
4553 APR_IMPLEMENT_EXTERNAL_HOOK_RUN_FIRST(htt, HTT, apr_status_t, line_sent,
4554                                       (worker_t *worker, line_t *line),
4555 				      (worker, line), APR_SUCCESS)
4556 
4557 APR_IMPLEMENT_EXTERNAL_HOOK_RUN_FIRST(htt, HTT, apr_status_t, client_port_args,
4558                                       (worker_t *worker, char *portinfo,
4559 				       char **new_portinfo, char *rest_of_line),
4560 				      (worker, portinfo, new_portinfo, rest_of_line), APR_SUCCESS)
4561 
4562 APR_IMPLEMENT_EXTERNAL_HOOK_RUN_FIRST(htt, HTT, apr_status_t, pre_connect,
4563                                       (worker_t *worker),
4564 				      (worker), APR_SUCCESS)
4565 
4566 APR_IMPLEMENT_EXTERNAL_HOOK_RUN_FIRST(htt, HTT, apr_status_t, connect,
4567                                       (worker_t *worker),
4568 				      (worker), APR_SUCCESS)
4569 
4570 APR_IMPLEMENT_EXTERNAL_HOOK_RUN_FIRST(htt, HTT, apr_status_t, post_connect,
4571                                       (worker_t *worker),
4572 				      (worker), APR_SUCCESS)
4573 
4574 APR_IMPLEMENT_EXTERNAL_HOOK_RUN_FIRST(htt, HTT, apr_status_t, accept,
4575                                       (worker_t *worker, char *rest_of_line),
4576 				      (worker, rest_of_line), APR_SUCCESS)
4577 
4578 APR_IMPLEMENT_EXTERNAL_HOOK_RUN_FIRST(htt, HTT, apr_status_t, pre_close,
4579                                       (worker_t *worker),
4580 				      (worker), APR_SUCCESS)
4581 
4582 APR_IMPLEMENT_EXTERNAL_HOOK_RUN_FIRST(htt, HTT, apr_status_t, close,
4583                                       (worker_t *worker, char *info, char **new_info),
4584 				      (worker, info, new_info), APR_SUCCESS)
4585 
4586 APR_IMPLEMENT_EXTERNAL_HOOK_RUN_FIRST(htt, HTT, apr_status_t, WAIT_begin,
4587                                       (worker_t *worker),
4588 				      (worker), APR_SUCCESS)
4589 
4590 APR_IMPLEMENT_EXTERNAL_HOOK_RUN_FIRST(htt, HTT, apr_status_t, read_pre_headers,
4591                                       (worker_t *worker),
4592 				      (worker), APR_SUCCESS)
4593 
4594 APR_IMPLEMENT_EXTERNAL_HOOK_RUN_FIRST(htt, HTT, apr_status_t, read_status_line,
4595                                       (worker_t *worker, char *line),
4596 				      (worker, line), APR_SUCCESS)
4597 
4598 APR_IMPLEMENT_EXTERNAL_HOOK_RUN_FIRST(htt, HTT, apr_status_t, read_header,
4599                                       (worker_t *worker, char *line),
4600 				      (worker, line), APR_SUCCESS)
4601 
4602 APR_IMPLEMENT_EXTERNAL_HOOK_RUN_FIRST(htt, HTT, apr_status_t, read_buf,
4603                                       (worker_t *worker, char *buf, apr_size_t len),
4604 				      (worker, buf, len), APR_SUCCESS)
4605 
4606 APR_IMPLEMENT_EXTERNAL_HOOK_RUN_FIRST(htt, HTT, apr_status_t, WAIT_end,
4607                                       (worker_t *worker, apr_status_t status),
4608 				      (worker, status), APR_SUCCESS)
4609 
4610 
4611