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", ©[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