xref: /lighttpd1.4/src/configparser.y (revision 4eeeb8fc)
1 %token_prefix TK_
2 %extra_argument {config_t *ctx}
3 %name configparser
4 
5 %include {
6 #include "first.h"
7 #include "configfile.h"
8 #include "buffer.h"
9 #include "array.h"
10 #include "request.h" /* http_request_host_normalize() */
11 
12 #include <assert.h>
13 #include <ctype.h>
14 #include <errno.h>
15 #include <stdio.h>
16 #include <string.h>
17 
18 static void configparser_push(config_t *ctx, data_config *dc, int isnew) {
19   if (isnew) {
20     dc->context_ndx = ctx->all_configs->used;
21     force_assert(dc->context_ndx > ctx->current->context_ndx);
22     array_insert_unique(ctx->all_configs, (data_unset *)dc);
23     dc->parent = ctx->current;
24     vector_config_weak_push(&dc->parent->children, dc);
25   }
26   if (ctx->configs_stack.used > 0 && ctx->current->context_ndx == 0) {
27     fprintf(stderr, "Cannot use conditionals inside a global { ... } block\n");
28     exit(-1);
29   }
30   vector_config_weak_push(&ctx->configs_stack, ctx->current);
31   ctx->current = dc;
32 }
33 
34 static data_config *configparser_pop(config_t *ctx) {
35   data_config *old = ctx->current;
36   ctx->current = vector_config_weak_pop(&ctx->configs_stack);
37   return old;
38 }
39 
40 /* return a copied variable */
41 static data_unset *configparser_get_variable(config_t *ctx, const buffer *key) {
42   data_unset *du;
43   data_config *dc;
44 
45 #if 0
46   fprintf(stderr, "get var %s\n", key->ptr);
47 #endif
48   for (dc = ctx->current; dc; dc = dc->parent) {
49 #if 0
50     fprintf(stderr, "get var on block: %s\n", dc->key->ptr);
51     array_print(dc->value, 0);
52 #endif
53     if (NULL != (du = array_get_element(dc->value, key->ptr))) {
54       return du->copy(du);
55     }
56   }
57   return NULL;
58 }
59 
60 /* op1 is to be eat/return by this function if success, op1->key is not cared
61    op2 is left untouch, unreferenced
62  */
63 data_unset *configparser_merge_data(data_unset *op1, const data_unset *op2) {
64   /* type mismatch */
65   if (op1->type != op2->type) {
66     if (op1->type == TYPE_STRING && op2->type == TYPE_INTEGER) {
67       data_string *ds = (data_string *)op1;
68       buffer_append_int(ds->value, ((data_integer*)op2)->value);
69       return op1;
70     } else if (op1->type == TYPE_INTEGER && op2->type == TYPE_STRING) {
71       data_string *ds = data_string_init();
72       buffer_append_int(ds->value, ((data_integer*)op1)->value);
73       buffer_append_string_buffer(ds->value, ((data_string*)op2)->value);
74       op1->free(op1);
75       return (data_unset *)ds;
76     } else {
77       fprintf(stderr, "data type mismatch, cannot merge\n");
78       op1->free(op1);
79       return NULL;
80     }
81   }
82 
83   switch (op1->type) {
84     case TYPE_STRING:
85       buffer_append_string_buffer(((data_string *)op1)->value, ((data_string *)op2)->value);
86       break;
87     case TYPE_INTEGER:
88       ((data_integer *)op1)->value += ((data_integer *)op2)->value;
89       break;
90     case TYPE_ARRAY: {
91       array *dst = ((data_array *)op1)->value;
92       array *src = ((data_array *)op2)->value;
93       data_unset *du;
94       size_t i;
95 
96       for (i = 0; i < src->used; i ++) {
97         du = (data_unset *)src->data[i];
98         if (du) {
99           if (du->is_index_key || buffer_is_empty(du->key) || !array_get_element(dst, du->key->ptr)) {
100             array_insert_unique(dst, du->copy(du));
101           } else {
102             fprintf(stderr, "Duplicate array-key '%s'\n", du->key->ptr);
103             op1->free(op1);
104             return NULL;
105           }
106         }
107       }
108       break;
109     default:
110       force_assert(0);
111       break;
112     }
113   }
114   return op1;
115 }
116 
117 }
118 
119 %parse_failure {
120   ctx->ok = 0;
121 }
122 
123 input ::= metalines.
124 metalines ::= metalines metaline.
125 metalines ::= .
126 metaline ::= varline.
127 metaline ::= global.
128 metaline ::= condlines(A) EOL. { A = NULL; }
129 metaline ::= include.
130 metaline ::= include_shell.
131 metaline ::= EOL.
132 
133 %type       value                  {data_unset *}
134 %type       expression             {data_unset *}
135 %type       aelement               {data_unset *}
136 %type       condline               {data_config *}
137 %type       condlines              {data_config *}
138 %type       aelements              {array *}
139 %type       array                  {array *}
140 %type       key                    {buffer *}
141 %type       stringop               {buffer *}
142 
143 %type       cond                   {config_cond_t }
144 
145 %destructor value                  { if ($$) $$->free($$); }
146 %destructor expression             { if ($$) $$->free($$); }
147 %destructor aelement               { if ($$) $$->free($$); }
148 %destructor aelements              { array_free($$); }
149 %destructor array                  { array_free($$); }
150 %destructor key                    { buffer_free($$); }
151 %destructor stringop               { buffer_free($$); }
152 
153 %token_type                        {buffer *}
154 %token_destructor                  { buffer_free($$); }
155 
156 varline ::= key(A) ASSIGN expression(B). {
157   if (ctx->ok) {
158     buffer_copy_buffer(B->key, A);
159     if (strncmp(A->ptr, "env.", sizeof("env.") - 1) == 0) {
160       fprintf(stderr, "Setting env variable is not supported in conditional %d %s: %s\n",
161           ctx->current->context_ndx,
162           ctx->current->key->ptr, A->ptr);
163       ctx->ok = 0;
164     } else if (NULL == array_get_element(ctx->current->value, B->key->ptr)) {
165       array_insert_unique(ctx->current->value, B);
166       B = NULL;
167     } else {
168       fprintf(stderr, "Duplicate config variable in conditional %d %s: %s\n",
169               ctx->current->context_ndx,
170               ctx->current->key->ptr, B->key->ptr);
171       ctx->ok = 0;
172       B->free(B);
173       B = NULL;
174     }
175   }
176   buffer_free(A);
177   A = NULL;
178 }
179 
180 varline ::= key(A) APPEND expression(B). {
181   if (ctx->ok) {
182     array *vars = ctx->current->value;
183     data_unset *du;
184 
185     if (strncmp(A->ptr, "env.", sizeof("env.") - 1) == 0) {
186       fprintf(stderr, "Appending env variable is not supported in conditional %d %s: %s\n",
187           ctx->current->context_ndx,
188           ctx->current->key->ptr, A->ptr);
189       ctx->ok = 0;
190     } else if (NULL != (du = array_extract_element(vars, A->ptr)) || NULL != (du = configparser_get_variable(ctx, A))) {
191       du = configparser_merge_data(du, B);
192       if (NULL == du) {
193         ctx->ok = 0;
194       }
195       else {
196         buffer_copy_buffer(du->key, A);
197         array_insert_unique(ctx->current->value, du);
198       }
199       B->free(B);
200     } else {
201       buffer_copy_buffer(B->key, A);
202       array_insert_unique(ctx->current->value, B);
203     }
204     buffer_free(A);
205     A = NULL;
206     B = NULL;
207   }
208 }
209 
210 key(A) ::= LKEY(B). {
211   if (strchr(B->ptr, '.') == NULL) {
212     A = buffer_init_string("var.");
213     buffer_append_string_buffer(A, B);
214     buffer_free(B);
215     B = NULL;
216   } else {
217     A = B;
218     B = NULL;
219   }
220 }
221 
222 expression(A) ::= expression(B) PLUS value(C). {
223   A = NULL;
224   if (ctx->ok) {
225     A = configparser_merge_data(B, C);
226     if (NULL == A) {
227       ctx->ok = 0;
228     }
229     B = NULL;
230     C->free(C);
231     C = NULL;
232   }
233 }
234 
235 expression(A) ::= value(B). {
236   A = B;
237   B = NULL;
238 }
239 
240 value(A) ::= key(B). {
241   A = NULL;
242   if (ctx->ok) {
243     if (strncmp(B->ptr, "env.", sizeof("env.") - 1) == 0) {
244       char *env;
245 
246       if (NULL != (env = getenv(B->ptr + 4))) {
247         data_string *ds;
248         ds = data_string_init();
249         buffer_append_string(ds->value, env);
250         A = (data_unset *)ds;
251       }
252       else {
253         fprintf(stderr, "Undefined env variable: %s\n", B->ptr + 4);
254         ctx->ok = 0;
255       }
256     } else if (NULL == (A = configparser_get_variable(ctx, B))) {
257       fprintf(stderr, "Undefined config variable: %s\n", B->ptr);
258       ctx->ok = 0;
259     }
260     buffer_free(B);
261     B = NULL;
262   }
263 }
264 
265 value(A) ::= STRING(B). {
266   A = (data_unset *)data_string_init();
267   buffer_copy_buffer(((data_string *)(A))->value, B);
268   buffer_free(B);
269   B = NULL;
270 }
271 
272 value(A) ::= INTEGER(B). {
273   char *endptr;
274   A = (data_unset *)data_integer_init();
275   errno = 0;
276   ((data_integer *)(A))->value = strtol(B->ptr, &endptr, 10);
277   /* skip trailing whitespace */
278   if (endptr != B->ptr) while (isspace(*endptr)) endptr++;
279   if (0 != errno || *endptr != '\0') {
280     fprintf(stderr, "error parsing number: '%s'\n", B->ptr);
281     ctx->ok = 0;
282   }
283   buffer_free(B);
284   B = NULL;
285 }
286 value(A) ::= array(B). {
287   A = (data_unset *)data_array_init();
288   array_free(((data_array *)(A))->value);
289   ((data_array *)(A))->value = B;
290   B = NULL;
291 }
292 array(A) ::= LPARAN RPARAN. {
293   A = array_init();
294 }
295 array(A) ::= LPARAN aelements(B) RPARAN. {
296   A = B;
297   B = NULL;
298 }
299 
300 aelements(A) ::= aelements(C) COMMA aelement(B). {
301   A = NULL;
302   if (ctx->ok) {
303     if (buffer_is_empty(B->key) ||
304         NULL == array_get_element(C, B->key->ptr)) {
305       array_insert_unique(C, B);
306       B = NULL;
307     } else {
308       fprintf(stderr, "Error: duplicate array-key: %s. Please get rid of the duplicate entry.\n",
309               B->key->ptr);
310       ctx->ok = 0;
311       B->free(B);
312       B = NULL;
313     }
314 
315     A = C;
316     C = NULL;
317   }
318 }
319 
320 aelements(A) ::= aelements(C) COMMA. {
321   A = C;
322   C = NULL;
323 }
324 
325 aelements(A) ::= aelement(B). {
326   A = NULL;
327   if (ctx->ok) {
328     A = array_init();
329     array_insert_unique(A, B);
330     B = NULL;
331   }
332 }
333 
334 aelement(A) ::= expression(B). {
335   A = B;
336   B = NULL;
337 }
338 aelement(A) ::= stringop(B) ARRAY_ASSIGN expression(C). {
339   A = NULL;
340   if (ctx->ok) {
341     buffer_copy_buffer(C->key, B);
342     buffer_free(B);
343     B = NULL;
344 
345     A = C;
346     C = NULL;
347   }
348 }
349 
350 eols ::= EOL.
351 eols ::= .
352 
353 globalstart ::= GLOBAL. {
354   data_config *dc;
355   dc = (data_config *)array_get_element(ctx->srv->config_context, "global");
356   force_assert(dc);
357   configparser_push(ctx, dc, 0);
358 }
359 
360 global ::= globalstart LCURLY metalines RCURLY. {
361   force_assert(ctx->current);
362   configparser_pop(ctx);
363   force_assert(ctx->current);
364 }
365 
366 condlines(A) ::= condlines(B) eols ELSE condline(C). {
367   A = NULL;
368   if (ctx->ok) {
369     if (B->context_ndx >= C->context_ndx) {
370       fprintf(stderr, "unreachable else condition\n");
371       ctx->ok = 0;
372     }
373     C->prev = B;
374     B->next = C;
375     A = C;
376     B = NULL;
377     C = NULL;
378   }
379 }
380 
381 condlines(A) ::= condline(B). {
382   A = B;
383   B = NULL;
384 }
385 
386 condline(A) ::= context LCURLY metalines RCURLY. {
387   A = NULL;
388   if (ctx->ok) {
389     data_config *cur;
390 
391     cur = ctx->current;
392     configparser_pop(ctx);
393 
394     force_assert(cur && ctx->current);
395 
396     A = cur;
397   }
398 }
399 
400 context ::= DOLLAR SRVVARNAME(B) LBRACKET stringop(C) RBRACKET cond(E) expression(D). {
401   data_config *dc;
402   buffer *b, *rvalue, *op;
403 
404   if (ctx->ok && D->type != TYPE_STRING) {
405     fprintf(stderr, "rvalue must be string");
406     ctx->ok = 0;
407   }
408 
409   if (ctx->ok) {
410     switch(E) {
411     case CONFIG_COND_NE:
412       op = buffer_init_string("!=");
413       break;
414     case CONFIG_COND_EQ:
415       op = buffer_init_string("==");
416       break;
417     case CONFIG_COND_NOMATCH:
418       op = buffer_init_string("!~");
419       break;
420     case CONFIG_COND_MATCH:
421       op = buffer_init_string("=~");
422       break;
423     default:
424       force_assert(0);
425       return; /* unreachable */
426     }
427 
428     b = buffer_init();
429     buffer_copy_buffer(b, ctx->current->key);
430     buffer_append_string(b, "/");
431     buffer_append_string_buffer(b, B);
432     buffer_append_string_buffer(b, C);
433     buffer_append_string_buffer(b, op);
434     rvalue = ((data_string*)D)->value;
435     buffer_append_string_buffer(b, rvalue);
436 
437     if (NULL != (dc = (data_config *)array_get_element(ctx->all_configs, b->ptr))) {
438       configparser_push(ctx, dc, 0);
439     } else {
440       static const struct {
441         comp_key_t comp;
442         char *comp_key;
443         size_t len;
444       } comps[] = {
445         { COMP_SERVER_SOCKET,      CONST_STR_LEN("SERVER[\"socket\"]"   ) },
446         { COMP_HTTP_URL,           CONST_STR_LEN("HTTP[\"url\"]"        ) },
447         { COMP_HTTP_HOST,          CONST_STR_LEN("HTTP[\"host\"]"       ) },
448         { COMP_HTTP_REFERER,       CONST_STR_LEN("HTTP[\"referer\"]"    ) },
449         { COMP_HTTP_USER_AGENT,    CONST_STR_LEN("HTTP[\"useragent\"]"  ) },
450         { COMP_HTTP_USER_AGENT,    CONST_STR_LEN("HTTP[\"user-agent\"]"  ) },
451         { COMP_HTTP_LANGUAGE,      CONST_STR_LEN("HTTP[\"language\"]"   ) },
452         { COMP_HTTP_COOKIE,        CONST_STR_LEN("HTTP[\"cookie\"]"     ) },
453         { COMP_HTTP_REMOTE_IP,     CONST_STR_LEN("HTTP[\"remoteip\"]"   ) },
454         { COMP_HTTP_REMOTE_IP,     CONST_STR_LEN("HTTP[\"remote-ip\"]"   ) },
455         { COMP_HTTP_QUERY_STRING,  CONST_STR_LEN("HTTP[\"querystring\"]") },
456         { COMP_HTTP_QUERY_STRING,  CONST_STR_LEN("HTTP[\"query-string\"]") },
457         { COMP_HTTP_REQUEST_METHOD, CONST_STR_LEN("HTTP[\"request-method\"]") },
458         { COMP_HTTP_SCHEME,        CONST_STR_LEN("HTTP[\"scheme\"]"     ) },
459         { COMP_UNSET, NULL, 0 },
460       };
461       size_t i;
462 
463       dc = data_config_init();
464 
465       buffer_copy_buffer(dc->key, b);
466       buffer_copy_buffer(dc->op, op);
467       buffer_copy_buffer(dc->comp_key, B);
468       buffer_append_string_len(dc->comp_key, CONST_STR_LEN("[\""));
469       buffer_append_string_buffer(dc->comp_key, C);
470       buffer_append_string_len(dc->comp_key, CONST_STR_LEN("\"]"));
471       dc->cond = E;
472 
473       for (i = 0; comps[i].comp_key; i ++) {
474         if (buffer_is_equal_string(
475               dc->comp_key, comps[i].comp_key, comps[i].len)) {
476           dc->comp = comps[i].comp;
477           break;
478         }
479       }
480       if (COMP_UNSET == dc->comp) {
481         fprintf(stderr, "error comp_key %s", dc->comp_key->ptr);
482         ctx->ok = 0;
483       }
484       else if (COMP_HTTP_REMOTE_IP == dc->comp
485                && (dc->cond == CONFIG_COND_EQ || dc->cond == CONFIG_COND_NE)) {
486         char * const slash = strchr(rvalue->ptr, '/');
487         if (NULL != slash && slash != rvalue->ptr){/*(skip AF_UNIX /path/file)*/
488           char *nptr;
489           const unsigned long nm_bits = strtoul(slash + 1, &nptr, 10);
490           if (*nptr || 0 == nm_bits
491               || nm_bits > (rvalue->ptr[0] == '[' ? 128 : 32)) {
492             /*(also rejects (slash+1 == nptr) which results in nm_bits = 0)*/
493             fprintf(stderr, "invalid or missing netmask: %s\n", rvalue->ptr);
494             ctx->ok = 0;
495           }
496           else {
497             int rc;
498             buffer_string_set_length(rvalue, (size_t)(slash - rvalue->ptr)); /*(truncate)*/
499             rc = http_request_host_normalize(rvalue);
500             buffer_append_string_len(rvalue, CONST_STR_LEN("/"));
501             buffer_append_int(rvalue, (int)nm_bits);
502             if (0 != rc) {
503               fprintf(stderr, "invalid IP addr: %s\n", rvalue->ptr);
504               ctx->ok = 0;
505             }
506           }
507         }
508         else {
509           if (http_request_host_normalize(rvalue)) {
510             fprintf(stderr, "invalid IP addr: %s\n", rvalue->ptr);
511             ctx->ok = 0;
512           }
513         }
514       }
515       else if (COMP_SERVER_SOCKET == dc->comp) {
516         /*(redundant with parsing in network.c; not actually required here)*/
517         if (rvalue->ptr[0] != ':' /*(network.c special-cases ":" and "[]")*/
518             && !(rvalue->ptr[0] == '[' && rvalue->ptr[1] == ']')) {
519           if (http_request_host_normalize(rvalue)) {
520             fprintf(stderr, "invalid IP addr: %s\n", rvalue->ptr);
521             ctx->ok = 0;
522           }
523         }
524       }
525       else if (COMP_HTTP_HOST == dc->comp) {
526         if (dc->cond == CONFIG_COND_EQ || dc->cond == CONFIG_COND_NE) {
527           if (http_request_host_normalize(rvalue)) {
528             fprintf(stderr, "invalid IP addr: %s\n", rvalue->ptr);
529             ctx->ok = 0;
530           }
531         }
532       }
533 
534       if (ctx->ok) switch(E) {
535       case CONFIG_COND_NE:
536       case CONFIG_COND_EQ:
537         dc->string = buffer_init_buffer(rvalue);
538         break;
539       case CONFIG_COND_NOMATCH:
540       case CONFIG_COND_MATCH: {
541 #ifdef HAVE_PCRE_H
542         const char *errptr;
543         int erroff, captures;
544 
545         if (NULL == (dc->regex =
546             pcre_compile(rvalue->ptr, 0, &errptr, &erroff, NULL))) {
547           dc->string = buffer_init_string(errptr);
548           dc->cond = CONFIG_COND_UNSET;
549 
550           fprintf(stderr, "parsing regex failed: %s -> %s at offset %d\n",
551               rvalue->ptr, errptr, erroff);
552 
553           ctx->ok = 0;
554         } else if (NULL == (dc->regex_study =
555             pcre_study(dc->regex, 0, &errptr)) &&
556                    errptr != NULL) {
557           fprintf(stderr, "studying regex failed: %s -> %s\n",
558               rvalue->ptr, errptr);
559           ctx->ok = 0;
560         } else if (0 != (pcre_fullinfo(dc->regex, dc->regex_study, PCRE_INFO_CAPTURECOUNT, &captures))) {
561           fprintf(stderr, "getting capture count for regex failed: %s\n",
562               rvalue->ptr);
563           ctx->ok = 0;
564         } else if (captures > 9) {
565           fprintf(stderr, "Too many captures in regex, use (?:...) instead of (...): %s\n",
566               rvalue->ptr);
567           ctx->ok = 0;
568         } else {
569           dc->string = buffer_init_buffer(rvalue);
570         }
571 #else
572         fprintf(stderr, "can't handle '$%s[%s] =~ ...' as you compiled without pcre support. \n"
573                         "(perhaps just a missing pcre-devel package ?) \n",
574                         B->ptr, C->ptr);
575         ctx->ok = 0;
576  #endif
577         break;
578       }
579 
580       default:
581         fprintf(stderr, "unknown condition for $%s[%s]\n",
582                         B->ptr, C->ptr);
583         ctx->ok = 0;
584         break;
585       }
586 
587       if (ctx->ok) {
588         configparser_push(ctx, dc, 1);
589       } else {
590         dc->free((data_unset*) dc);
591       }
592     }
593 
594     buffer_free(b);
595     buffer_free(op);
596     buffer_free(B);
597     B = NULL;
598     buffer_free(C);
599     C = NULL;
600     D->free(D);
601     D = NULL;
602   }
603 }
604 cond(A) ::= EQ. {
605   A = CONFIG_COND_EQ;
606 }
607 cond(A) ::= MATCH. {
608   A = CONFIG_COND_MATCH;
609 }
610 cond(A) ::= NE. {
611   A = CONFIG_COND_NE;
612 }
613 cond(A) ::= NOMATCH. {
614   A = CONFIG_COND_NOMATCH;
615 }
616 
617 stringop(A) ::= expression(B). {
618   A = NULL;
619   if (ctx->ok) {
620     if (B->type == TYPE_STRING) {
621       A = buffer_init_buffer(((data_string*)B)->value);
622     } else if (B->type == TYPE_INTEGER) {
623       A = buffer_init();
624       buffer_copy_int(A, ((data_integer *)B)->value);
625     } else {
626       fprintf(stderr, "operand must be string");
627       ctx->ok = 0;
628     }
629   }
630   B->free(B);
631   B = NULL;
632 }
633 
634 include ::= INCLUDE stringop(A). {
635   if (ctx->ok) {
636     if (0 != config_parse_file(ctx->srv, ctx, A->ptr)) {
637       ctx->ok = 0;
638     }
639     buffer_free(A);
640     A = NULL;
641   }
642 }
643 
644 include_shell ::= INCLUDE_SHELL stringop(A). {
645   if (ctx->ok) {
646     if (0 != config_parse_cmd(ctx->srv, ctx, A->ptr)) {
647       ctx->ok = 0;
648     }
649     buffer_free(A);
650     A = NULL;
651   }
652 }
653