blob: 58d1940a24eee09f49b49ae42aa4280b5070a5ac [file] [log] [blame]
Dragan Dosen59bb97a2017-06-02 12:03:16 +02001/*
2 * Mod Defender for HAProxy
3 *
4 * Support for the Mod Defender code on non-Apache platforms.
5 *
6 * Copyright 2017 HAProxy Technologies, Dragan Dosen <ddosen@haproxy.com>
7 *
8 * Parts of code based on Apache HTTP Server source
9 * Copyright 2015 The Apache Software Foundation (http://www.apache.org/)
10 *
11 * This program is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU General Public License
13 * as published by the Free Software Foundation; either version
14 * 3 of the License, or (at your option) any later version.
15 *
16 */
17#include <limits.h>
18
19#include <http_core.h>
20#include <http_main.h>
21#include <http_log.h>
22
23#include <apr_lib.h>
24#include <apr_strings.h>
25#include <apr_fnmatch.h>
26
27#include "standalone.h"
28
29#define MAX_ARGC 64
30#define MAX_INCLUDE_DIR_DEPTH 128
31
32#define SLASHES "/"
33
34#define FILTER_POOL apr_hook_global_pool
35#define TRIE_INITIAL_SIZE 4
36
37typedef struct filter_trie_node filter_trie_node;
38
39typedef struct {
40 int c;
41 filter_trie_node *child;
42} filter_trie_child_ptr;
43
44struct filter_trie_node {
45 ap_filter_rec_t *frec;
46 filter_trie_child_ptr *children;
47 int nchildren;
48 int size;
49};
50
51typedef struct {
52 const char *fname;
53} fnames;
54
55AP_DECLARE_DATA const char *ap_server_root = "/";
56
57void (*logger)(int level, char *str) = NULL;
58
59static void str_tolower(char *str)
60{
61 while (*str) {
62 *str = apr_tolower(*str);
63 ++str;
64 }
65}
66
67static char x2c(const char *what)
68{
69 char digit;
70
71#if !APR_CHARSET_EBCDIC
72 digit = ((what[0] >= 'A') ? ((what[0] & 0xdf) - 'A') + 10
73 : (what[0] - '0'));
74 digit *= 16;
75 digit += (what[1] >= 'A' ? ((what[1] & 0xdf) - 'A') + 10
76 : (what[1] - '0'));
77#else /*APR_CHARSET_EBCDIC*/
78 char xstr[5];
79 xstr[0]='0';
80 xstr[1]='x';
81 xstr[2]=what[0];
82 xstr[3]=what[1];
83 xstr[4]='\0';
84 digit = apr_xlate_conv_byte(ap_hdrs_from_ascii,
85 0xFF & strtol(xstr, NULL, 16));
86#endif /*APR_CHARSET_EBCDIC*/
87 return (digit);
88}
89
90static int unescape_url(char *url, const char *forbid, const char *reserved)
91{
92 int badesc, badpath;
93 char *x, *y;
94
95 badesc = 0;
96 badpath = 0;
97 /* Initial scan for first '%'. Don't bother writing values before
98 * seeing a '%' */
99 y = strchr(url, '%');
100 if (y == NULL) {
101 return OK;
102 }
103 for (x = y; *y; ++x, ++y) {
104 if (*y != '%') {
105 *x = *y;
106 }
107 else {
108 if (!apr_isxdigit(*(y + 1)) || !apr_isxdigit(*(y + 2))) {
109 badesc = 1;
110 *x = '%';
111 }
112 else {
113 char decoded;
114 decoded = x2c(y + 1);
115 if ((decoded == '\0')
116 || (forbid && ap_strchr_c(forbid, decoded))) {
117 badpath = 1;
118 *x = decoded;
119 y += 2;
120 }
121 else if (reserved && ap_strchr_c(reserved, decoded)) {
122 *x++ = *y++;
123 *x++ = *y++;
124 *x = *y;
125 }
126 else {
127 *x = decoded;
128 y += 2;
129 }
130 }
131 }
132 }
133 *x = '\0';
134 if (badesc) {
135 return HTTP_BAD_REQUEST;
136 }
137 else if (badpath) {
138 return HTTP_NOT_FOUND;
139 }
140 else {
141 return OK;
142 }
143}
144
145AP_DECLARE(int) ap_unescape_url(char *url)
146{
147 /* Traditional */
148 return unescape_url(url, SLASHES, NULL);
149}
150
151AP_DECLARE(void) ap_get_server_revision(ap_version_t *version)
152{
153 version->major = AP_SERVER_MAJORVERSION_NUMBER;
154 version->minor = AP_SERVER_MINORVERSION_NUMBER;
155 version->patch = AP_SERVER_PATCHLEVEL_NUMBER;
156 version->add_string = AP_SERVER_ADD_STRING;
157}
158
159static void log_error_core(const char *file, int line, int module_index,
160 int level,
161 apr_status_t status, const server_rec *s,
162 const conn_rec *c,
163 const request_rec *r, apr_pool_t *pool,
164 const char *fmt, va_list args)
165{
166 char errstr[MAX_STRING_LEN];
167
168 apr_vsnprintf(errstr, MAX_STRING_LEN, fmt, args);
169
170 if (logger != NULL)
171 logger(level, errstr);
172}
173
174AP_DECLARE(void) ap_log_error_(const char *file, int line, int module_index,
175 int level, apr_status_t status,
176 const server_rec *s, const char *fmt, ...)
177{
178 va_list args;
179
180 va_start(args, fmt);
181 log_error_core(file, line, module_index, level, status, s, NULL, NULL,
182 NULL, fmt, args);
183 va_end(args);
184}
185
186AP_DECLARE(void) ap_log_rerror_(const char *file, int line, int module_index,
187 int level, apr_status_t status,
188 const request_rec *r, const char *fmt, ...)
189{
190 va_list args;
191
192 va_start(args, fmt);
193 log_error_core(file, line, module_index, level, status, r->server, NULL, r,
194 NULL, fmt, args);
195 va_end(args);
196}
197
198AP_DECLARE(void) ap_log_cerror_(const char *file, int line, int module_index,
199 int level, apr_status_t status,
200 const conn_rec *c, const char *fmt, ...)
201{
202 va_list args;
203
204 va_start(args, fmt);
205 log_error_core(file, line, module_index, level, status, c->base_server, c,
206 NULL, NULL, fmt, args);
207 va_end(args);
208}
209
210AP_DECLARE(piped_log *) ap_open_piped_log(apr_pool_t *p, const char *program)
211{
212 return NULL;
213}
214
215AP_DECLARE(apr_file_t *) ap_piped_log_write_fd(piped_log *pl)
216{
217 return NULL;
218}
219
220static cmd_parms default_parms =
221{NULL, 0, 0, NULL, -1, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL};
222
223AP_DECLARE(char *) ap_server_root_relative(apr_pool_t *p, const char *file)
224{
225 char *newpath = NULL;
226 apr_status_t rv;
227 rv = apr_filepath_merge(&newpath, ap_server_root, file,
228 APR_FILEPATH_TRUENAME, p);
229 if (newpath && (rv == APR_SUCCESS || APR_STATUS_IS_EPATHWILD(rv)
230 || APR_STATUS_IS_ENOENT(rv)
231 || APR_STATUS_IS_ENOTDIR(rv))) {
232 return newpath;
233 }
234 else {
235 return NULL;
236 }
237}
238
239AP_DECLARE(apr_status_t) ap_get_brigade(ap_filter_t *next,
240 apr_bucket_brigade *bb,
241 ap_input_mode_t mode,
242 apr_read_type_e block,
243 apr_off_t readbytes)
244{
245 if (next) {
246 return next->frec->filter_func.in_func(next, bb, mode, block,
247 readbytes);
248 }
249 return AP_NOBODY_READ;
250}
251
252static void
253argstr_to_table(char *str, apr_table_t *parms)
254{
255 char *key;
256 char *value;
257 char *strtok_state;
258
259 if (str == NULL) {
260 return;
261 }
262
263 key = apr_strtok(str, "&", &strtok_state);
264 while (key) {
265 value = strchr(key, '=');
266 if (value) {
267 *value = '\0'; /* Split the string in two */
268 value++; /* Skip passed the = */
269 }
270 else {
271 value = "1";
272 }
273 ap_unescape_url(key);
274 ap_unescape_url(value);
275 apr_table_set(parms, key, value);
276 key = apr_strtok(NULL, "&", &strtok_state);
277 }
278}
279
280AP_DECLARE(void) ap_args_to_table(request_rec *r, apr_table_t **table)
281{
282 apr_table_t *t = apr_table_make(r->pool, 10);
283 argstr_to_table(apr_pstrdup(r->pool, r->args), t);
284 *table = t;
285}
286
287/* Link a trie node to its parent
288 */
289static void trie_node_link(apr_pool_t *p, filter_trie_node *parent,
290 filter_trie_node *child, int c)
291{
292 int i, j;
293
294 if (parent->nchildren == parent->size) {
295 filter_trie_child_ptr *new;
296 parent->size *= 2;
297 new = (filter_trie_child_ptr *)apr_palloc(p, parent->size *
298 sizeof(filter_trie_child_ptr));
299 memcpy(new, parent->children, parent->nchildren *
300 sizeof(filter_trie_child_ptr));
301 parent->children = new;
302 }
303
304 for (i = 0; i < parent->nchildren; i++) {
305 if (c == parent->children[i].c) {
306 return;
307 }
308 else if (c < parent->children[i].c) {
309 break;
310 }
311 }
312 for (j = parent->nchildren; j > i; j--) {
313 parent->children[j].c = parent->children[j - 1].c;
314 parent->children[j].child = parent->children[j - 1].child;
315 }
316 parent->children[i].c = c;
317 parent->children[i].child = child;
318
319 parent->nchildren++;
320}
321
322/* Allocate a new node for a trie.
323 * If parent is non-NULL, link the new node under the parent node with
324 * key 'c' (or, if an existing child node matches, return that one)
325 */
326static filter_trie_node *trie_node_alloc(apr_pool_t *p,
327 filter_trie_node *parent, char c)
328{
329 filter_trie_node *new_node;
330 if (parent) {
331 int i;
332 for (i = 0; i < parent->nchildren; i++) {
333 if (c == parent->children[i].c) {
334 return parent->children[i].child;
335 }
336 else if (c < parent->children[i].c) {
337 break;
338 }
339 }
340 new_node = (filter_trie_node *)apr_palloc(p, sizeof(filter_trie_node));
341 trie_node_link(p, parent, new_node, c);
342 }
343 else { /* No parent node */
344 new_node = (filter_trie_node *)apr_palloc(p,
345 sizeof(filter_trie_node));
346 }
347
348 new_node->frec = NULL;
349 new_node->nchildren = 0;
350 new_node->size = TRIE_INITIAL_SIZE;
351 new_node->children = (filter_trie_child_ptr *)apr_palloc(p,
352 new_node->size * sizeof(filter_trie_child_ptr));
353 return new_node;
354}
355
356static filter_trie_node *registered_output_filters = NULL;
357static filter_trie_node *registered_input_filters = NULL;
358
359
360static apr_status_t filter_cleanup(void *ctx)
361{
362 registered_output_filters = NULL;
363 registered_input_filters = NULL;
364 return APR_SUCCESS;
365}
366
367static ap_filter_rec_t *register_filter(const char *name,
368 ap_filter_func filter_func,
369 ap_init_filter_func filter_init,
370 ap_filter_type ftype,
371 filter_trie_node **reg_filter_set)
372{
373 ap_filter_rec_t *frec;
374 char *normalized_name;
375 const char *n;
376 filter_trie_node *node;
377
378 if (!*reg_filter_set) {
379 *reg_filter_set = trie_node_alloc(FILTER_POOL, NULL, 0);
380 }
381
382 normalized_name = apr_pstrdup(FILTER_POOL, name);
383 str_tolower(normalized_name);
384
385 node = *reg_filter_set;
386 for (n = normalized_name; *n; n++) {
387 filter_trie_node *child = trie_node_alloc(FILTER_POOL, node, *n);
388 if (apr_isalpha(*n)) {
389 trie_node_link(FILTER_POOL, node, child, apr_toupper(*n));
390 }
391 node = child;
392 }
393 if (node->frec) {
394 frec = node->frec;
395 }
396 else {
397 frec = apr_pcalloc(FILTER_POOL, sizeof(*frec));
398 node->frec = frec;
399 frec->name = normalized_name;
400 }
401 frec->filter_func = filter_func;
402 frec->filter_init_func = filter_init;
403 frec->ftype = ftype;
404
405 apr_pool_cleanup_register(FILTER_POOL, NULL, filter_cleanup,
406 apr_pool_cleanup_null);
407 return frec;
408}
409
410AP_DECLARE(ap_filter_rec_t *) ap_register_input_filter(const char *name,
411 ap_in_filter_func filter_func,
412 ap_init_filter_func filter_init,
413 ap_filter_type ftype)
414{
415 ap_filter_func f;
416 f.in_func = filter_func;
417 return register_filter(name, f, filter_init, ftype,
418 &registered_input_filters);
419}
420
421static ap_filter_t *add_any_filter_handle(ap_filter_rec_t *frec, void *ctx,
422 request_rec *r, conn_rec *c,
423 ap_filter_t **r_filters,
424 ap_filter_t **p_filters,
425 ap_filter_t **c_filters)
426{
427 apr_pool_t *p = frec->ftype < AP_FTYPE_CONNECTION && r ? r->pool : c->pool;
428 ap_filter_t *f = apr_palloc(p, sizeof(*f));
429 ap_filter_t **outf;
430
431 if (frec->ftype < AP_FTYPE_PROTOCOL) {
432 if (r) {
433 outf = r_filters;
434 }
435 else {
436 ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, c, APLOGNO(00080)
437 "a content filter was added without a request: %s", frec->name);
438 return NULL;
439 }
440 }
441 else if (frec->ftype < AP_FTYPE_CONNECTION) {
442 if (r) {
443 outf = p_filters;
444 }
445 else {
446 ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, c, APLOGNO(00081)
447 "a protocol filter was added without a request: %s", frec->name);
448 return NULL;
449 }
450 }
451 else {
452 outf = c_filters;
453 }
454
455 f->frec = frec;
456 f->ctx = ctx;
457 /* f->r must always be NULL for connection filters */
458 f->r = frec->ftype < AP_FTYPE_CONNECTION ? r : NULL;
459 f->c = c;
460 f->next = NULL;
461
462 if (INSERT_BEFORE(f, *outf)) {
463 f->next = *outf;
464
465 if (*outf) {
466 ap_filter_t *first = NULL;
467
468 if (r) {
469 /* If we are adding our first non-connection filter,
470 * Then don't try to find the right location, it is
471 * automatically first.
472 */
473 if (*r_filters != *c_filters) {
474 first = *r_filters;
475 while (first && (first->next != (*outf))) {
476 first = first->next;
477 }
478 }
479 }
480 if (first && first != (*outf)) {
481 first->next = f;
482 }
483 }
484 *outf = f;
485 }
486 else {
487 ap_filter_t *fscan = *outf;
488 while (!INSERT_BEFORE(f, fscan->next))
489 fscan = fscan->next;
490
491 f->next = fscan->next;
492 fscan->next = f;
493 }
494
495 if (frec->ftype < AP_FTYPE_CONNECTION && (*r_filters == *c_filters)) {
496 *r_filters = *p_filters;
497 }
498 return f;
499}
500
501static ap_filter_t *add_any_filter(const char *name, void *ctx,
502 request_rec *r, conn_rec *c,
503 const filter_trie_node *reg_filter_set,
504 ap_filter_t **r_filters,
505 ap_filter_t **p_filters,
506 ap_filter_t **c_filters)
507{
508 if (reg_filter_set) {
509 const char *n;
510 const filter_trie_node *node;
511
512 node = reg_filter_set;
513 for (n = name; *n; n++) {
514 int start, end;
515 start = 0;
516 end = node->nchildren - 1;
517 while (end >= start) {
518 int middle = (end + start) / 2;
519 char ch = node->children[middle].c;
520 if (*n == ch) {
521 node = node->children[middle].child;
522 break;
523 }
524 else if (*n < ch) {
525 end = middle - 1;
526 }
527 else {
528 start = middle + 1;
529 }
530 }
531 if (end < start) {
532 node = NULL;
533 break;
534 }
535 }
536
537 if (node && node->frec) {
538 return add_any_filter_handle(node->frec, ctx, r, c, r_filters,
539 p_filters, c_filters);
540 }
541 }
542
543 ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, r ? r->connection : c, APLOGNO(00082)
544 "an unknown filter was not added: %s", name);
545 return NULL;
546}
547
548AP_DECLARE(ap_filter_t *) ap_add_input_filter(const char *name, void *ctx,
549 request_rec *r, conn_rec *c)
550{
551 return add_any_filter(name, ctx, r, c, registered_input_filters,
552 r ? &r->input_filters : NULL,
553 r ? &r->proto_input_filters : NULL,
554 &c->input_filters);
555}
556
557static void remove_any_filter(ap_filter_t *f, ap_filter_t **r_filt, ap_filter_t **p_filt,
558 ap_filter_t **c_filt)
559{
560 ap_filter_t **curr = r_filt ? r_filt : c_filt;
561 ap_filter_t *fscan = *curr;
562
563 if (p_filt && *p_filt == f)
564 *p_filt = (*p_filt)->next;
565
566 if (*curr == f) {
567 *curr = (*curr)->next;
568 return;
569 }
570
571 while (fscan->next != f) {
572 if (!(fscan = fscan->next)) {
573 return;
574 }
575 }
576
577 fscan->next = f->next;
578}
579
580AP_DECLARE(void) ap_remove_input_filter(ap_filter_t *f)
581{
582 remove_any_filter(f, f->r ? &f->r->input_filters : NULL,
583 f->r ? &f->r->proto_input_filters : NULL,
584 &f->c->input_filters);
585}
586
587static int cfg_closefile(ap_configfile_t *cfp)
588{
589#ifdef DEBUG
590 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, NULL,
591 "Done with config file %s", cfp->name);
592#endif
593 return (cfp->close == NULL) ? 0 : cfp->close(cfp->param);
594}
595
596/* we can't use apr_file_* directly because of linking issues on Windows */
597static apr_status_t cfg_close(void *param)
598{
599 return apr_file_close(param);
600}
601
602static apr_status_t cfg_getch(char *ch, void *param)
603{
604 return apr_file_getc(ch, param);
605}
606
607static apr_status_t cfg_getstr(void *buf, apr_size_t bufsiz, void *param)
608{
609 return apr_file_gets(buf, bufsiz, param);
610}
611
612/* Read one line from open ap_configfile_t, strip LF, increase line number */
613/* If custom handler does not define a getstr() function, read char by char */
614static apr_status_t cfg_getline_core(char *buf, apr_size_t bufsize,
615 apr_size_t offset, ap_configfile_t *cfp)
616{
617 apr_status_t rc;
618 /* If a "get string" function is defined, use it */
619 if (cfp->getstr != NULL) {
620 char *cp;
621 char *cbuf = buf + offset;
622 apr_size_t cbufsize = bufsize - offset;
623
624 while (1) {
625 ++cfp->line_number;
626 rc = cfp->getstr(cbuf, cbufsize, cfp->param);
627 if (rc == APR_EOF) {
628 if (cbuf != buf + offset) {
629 *cbuf = '\0';
630 break;
631 }
632 else {
633 return APR_EOF;
634 }
635 }
636 if (rc != APR_SUCCESS) {
637 return rc;
638 }
639
640 /*
641 * check for line continuation,
642 * i.e. match [^\\]\\[\r]\n only
643 */
644 cp = cbuf;
645 cp += strlen(cp);
646 if (cp > buf && cp[-1] == LF) {
647 cp--;
648 if (cp > buf && cp[-1] == CR)
649 cp--;
650 if (cp > buf && cp[-1] == '\\') {
651 cp--;
652 /*
653 * line continuation requested -
654 * then remove backslash and continue
655 */
656 cbufsize -= (cp-cbuf);
657 cbuf = cp;
658 continue;
659 }
660 }
661 else if (cp - buf >= bufsize - 1) {
662 return APR_ENOSPC;
663 }
664 break;
665 }
666 } else {
667 /* No "get string" function defined; read character by character */
668 apr_size_t i = offset;
669
670 if (bufsize < 2) {
671 /* too small, assume caller is crazy */
672 return APR_EINVAL;
673 }
674 buf[offset] = '\0';
675
676 while (1) {
677 char c;
678 rc = cfp->getch(&c, cfp->param);
679 if (rc == APR_EOF) {
680 if (i > offset)
681 break;
682 else
683 return APR_EOF;
684 }
685 if (rc != APR_SUCCESS)
686 return rc;
687 if (c == LF) {
688 ++cfp->line_number;
689 /* check for line continuation */
690 if (i > 0 && buf[i-1] == '\\') {
691 i--;
692 continue;
693 }
694 else {
695 break;
696 }
697 }
698 buf[i] = c;
699 ++i;
700 if (i >= bufsize - 1) {
701 return APR_ENOSPC;
702 }
703 }
704 buf[i] = '\0';
705 }
706 return APR_SUCCESS;
707}
708
709static int cfg_trim_line(char *buf)
710{
711 char *start, *end;
712 /*
713 * Leading and trailing white space is eliminated completely
714 */
715 start = buf;
716 while (apr_isspace(*start))
717 ++start;
718 /* blast trailing whitespace */
719 end = &start[strlen(start)];
720 while (--end >= start && apr_isspace(*end))
721 *end = '\0';
722 /* Zap leading whitespace by shifting */
723 if (start != buf)
724 memmove(buf, start, end - start + 2);
725#ifdef DEBUG_CFG_LINES
726 ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, NULL, APLOGNO(00555) "Read config: '%s'", buf);
727#endif
728 return end - start + 1;
729}
730
731/* Read one line from open ap_configfile_t, strip LF, increase line number */
732/* If custom handler does not define a getstr() function, read char by char */
733static apr_status_t cfg_getline(char *buf, apr_size_t bufsize,
734 ap_configfile_t *cfp)
735{
736 apr_status_t rc = cfg_getline_core(buf, bufsize, 0, cfp);
737 if (rc == APR_SUCCESS)
738 cfg_trim_line(buf);
739 return rc;
740}
741
742static char *substring_conf(apr_pool_t *p, const char *start, int len,
743 char quote)
744{
745 char *result = apr_palloc(p, len + 1);
746 char *resp = result;
747 int i;
748
749 for (i = 0; i < len; ++i) {
750 if (start[i] == '\\' && (start[i + 1] == '\\'
751 || (quote && start[i + 1] == quote)))
752 *resp++ = start[++i];
753 else
754 *resp++ = start[i];
755 }
756
757 *resp++ = '\0';
758#if RESOLVE_ENV_PER_TOKEN
759 return (char *)ap_resolve_env(p,result);
760#else
761 return result;
762#endif
763}
764
765static char *getword_conf(apr_pool_t *p, const char **line)
766{
767 const char *str = *line, *strend;
768 char *res;
769 char quote;
770
771 while (apr_isspace(*str))
772 ++str;
773
774 if (!*str) {
775 *line = str;
776 return "";
777 }
778
779 if ((quote = *str) == '"' || quote == '\'') {
780 strend = str + 1;
781 while (*strend && *strend != quote) {
782 if (*strend == '\\' && strend[1] &&
783 (strend[1] == quote || strend[1] == '\\')) {
784 strend += 2;
785 }
786 else {
787 ++strend;
788 }
789 }
790 res = substring_conf(p, str + 1, strend - str - 1, quote);
791
792 if (*strend == quote)
793 ++strend;
794 }
795 else {
796 strend = str;
797 while (*strend && !apr_isspace(*strend))
798 ++strend;
799
800 res = substring_conf(p, str, strend - str, 0);
801 }
802
803 while (apr_isspace(*strend))
804 ++strend;
805 *line = strend;
806 return res;
807}
808
809/* Open a ap_configfile_t as FILE, return open ap_configfile_t struct pointer */
810static apr_status_t pcfg_openfile(ap_configfile_t **ret_cfg,
811 apr_pool_t *p, const char *name)
812{
813 ap_configfile_t *new_cfg;
814 apr_file_t *file = NULL;
815 apr_finfo_t finfo;
816 apr_status_t status;
817#ifdef DEBUG
818 char buf[120];
819#endif
820
821 if (name == NULL) {
822 ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, APLOGNO(00552)
823 "Internal error: pcfg_openfile() called with NULL filename");
824 return APR_EBADF;
825 }
826
827 status = apr_file_open(&file, name, APR_READ | APR_BUFFERED,
828 APR_OS_DEFAULT, p);
829#ifdef DEBUG
830 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, NULL, APLOGNO(00553)
831 "Opening config file %s (%s)",
832 name, (status != APR_SUCCESS) ?
833 apr_strerror(status, buf, sizeof(buf)) : "successful");
834#endif
835 if (status != APR_SUCCESS)
836 return status;
837
838 status = apr_file_info_get(&finfo, APR_FINFO_TYPE, file);
839 if (status != APR_SUCCESS)
840 return status;
841
842 if (finfo.filetype != APR_REG &&
843 strcmp(name, "/dev/null") != 0) {
844 ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, APLOGNO(00554)
845 "Access to file %s denied by server: not a regular file",
846 name);
847 apr_file_close(file);
848 return APR_EBADF;
849 }
850
851 new_cfg = apr_palloc(p, sizeof(*new_cfg));
852 new_cfg->param = file;
853 new_cfg->name = apr_pstrdup(p, name);
854 new_cfg->getch = cfg_getch;
855 new_cfg->getstr = cfg_getstr;
856 new_cfg->close = cfg_close;
857 new_cfg->line_number = 0;
858 *ret_cfg = new_cfg;
859 return APR_SUCCESS;
860}
861
862static const command_rec *find_command(const char *name,
863 const command_rec *cmds)
864{
865 while (cmds->name) {
Tim Duesterhus8cb12a82021-01-02 22:31:55 +0100866 if (strcasecmp(name, cmds->name) == 0)
Dragan Dosen59bb97a2017-06-02 12:03:16 +0200867 return cmds;
868 ++cmds;
869 }
870
871 return NULL;
872}
873
874static const char *invoke_cmd(const command_rec *cmd, cmd_parms *parms,
875 void *mconfig, const char *args)
876{
877 int override_list_ok = 0;
878 char *w, *w2, *w3;
879 const char *errmsg = NULL;
880
881 /** Have we been provided a list of acceptable directives? */
882 if (parms->override_list != NULL) {
883 if (apr_table_get(parms->override_list, cmd->name) != NULL) {
884 override_list_ok = 1;
885 }
886 }
887
888 if ((parms->override & cmd->req_override) == 0 && !override_list_ok) {
889 return apr_pstrcat(parms->pool, cmd->name,
890 " not allowed here", NULL);
891 }
892
893 parms->info = cmd->cmd_data;
894 parms->cmd = cmd;
895
896 switch (cmd->args_how) {
897 case RAW_ARGS:
898#ifdef RESOLVE_ENV_PER_TOKEN
899 args = ap_resolve_env(parms->pool,args);
900#endif
901 return cmd->AP_RAW_ARGS(parms, mconfig, args);
902
903 case TAKE_ARGV:
904 {
905 char *argv[MAX_ARGC];
906 int argc = 0;
907
908 do {
909 w = getword_conf(parms->pool, &args);
910 if (*w == '\0' && *args == '\0') {
911 break;
912 }
913 argv[argc] = w;
914 argc++;
915 } while (argc < MAX_ARGC && *args != '\0');
916
917 return cmd->AP_TAKE_ARGV(parms, mconfig, argc, argv);
918 }
919
920 case NO_ARGS:
921 if (*args != 0)
922 return apr_pstrcat(parms->pool, cmd->name, " takes no arguments",
923 NULL);
924
925 return cmd->AP_NO_ARGS(parms, mconfig);
926
927 case TAKE1:
928 w = getword_conf(parms->pool, &args);
929
930 if (*w == '\0' || *args != 0)
931 return apr_pstrcat(parms->pool, cmd->name, " takes one argument",
932 cmd->errmsg ? ", " : NULL, cmd->errmsg, NULL);
933
934 return cmd->AP_TAKE1(parms, mconfig, w);
935
936 case TAKE2:
937 w = getword_conf(parms->pool, &args);
938 w2 = getword_conf(parms->pool, &args);
939
940 if (*w == '\0' || *w2 == '\0' || *args != 0)
941 return apr_pstrcat(parms->pool, cmd->name, " takes two arguments",
942 cmd->errmsg ? ", " : NULL, cmd->errmsg, NULL);
943
944 return cmd->AP_TAKE2(parms, mconfig, w, w2);
945
946 case TAKE12:
947 w = getword_conf(parms->pool, &args);
948 w2 = getword_conf(parms->pool, &args);
949
950 if (*w == '\0' || *args != 0)
951 return apr_pstrcat(parms->pool, cmd->name, " takes 1-2 arguments",
952 cmd->errmsg ? ", " : NULL, cmd->errmsg, NULL);
953
954 return cmd->AP_TAKE2(parms, mconfig, w, *w2 ? w2 : NULL);
955
956 case TAKE3:
957 w = getword_conf(parms->pool, &args);
958 w2 = getword_conf(parms->pool, &args);
959 w3 = getword_conf(parms->pool, &args);
960
961 if (*w == '\0' || *w2 == '\0' || *w3 == '\0' || *args != 0)
962 return apr_pstrcat(parms->pool, cmd->name, " takes three arguments",
963 cmd->errmsg ? ", " : NULL, cmd->errmsg, NULL);
964
965 return cmd->AP_TAKE3(parms, mconfig, w, w2, w3);
966
967 case TAKE23:
968 w = getword_conf(parms->pool, &args);
969 w2 = getword_conf(parms->pool, &args);
970 w3 = *args ? getword_conf(parms->pool, &args) : NULL;
971
972 if (*w == '\0' || *w2 == '\0' || *args != 0)
973 return apr_pstrcat(parms->pool, cmd->name,
974 " takes two or three arguments",
975 cmd->errmsg ? ", " : NULL, cmd->errmsg, NULL);
976
977 return cmd->AP_TAKE3(parms, mconfig, w, w2, w3);
978
979 case TAKE123:
980 w = getword_conf(parms->pool, &args);
981 w2 = *args ? getword_conf(parms->pool, &args) : NULL;
982 w3 = *args ? getword_conf(parms->pool, &args) : NULL;
983
984 if (*w == '\0' || *args != 0)
985 return apr_pstrcat(parms->pool, cmd->name,
986 " takes one, two or three arguments",
987 cmd->errmsg ? ", " : NULL, cmd->errmsg, NULL);
988
989 return cmd->AP_TAKE3(parms, mconfig, w, w2, w3);
990
991 case TAKE13:
992 w = getword_conf(parms->pool, &args);
993 w2 = *args ? getword_conf(parms->pool, &args) : NULL;
994 w3 = *args ? getword_conf(parms->pool, &args) : NULL;
995
996 if (*w == '\0' || (w2 && *w2 && !w3) || *args != 0)
997 return apr_pstrcat(parms->pool, cmd->name,
998 " takes one or three arguments",
999 cmd->errmsg ? ", " : NULL, cmd->errmsg, NULL);
1000
1001 return cmd->AP_TAKE3(parms, mconfig, w, w2, w3);
1002
1003 case ITERATE:
1004 w = getword_conf(parms->pool, &args);
1005
1006 if (*w == '\0')
1007 return apr_pstrcat(parms->pool, cmd->name,
1008 " requires at least one argument",
1009 cmd->errmsg ? ", " : NULL, cmd->errmsg, NULL);
1010
1011 while (*w != '\0') {
1012 errmsg = cmd->AP_TAKE1(parms, mconfig, w);
1013
1014 if (errmsg && strcmp(errmsg, DECLINE_CMD) != 0)
1015 return errmsg;
1016
1017 w = getword_conf(parms->pool, &args);
1018 }
1019
1020 return errmsg;
1021
1022 case ITERATE2:
1023 w = getword_conf(parms->pool, &args);
1024
1025 if (*w == '\0' || *args == 0)
1026 return apr_pstrcat(parms->pool, cmd->name,
1027 " requires at least two arguments",
1028 cmd->errmsg ? ", " : NULL, cmd->errmsg, NULL);
1029
1030 while (*(w2 = getword_conf(parms->pool, &args)) != '\0') {
1031
1032 errmsg = cmd->AP_TAKE2(parms, mconfig, w, w2);
1033
1034 if (errmsg && strcmp(errmsg, DECLINE_CMD) != 0)
1035 return errmsg;
1036 }
1037
1038 return errmsg;
1039
1040 case FLAG:
1041 /*
1042 * This is safe to use temp_pool here, because the 'flag' itself is not
1043 * forwarded as-is
1044 */
1045 w = getword_conf(parms->temp_pool, &args);
1046
Tim Duesterhus8cb12a82021-01-02 22:31:55 +01001047 if (*w == '\0' || (strcasecmp(w, "on") != 0 && strcasecmp(w, "off") != 0))
Dragan Dosen59bb97a2017-06-02 12:03:16 +02001048 return apr_pstrcat(parms->pool, cmd->name, " must be On or Off",
1049 NULL);
1050
1051 return cmd->AP_FLAG(parms, mconfig, strcasecmp(w, "off") != 0);
1052
1053 default:
1054 return apr_pstrcat(parms->pool, cmd->name,
1055 " is improperly configured internally (server bug)",
1056 NULL);
1057 }
1058}
1059
1060static int is_directory(apr_pool_t *p, const char *path)
1061{
1062 apr_finfo_t finfo;
1063
1064 if (apr_stat(&finfo, path, APR_FINFO_TYPE, p) != APR_SUCCESS)
1065 return 0; /* in error condition, just return no */
1066
1067 return (finfo.filetype == APR_DIR);
1068}
1069
1070static char *make_full_path(apr_pool_t *a, const char *src1,
1071 const char *src2)
1072{
1073 apr_size_t len1, len2;
1074 char *path;
1075
1076 len1 = strlen(src1);
1077 len2 = strlen(src2);
1078 /* allocate +3 for '/' delimiter, trailing NULL and overallocate
1079 * one extra byte to allow the caller to add a trailing '/'
1080 */
1081 path = (char *)apr_palloc(a, len1 + len2 + 3);
1082 if (len1 == 0) {
1083 *path = '/';
1084 memcpy(path + 1, src2, len2 + 1);
1085 }
1086 else {
1087 char *next;
1088 memcpy(path, src1, len1);
1089 next = path + len1;
1090 if (next[-1] != '/') {
1091 *next++ = '/';
1092 }
1093 memcpy(next, src2, len2 + 1);
1094 }
1095 return path;
1096}
1097
1098static int fname_alphasort(const void *fn1, const void *fn2)
1099{
1100 const fnames *f1 = fn1;
1101 const fnames *f2 = fn2;
1102
1103 return strcmp(f1->fname,f2->fname);
1104}
1105
1106static const char *process_resource_config(const char *fname,
1107 apr_array_header_t *ari,
1108 apr_pool_t *p,
1109 apr_pool_t *ptemp)
1110{
1111 *(char **)apr_array_push(ari) = (char *)fname;
1112 return NULL;
1113}
1114
1115static const char *process_resource_config_nofnmatch(const char *fname,
1116 apr_array_header_t *ari,
1117 apr_pool_t *p,
1118 apr_pool_t *ptemp,
1119 unsigned depth,
1120 int optional)
1121{
1122 const char *error;
1123 apr_status_t rv;
1124
1125 if (is_directory(ptemp, fname)) {
1126 apr_dir_t *dirp;
1127 apr_finfo_t dirent;
1128 int current;
1129 apr_array_header_t *candidates = NULL;
1130 fnames *fnew;
1131 char *path = apr_pstrdup(ptemp, fname);
1132
1133 if (++depth > MAX_INCLUDE_DIR_DEPTH) {
1134 return apr_psprintf(p, "Directory %s exceeds the maximum include "
1135 "directory nesting level of %u. You have "
1136 "probably a recursion somewhere.", path,
1137 MAX_INCLUDE_DIR_DEPTH);
1138 }
1139
1140 /*
1141 * first course of business is to grok all the directory
1142 * entries here and store 'em away. Recall we need full pathnames
1143 * for this.
1144 */
1145 rv = apr_dir_open(&dirp, path, ptemp);
1146 if (rv != APR_SUCCESS) {
1147 return apr_psprintf(p, "Could not open config directory %s: %pm",
1148 path, &rv);
1149 }
1150
1151 candidates = apr_array_make(ptemp, 1, sizeof(fnames));
1152 while (apr_dir_read(&dirent, APR_FINFO_DIRENT, dirp) == APR_SUCCESS) {
1153 /* strip out '.' and '..' */
Tim Duesterhus8cb12a82021-01-02 22:31:55 +01001154 if (strcmp(dirent.name, ".") != 0
1155 && strcmp(dirent.name, "..") != 0) {
Dragan Dosen59bb97a2017-06-02 12:03:16 +02001156 fnew = (fnames *) apr_array_push(candidates);
1157 fnew->fname = make_full_path(ptemp, path, dirent.name);
1158 }
1159 }
1160
1161 apr_dir_close(dirp);
1162 if (candidates->nelts != 0) {
1163 qsort((void *) candidates->elts, candidates->nelts,
1164 sizeof(fnames), fname_alphasort);
1165
1166 /*
1167 * Now recurse these... we handle errors and subdirectories
1168 * via the recursion, which is nice
1169 */
1170 for (current = 0; current < candidates->nelts; ++current) {
1171 fnew = &((fnames *) candidates->elts)[current];
1172 error = process_resource_config_nofnmatch(fnew->fname,
1173 ari, p, ptemp,
1174 depth, optional);
1175 if (error) {
1176 return error;
1177 }
1178 }
1179 }
1180
1181 return NULL;
1182 }
1183
1184 return process_resource_config(fname, ari, p, ptemp);
1185}
1186
1187static const char *process_resource_config_fnmatch(const char *path,
1188 const char *fname,
1189 apr_array_header_t *ari,
1190 apr_pool_t *p,
1191 apr_pool_t *ptemp,
1192 unsigned depth,
1193 int optional)
1194{
1195 const char *rest;
1196 apr_status_t rv;
1197 apr_dir_t *dirp;
1198 apr_finfo_t dirent;
1199 apr_array_header_t *candidates = NULL;
1200 fnames *fnew;
1201 int current;
1202
1203 /* find the first part of the filename */
1204 rest = ap_strchr_c(fname, '/');
1205 if (rest) {
1206 fname = apr_pstrndup(ptemp, fname, rest - fname);
1207 rest++;
1208 }
1209
1210 /* optimisation - if the filename isn't a wildcard, process it directly */
1211 if (!apr_fnmatch_test(fname)) {
1212 path = make_full_path(ptemp, path, fname);
1213 if (!rest) {
1214 return process_resource_config_nofnmatch(path,
1215 ari, p,
1216 ptemp, 0, optional);
1217 }
1218 else {
1219 return process_resource_config_fnmatch(path, rest,
1220 ari, p,
1221 ptemp, 0, optional);
1222 }
1223 }
1224
1225 /*
1226 * first course of business is to grok all the directory
1227 * entries here and store 'em away. Recall we need full pathnames
1228 * for this.
1229 */
1230 rv = apr_dir_open(&dirp, path, ptemp);
1231 if (rv != APR_SUCCESS) {
1232 return apr_psprintf(p, "Could not open config directory %s: %pm",
1233 path, &rv);
1234 }
1235
1236 candidates = apr_array_make(ptemp, 1, sizeof(fnames));
1237 while (apr_dir_read(&dirent, APR_FINFO_DIRENT | APR_FINFO_TYPE, dirp) == APR_SUCCESS) {
1238 /* strip out '.' and '..' */
Tim Duesterhus8cb12a82021-01-02 22:31:55 +01001239 if (strcmp(dirent.name, ".") != 0
1240 && strcmp(dirent.name, "..") != 0
Dragan Dosen59bb97a2017-06-02 12:03:16 +02001241 && (apr_fnmatch(fname, dirent.name,
1242 APR_FNM_PERIOD) == APR_SUCCESS)) {
1243 const char *full_path = make_full_path(ptemp, path, dirent.name);
1244 /* If matching internal to path, and we happen to match something
1245 * other than a directory, skip it
1246 */
1247 if (rest && (rv == APR_SUCCESS) && (dirent.filetype != APR_DIR)) {
1248 continue;
1249 }
1250 fnew = (fnames *) apr_array_push(candidates);
1251 fnew->fname = full_path;
1252 }
1253 }
1254
1255 apr_dir_close(dirp);
1256 if (candidates->nelts != 0) {
1257 const char *error;
1258
1259 qsort((void *) candidates->elts, candidates->nelts,
1260 sizeof(fnames), fname_alphasort);
1261
1262 /*
1263 * Now recurse these... we handle errors and subdirectories
1264 * via the recursion, which is nice
1265 */
1266 for (current = 0; current < candidates->nelts; ++current) {
1267 fnew = &((fnames *) candidates->elts)[current];
1268 if (!rest) {
1269 error = process_resource_config_nofnmatch(fnew->fname,
1270 ari, p,
1271 ptemp, 0, optional);
1272 }
1273 else {
1274 error = process_resource_config_fnmatch(fnew->fname, rest,
1275 ari, p,
1276 ptemp, 0, optional);
1277 }
1278 if (error) {
1279 return error;
1280 }
1281 }
1282 }
1283 else {
1284
1285 if (!optional) {
1286 return apr_psprintf(p, "No matches for the wildcard '%s' in '%s', failing "
1287 "(use IncludeOptional if required)", fname, path);
1288 }
1289 }
1290
1291 return NULL;
1292}
1293
1294static const char *process_fnmatch_configs(const char *fname,
1295 apr_array_header_t *ari,
1296 apr_pool_t *p,
1297 apr_pool_t *ptemp,
1298 int optional)
1299{
1300 if (!apr_fnmatch_test(fname)) {
1301 return process_resource_config_nofnmatch(fname, ari, p, ptemp, 0, optional);
1302 }
1303 else {
1304 apr_status_t status;
1305 const char *rootpath, *filepath = fname;
1306
1307 /* locate the start of the directories proper */
1308 status = apr_filepath_root(&rootpath, &filepath, APR_FILEPATH_TRUENAME, ptemp);
1309
1310 /* we allow APR_SUCCESS and APR_EINCOMPLETE */
1311 if (APR_ERELATIVE == status) {
1312 return apr_pstrcat(p, "Include must have an absolute path, ", fname, NULL);
1313 }
1314 else if (APR_EBADPATH == status) {
1315 return apr_pstrcat(p, "Include has a bad path, ", fname, NULL);
1316 }
1317
1318 /* walk the filepath */
1319 return process_resource_config_fnmatch(rootpath, filepath, ari, p, ptemp,
1320 0, optional);
1321 }
1322}
1323
1324const char *read_module_config(server_rec *s, void *mconfig,
1325 const command_rec *cmds,
1326 apr_pool_t *p, apr_pool_t *ptemp,
1327 const char *filename)
1328{
1329 apr_array_header_t *ari, *arr;
1330 ap_directive_t *newdir;
1331 cmd_parms *parms;
1332
1333 char line[MAX_STRING_LEN];
1334 const char *errmsg;
1335 const char *err = NULL;
1336
1337 ari = apr_array_make(p, 1, sizeof(char *));
1338 arr = apr_array_make(p, 1, sizeof(cmd_parms));
1339
1340 errmsg = process_fnmatch_configs(filename, ari, p, ptemp, 0);
1341
1342 if (errmsg != NULL)
1343 goto out;
1344
1345 while (ari->nelts || arr->nelts) {
1346
1347 /* similar to process_command_config() */
1348 if (ari->nelts) {
1349 char *inc = *(char **)apr_array_pop(ari);
1350
1351 parms = (cmd_parms *)apr_array_push(arr);
1352 *parms = default_parms;
1353 parms->pool = p;
1354 parms->temp_pool = ptemp;
1355 parms->server = s;
1356 parms->override = (RSRC_CONF | ACCESS_CONF);
1357 parms->override_opts = OPT_ALL | OPT_SYM_OWNER | OPT_MULTI;
1358
1359 if (pcfg_openfile(&parms->config_file, p, inc) != APR_SUCCESS) {
1360 apr_array_pop(arr);
1361 errmsg = apr_pstrcat(p, "Cannot open file: ", inc, NULL);
1362 goto out;
1363 }
1364 }
1365
1366 if (arr->nelts > MAX_INCLUDE_DIR_DEPTH) {
1367 errmsg = apr_psprintf(p, "Exceeded the maximum include "
1368 "directory nesting level of %u. You have "
1369 "probably a recursion somewhere.",
1370 MAX_INCLUDE_DIR_DEPTH);
1371 goto out;
1372 }
1373
1374 if (!(parms = (cmd_parms *)apr_array_pop(arr)))
1375 break;
1376
1377 while (!(cfg_getline(line, MAX_STRING_LEN, parms->config_file))) {
1378
1379 const command_rec *cmd;
1380 char *cmd_name;
1381 const char *args = line;
1382 int optional = 0;
1383
1384 if (*line == '#' || *line == '\0')
1385 continue;
1386
1387 if (!(cmd_name = getword_conf(p, &args)))
1388 continue;
1389
1390 /* similar to invoke_cmd() */
Tim Duesterhus8cb12a82021-01-02 22:31:55 +01001391 if (strcasecmp(cmd_name, "IncludeOptional") == 0 ||
1392 strcasecmp(cmd_name, "Include") == 0)
Dragan Dosen59bb97a2017-06-02 12:03:16 +02001393 {
1394 char *w, *fullname;
1395
Tim Duesterhus8cb12a82021-01-02 22:31:55 +01001396 if (strcasecmp(cmd_name, "IncludeOptional") == 0)
Dragan Dosen59bb97a2017-06-02 12:03:16 +02001397 optional = 1;
1398
1399 w = getword_conf(parms->pool, &args);
1400
1401 if (*w == '\0' || *args != 0) {
1402 errmsg = apr_pstrcat(parms->pool, cmd_name, " takes one argument", NULL);
1403 goto out;
1404 }
1405
1406 fullname = ap_server_root_relative(ptemp, w);
1407 errmsg = process_fnmatch_configs(fullname, ari, p, ptemp, optional);
1408
1409 *(cmd_parms *)apr_array_push(arr) = *parms;
1410
1411 if(errmsg != NULL)
1412 goto out;
1413
1414 parms = NULL;
1415 break;
1416 }
1417
1418 if (!(cmd = find_command(cmd_name, cmds))) {
1419 errmsg = apr_pstrcat(parms->pool, "Invalid command '",
1420 cmd_name, "'", NULL);
1421 goto out;
1422 }
1423
1424 newdir = apr_pcalloc(p, sizeof(ap_directive_t));
1425 newdir->filename = parms->config_file->name;
1426 newdir->line_num = parms->config_file->line_number;
1427 newdir->directive = cmd_name;
1428 newdir->args = apr_pstrdup(p, args);
1429
1430 parms->directive = newdir;
1431
1432 if ((errmsg = invoke_cmd(cmd, parms, mconfig, args)) != NULL)
1433 break;
1434 }
1435
1436 if (parms != NULL)
1437 cfg_closefile(parms->config_file);
1438
1439 if (errmsg != NULL)
1440 break;
1441 }
1442
1443 if (errmsg) {
1444 if (parms) {
1445 err = apr_psprintf(p, "Syntax error on line %d of %s: %s",
1446 parms->config_file->line_number,
1447 parms->config_file->name,
1448 errmsg);
1449 errmsg = err;
1450 }
1451 }
1452
1453out:
1454
1455 while ((parms = (cmd_parms *)apr_array_pop(arr)) != NULL)
1456 cfg_closefile(parms->config_file);
1457
1458 return errmsg;
1459}
1460
1461int lookup_builtin_method(const char *method, apr_size_t len)
1462{
1463 /* Note: from Apache 2 HTTP Server source. */
1464
1465 /* Note: the following code was generated by the "shilka" tool from
1466 the "cocom" parsing/compilation toolkit. It is an optimized lookup
1467 based on analysis of the input keywords. Postprocessing was done
1468 on the shilka output, but the basic structure and analysis is
1469 from there. Should new HTTP methods be added, then manual insertion
1470 into this code is fine, or simply re-running the shilka tool on
1471 the appropriate input. */
1472
1473 /* Note: it is also quite reasonable to just use our method_registry,
1474 but I'm assuming (probably incorrectly) we want more speed here
1475 (based on the optimizations the previous code was doing). */
1476
1477 switch (len)
1478 {
1479 case 3:
1480 switch (method[0])
1481 {
1482 case 'P':
1483 return (method[1] == 'U'
1484 && method[2] == 'T'
1485 ? M_PUT : UNKNOWN_METHOD);
1486 case 'G':
1487 return (method[1] == 'E'
1488 && method[2] == 'T'
1489 ? M_GET : UNKNOWN_METHOD);
1490 default:
1491 return UNKNOWN_METHOD;
1492 }
1493
1494 case 4:
1495 switch (method[0])
1496 {
1497 case 'H':
1498 return (method[1] == 'E'
1499 && method[2] == 'A'
1500 && method[3] == 'D'
1501 ? M_GET : UNKNOWN_METHOD);
1502 case 'P':
1503 return (method[1] == 'O'
1504 && method[2] == 'S'
1505 && method[3] == 'T'
1506 ? M_POST : UNKNOWN_METHOD);
1507 case 'M':
1508 return (method[1] == 'O'
1509 && method[2] == 'V'
1510 && method[3] == 'E'
1511 ? M_MOVE : UNKNOWN_METHOD);
1512 case 'L':
1513 return (method[1] == 'O'
1514 && method[2] == 'C'
1515 && method[3] == 'K'
1516 ? M_LOCK : UNKNOWN_METHOD);
1517 case 'C':
1518 return (method[1] == 'O'
1519 && method[2] == 'P'
1520 && method[3] == 'Y'
1521 ? M_COPY : UNKNOWN_METHOD);
1522 default:
1523 return UNKNOWN_METHOD;
1524 }
1525
1526 case 5:
1527 switch (method[2])
1528 {
1529 case 'T':
1530 return (memcmp(method, "PATCH", 5) == 0
1531 ? M_PATCH : UNKNOWN_METHOD);
1532 case 'R':
1533 return (memcmp(method, "MERGE", 5) == 0
1534 ? M_MERGE : UNKNOWN_METHOD);
1535 case 'C':
1536 return (memcmp(method, "MKCOL", 5) == 0
1537 ? M_MKCOL : UNKNOWN_METHOD);
1538 case 'B':
1539 return (memcmp(method, "LABEL", 5) == 0
1540 ? M_LABEL : UNKNOWN_METHOD);
1541 case 'A':
1542 return (memcmp(method, "TRACE", 5) == 0
1543 ? M_TRACE : UNKNOWN_METHOD);
1544 default:
1545 return UNKNOWN_METHOD;
1546 }
1547
1548 case 6:
1549 switch (method[0])
1550 {
1551 case 'U':
1552 switch (method[5])
1553 {
1554 case 'K':
1555 return (memcmp(method, "UNLOCK", 6) == 0
1556 ? M_UNLOCK : UNKNOWN_METHOD);
1557 case 'E':
1558 return (memcmp(method, "UPDATE", 6) == 0
1559 ? M_UPDATE : UNKNOWN_METHOD);
1560 default:
1561 return UNKNOWN_METHOD;
1562 }
1563 case 'R':
1564 return (memcmp(method, "REPORT", 6) == 0
1565 ? M_REPORT : UNKNOWN_METHOD);
1566 case 'D':
1567 return (memcmp(method, "DELETE", 6) == 0
1568 ? M_DELETE : UNKNOWN_METHOD);
1569 default:
1570 return UNKNOWN_METHOD;
1571 }
1572
1573 case 7:
1574 switch (method[1])
1575 {
1576 case 'P':
1577 return (memcmp(method, "OPTIONS", 7) == 0
1578 ? M_OPTIONS : UNKNOWN_METHOD);
1579 case 'O':
1580 return (memcmp(method, "CONNECT", 7) == 0
1581 ? M_CONNECT : UNKNOWN_METHOD);
1582 case 'H':
1583 return (memcmp(method, "CHECKIN", 7) == 0
1584 ? M_CHECKIN : UNKNOWN_METHOD);
1585 default:
1586 return UNKNOWN_METHOD;
1587 }
1588
1589 case 8:
1590 switch (method[0])
1591 {
1592 case 'P':
1593 return (memcmp(method, "PROPFIND", 8) == 0
1594 ? M_PROPFIND : UNKNOWN_METHOD);
1595 case 'C':
1596 return (memcmp(method, "CHECKOUT", 8) == 0
1597 ? M_CHECKOUT : UNKNOWN_METHOD);
1598 default:
1599 return UNKNOWN_METHOD;
1600 }
1601
1602 case 9:
1603 return (memcmp(method, "PROPPATCH", 9) == 0
1604 ? M_PROPPATCH : UNKNOWN_METHOD);
1605
1606 case 10:
1607 switch (method[0])
1608 {
1609 case 'U':
1610 return (memcmp(method, "UNCHECKOUT", 10) == 0
1611 ? M_UNCHECKOUT : UNKNOWN_METHOD);
1612 case 'M':
1613 return (memcmp(method, "MKACTIVITY", 10) == 0
1614 ? M_MKACTIVITY : UNKNOWN_METHOD);
1615 default:
1616 return UNKNOWN_METHOD;
1617 }
1618
1619 case 11:
1620 return (memcmp(method, "MKWORKSPACE", 11) == 0
1621 ? M_MKWORKSPACE : UNKNOWN_METHOD);
1622
1623 case 15:
1624 return (memcmp(method, "VERSION-CONTROL", 15) == 0
1625 ? M_VERSION_CONTROL : UNKNOWN_METHOD);
1626
1627 case 16:
1628 return (memcmp(method, "BASELINE-CONTROL", 16) == 0
1629 ? M_BASELINE_CONTROL : UNKNOWN_METHOD);
1630
1631 default:
1632 return UNKNOWN_METHOD;
1633 }
1634
1635 /* NOTREACHED */
1636}