Blob


1 %{
3 /*
4 * Copyright (c) 2021-2024 Omar Polo <op@omarpolo.com>
5 * Copyright (c) 2018 Florian Obser <florian@openbsd.org>
6 * Copyright (c) 2004, 2005 Esben Norby <norby@openbsd.org>
7 * Copyright (c) 2004 Ryan McBride <mcbride@openbsd.org>
8 * Copyright (c) 2002, 2003, 2004 Henning Brauer <henning@openbsd.org>
9 * Copyright (c) 2001 Markus Friedl. All rights reserved.
10 * Copyright (c) 2001 Daniel Hartmeier. All rights reserved.
11 * Copyright (c) 2001 Theo de Raadt. All rights reserved.
12 *
13 * Permission to use, copy, modify, and distribute this software for any
14 * purpose with or without fee is hereby granted, provided that the above
15 * copyright notice and this permission notice appear in all copies.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
18 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
19 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
20 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
21 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
22 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
23 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
24 */
26 #include "gmid.h"
28 #include <ctype.h>
29 #include <errno.h>
30 #include <netdb.h>
31 #include <stdarg.h>
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <string.h>
35 #include <syslog.h>
37 #include "log.h"
39 struct conf *conf;
41 static const char *default_host = NULL;
42 static uint16_t default_port = 1965;
44 TAILQ_HEAD(files, file) files = TAILQ_HEAD_INITIALIZER(files);
45 static struct file {
46 TAILQ_ENTRY(file) entry;
47 FILE *stream;
48 char *name;
49 size_t ungetpos;
50 size_t ungetsize;
51 u_char *ungetbuf;
52 int eof_reached;
53 int lineno;
54 int errors;
55 } *file, *topfile;
57 struct file *pushfile(const char *, int);
58 int popfile(void);
59 int yyparse(void);
60 int yylex(void);
61 void yyerror(const char *, ...)
62 __attribute__((__format__ (printf, 1, 2)))
63 __attribute__((__nonnull__ (1)));
64 void yywarn(const char *, ...)
65 __attribute__((__format__ (printf, 1, 2)))
66 __attribute__((__nonnull__ (1)));
67 int kw_cmp(const void *, const void *);
68 int lookup(char *);
69 int igetc(void);
70 int lgetc(int);
71 void lungetc(int);
72 int findeol(void);
74 /*
75 * #define YYDEBUG 1
76 * int yydebug = 1;
77 */
79 TAILQ_HEAD(symhead, sym) symhead = TAILQ_HEAD_INITIALIZER(symhead);
80 struct sym {
81 TAILQ_ENTRY(sym) entry;
82 int used;
83 int persist;
84 char *name;
85 char *val;
86 };
88 int symset(const char *, const char *, int);
89 char *symget(const char *);
91 char *ensure_absolute_path(char*);
92 int check_block_code(int);
93 char *check_block_fmt(char*);
94 int check_strip_no(int);
95 int check_prefork_num(int);
96 void advance_loc(void);
97 void advance_proxy(void);
98 int fastcgi_conf(const char *, const char *);
99 void add_param(char *, char *);
100 int getservice(const char *);
101 void listen_on(const char *, const char *, int);
103 static struct vhost *host;
104 static struct location *loc;
105 static struct proxy *proxy;
106 static char *current_media;
107 static int errors;
109 typedef struct {
110 union {
111 char *string;
112 int number;
113 } v;
114 int lineno;
115 } YYSTYPE;
117 #define YYSTYPE YYSTYPE
119 %}
121 /* for bison: */
122 /* %define parse.error verbose */
124 %token ACCESS ALIAS AUTO
125 %token BLOCK
126 %token CA CERT CGI CHROOT CLIENT
127 %token DEFAULT
128 %token FACILITY FASTCGI FOR_HOST
129 %token INCLUDE INDEX IPV6
130 %token KEY
131 %token LANG LISTEN LOCATION LOG
132 %token OCSP OFF ON
133 %token PARAM PORT PREFORK PROTO PROTOCOLS PROXY PROXYV1
134 %token RELAY_TO REQUIRE RETURN ROOT
135 %token SERVER SNI SOCKET STRIP STYLE SYSLOG
136 %token TCP TOEXT TYPE TYPES
137 %token USE_TLS USER
138 %token VERIFYNAME
140 %token ERROR
142 %token <v.string> STRING
143 %token <v.number> NUM
145 %type <v.number> bool proxy_port
146 %type <v.string> string numberstring listen_addr
148 %%
150 /*
151 * Allow empty lines at the start of the configuration.
152 */
153 grammar : nl conf | conf
156 conf : /* empty */
157 | conf include nl
158 | conf varset nl
159 | conf option nl
160 | conf vhost nl
161 | conf types nl
162 | conf error nl { file->errors++; }
165 include : INCLUDE STRING {
166 struct file *nfile;
168 if ((nfile = pushfile($2, 0)) == NULL) {
169 yyerror("failed to include file %s", $2);
170 free($2);
171 YYERROR;
173 free($2);
175 file = nfile;
176 lungetc('\n');
180 bool : ON { $$ = 1; }
181 | OFF { $$ = 0; }
184 string : string STRING {
185 if (asprintf(&$$, "%s%s", $1, $2) == -1) {
186 free($1);
187 free($2);
188 yyerror("string: asprintf: %s", strerror(errno));
189 YYERROR;
191 free($1);
192 free($2);
194 | STRING
197 numberstring : NUM {
198 char *s;
199 if (asprintf(&s, "%d", $1) == -1) {
200 yyerror("asprintf: number");
201 YYERROR;
203 $$ = s;
205 | STRING
208 varset : STRING '=' string {
209 char *s = $1;
210 while (*s++) {
211 if (isspace((unsigned char)*s)) {
212 yyerror("macro name cannot contain "
213 "whitespaces");
214 free($1);
215 free($3);
216 YYERROR;
219 symset($1, $3, 0);
220 free($1);
221 free($3);
225 option : CHROOT string {
226 if (strlcpy(conf->chroot, $2, sizeof(conf->chroot)) >=
227 sizeof(conf->chroot))
228 yyerror("chroot path too long");
229 free($2);
231 | IPV6 bool {
232 yywarn("option `ipv6' is deprecated,"
233 " please use `listen on'");
234 if ($2)
235 default_host = NULL;
236 else
237 default_host = "0.0.0.0";
239 | log
240 | PORT NUM {
241 yywarn("option `port' is deprecated,"
242 " please use `listen on'");
243 default_port = $2;
245 | PREFORK NUM { conf->prefork = check_prefork_num($2); }
246 | PROTOCOLS string {
247 if (tls_config_parse_protocols(&conf->protos, $2) == -1)
248 yyerror("invalid protocols string \"%s\"", $2);
249 free($2);
251 | USER string {
252 if (strlcpy(conf->user, $2, sizeof(conf->user)) >=
253 sizeof(conf->user))
254 yyerror("user name too long");
255 free($2);
259 log : LOG '{' optnl logopts '}'
260 | LOG logopt
263 logopts : /* empty */
264 | logopts logopt optnl
267 logopt : ACCESS string {
268 free(conf->log_access);
269 conf->log_access = $2;
271 | STYLE string {
272 if (!strcmp("combined", $2))
273 conf->log_format = LOG_FORMAT_COMBINED;
274 else if (!strcmp("common", $2))
275 conf->log_format = LOG_FORMAT_COMMON;
276 else if (!strcmp("condensed", $2))
277 conf->log_format = LOG_FORMAT_CONDENSED;
278 else if (!strcmp("legacy", $2))
279 conf->log_format = LOG_FORMAT_LEGACY;
280 else
281 yyerror("unknown log style: %s", $2);
282 free($2);
284 | SYSLOG FACILITY string {
285 const char *str = $3;
287 conf->log_syslog = 1;
289 if (!strncasecmp(str, "LOG_", 4))
290 str += 4;
292 if (!strcasecmp(str, "daemon"))
293 conf->log_facility = LOG_DAEMON;
294 #ifdef LOG_FTP
295 else if (!strcasecmp(str, "ftp"))
296 conf->log_facility = LOG_FTP;
297 #endif
298 else if (!strcasecmp(str, "local1"))
299 conf->log_facility = LOG_LOCAL1;
300 else if (!strcasecmp(str, "local2"))
301 conf->log_facility = LOG_LOCAL2;
302 else if (!strcasecmp(str, "local3"))
303 conf->log_facility = LOG_LOCAL3;
304 else if (!strcasecmp(str, "local4"))
305 conf->log_facility = LOG_LOCAL4;
306 else if (!strcasecmp(str, "local5"))
307 conf->log_facility = LOG_LOCAL5;
308 else if (!strcasecmp(str, "local6"))
309 conf->log_facility = LOG_LOCAL6;
310 else if (!strcasecmp(str, "local7"))
311 conf->log_facility = LOG_LOCAL7;
312 else if (!strcasecmp(str, "user"))
313 conf->log_facility = LOG_USER;
314 else
315 yywarn("unknown syslog facility `%s'",
316 $3);
318 free($3);
320 | SYSLOG OFF {
321 conf->log_syslog = 0;
323 | SYSLOG {
324 conf->log_syslog = 1;
328 vhost : SERVER string {
329 host = new_vhost();
330 TAILQ_INSERT_HEAD(&conf->hosts, host, vhosts);
332 loc = new_location();
333 TAILQ_INSERT_HEAD(&host->locations, loc, locations);
335 TAILQ_INIT(&host->proxies);
337 (void) strlcpy(loc->match, "*", sizeof(loc->match));
339 if (strlcpy(host->domain, $2, sizeof(host->domain))
340 >= sizeof(host->domain))
341 yyerror("server name too long: %s", $2);
343 if (strstr($2, "xn--") != NULL) {
344 yywarn("\"%s\" looks like punycode: you "
345 "should use the decoded hostname", $2);
348 free($2);
349 } '{' optnl servbody '}' {
350 if (host->cert_path == NULL ||
351 host->key_path == NULL)
352 yyerror("invalid vhost definition: %s",
353 host->domain);
354 if (TAILQ_EMPTY(&host->addrs)) {
355 char portno[32];
356 int r;
358 r = snprintf(portno, sizeof(portno), "%d",
359 default_port);
360 if (r < 0 || (size_t)r >= sizeof(portno))
361 fatal("snprintf");
363 yywarn("missing `listen on' in server %s,"
364 " assuming %s port %d", host->domain,
365 default_host ? default_host : "*",
366 default_port);
367 listen_on(default_host, portno, 0);
370 | error '}' { yyerror("bad server directive"); }
373 servbody : /* empty */
374 | servbody servopt optnl
375 | servbody location optnl
376 | servbody proxy optnl
379 listen_addr : '*' { $$ = NULL; }
380 | STRING
383 servopt : ALIAS string {
384 struct alist *a;
386 a = xcalloc(1, sizeof(*a));
387 if (strlcpy(a->alias, $2, sizeof(a->alias))
388 >= sizeof(a->alias))
389 yyerror("alias too long: %s", $2);
390 free($2);
391 TAILQ_INSERT_TAIL(&host->aliases, a, aliases);
393 | CERT string {
394 ensure_absolute_path($2);
395 free(host->cert_path);
396 host->cert_path = $2;
398 | CGI string {
399 free($2);
400 yyerror("`cgi' was removed in gmid 2.0."
401 " Please use fastcgi or proxy instead.");
403 | KEY string {
404 ensure_absolute_path($2);
405 free(host->key_path);
406 host->key_path = $2;
408 | OCSP string {
409 ensure_absolute_path($2);
410 free(host->ocsp_path);
411 host->ocsp_path = $2;
413 | PARAM string '=' string {
414 yywarn("the top-level `param' directive is deprecated."
415 " Please use `fastcgi { param ... }`");
416 add_param($2, $4);
418 | LISTEN ON listen_addr {
419 listen_on($3, "1965", 0);
420 free($3);
422 | LISTEN ON listen_addr PORT STRING {
423 listen_on($3, $5, 0);
424 free($3);
425 free($5);
427 | LISTEN ON listen_addr PORT NUM {
428 char portno[32];
429 int r;
431 r = snprintf(portno, sizeof(portno), "%d", $5);
432 if (r < 0 || (size_t)r >= sizeof(portno))
433 fatal("snprintf");
435 listen_on($3, portno, 0);
436 free($3);
438 | LISTEN ON listen_addr PROXYV1 {
439 listen_on($3, "1965", 1);
440 free($3);
442 | LISTEN ON listen_addr PORT STRING PROXYV1 {
443 listen_on($3, $5, 1);
444 free($3);
445 free($5);
447 | LISTEN ON listen_addr PORT NUM PROXYV1 {
448 char portno[32];
449 int r;
451 r = snprintf(portno, sizeof(portno), "%d", $5);
452 if (r < 0 || (size_t)r >= sizeof(portno))
453 fatal("snprintf");
455 listen_on($3, portno, 1);
456 free($3);
458 | locopt
461 proxy : PROXY { advance_proxy(); }
462 proxy_matches '{' optnl proxy_opts '}' {
463 if (*proxy->host == '\0')
464 yyerror("invalid proxy block: missing `relay-to' option");
466 if ((proxy->cert_path == NULL && proxy->key_path != NULL) ||
467 (proxy->cert_path != NULL && proxy->key_path == NULL))
468 yyerror("invalid proxy block: missing cert or key");
472 proxy_matches : /* empty */
473 | proxy_matches proxy_match
476 proxy_port : /* empty */ { $$ = 1965; }
477 | PORT STRING {
478 if (($$ = getservice($2)) == -1)
479 yyerror("invalid port number %s", $2);
480 free($2);
482 | PORT NUM { $$ = $2; }
485 proxy_match : PROTO string {
486 if (strlcpy(proxy->match_proto, $2,
487 sizeof(proxy->match_proto))
488 >= sizeof(proxy->match_proto))
489 yyerror("proto too long: %s", $2);
490 free($2);
492 | FOR_HOST string proxy_port {
493 if (strlcpy(proxy->match_host, $2,
494 sizeof(proxy->match_host))
495 >= sizeof(proxy->match_host))
496 yyerror("for-host too long: %s", $2);
497 (void) snprintf(proxy->match_port, sizeof(proxy->match_port),
498 "%d", $3);
499 free($2);
503 proxy_opts : /* empty */
504 | proxy_opts proxy_opt optnl
507 proxy_opt : CERT string {
508 free(proxy->cert);
509 ensure_absolute_path($2);
510 proxy->cert_path = $2;
512 | KEY string {
513 free(proxy->key);
514 ensure_absolute_path($2);
515 proxy->key_path = $2;
517 | PROTOCOLS string {
518 if (tls_config_parse_protocols(&proxy->protocols, $2) == -1)
519 yyerror("invalid protocols string \"%s\"", $2);
520 free($2);
522 | RELAY_TO string proxy_port {
523 if (strlcpy(proxy->host, $2, sizeof(proxy->host))
524 >= sizeof(proxy->host))
525 yyerror("relay-to host too long: %s", $2);
526 (void) snprintf(proxy->port, sizeof(proxy->port),
527 "%d", $3);
528 free($2);
530 | REQUIRE CLIENT CA string {
531 ensure_absolute_path($4);
532 proxy->reqca_path = $4;
534 | SNI string {
535 if (strlcpy(proxy->sni, $2, sizeof(proxy->sni))
536 >= sizeof(proxy->sni))
537 yyerror("sni hostname too long: %s", $2);
538 free($2);
540 | USE_TLS bool {
541 proxy->notls = !$2;
543 | VERIFYNAME bool {
544 proxy->noverifyname = !$2;
548 location : LOCATION { advance_loc(); } string '{' optnl locopts '}' {
549 /* drop the starting '/' if any */
550 if (*$3 == '/')
551 memmove($3, $3+1, strlen($3));
552 if (strlcpy(loc->match, $3, sizeof(loc->match))
553 >= sizeof(loc->match))
554 yyerror("location path too long: %s", $3);
555 free($3);
557 | error '}'
560 locopts : /* empty */
561 | locopts locopt optnl
564 locopt : AUTO INDEX bool { loc->auto_index = $3 ? 1 : -1; }
565 | BLOCK RETURN NUM string {
566 check_block_fmt($4);
567 if (strlcpy(loc->block_fmt, $4, sizeof(loc->block_fmt))
568 >= sizeof(loc->block_fmt))
569 yyerror("block return meta too long: %s", $4);
570 loc->block_code = check_block_code($3);
571 free($4);
573 | BLOCK RETURN NUM {
574 (void) strlcpy(loc->block_fmt, "temporary failure",
575 sizeof(loc->block_fmt));
576 loc->block_code = check_block_code($3);
577 if ($3 >= 30 && $3 < 40)
578 yyerror("missing `meta' for block return %d", $3);
580 | BLOCK {
581 (void) strlcpy(loc->block_fmt, "temporary failure",
582 sizeof(loc->block_fmt));
583 loc->block_code = 40;
585 | DEFAULT TYPE string {
586 if (strlcpy(loc->default_mime, $3,
587 sizeof(loc->default_mime))
588 >= sizeof(loc->default_mime))
589 yyerror("default type too long: %s", $3);
590 free($3);
592 | fastcgi
593 | INDEX string {
594 if (strlcpy(loc->index, $2, sizeof(loc->index))
595 >= sizeof(loc->index))
596 yyerror("index string too long: %s", $2);
597 free($2);
599 | LANG string {
600 if (strlcpy(loc->lang, $2, sizeof(loc->lang))
601 >= sizeof(loc->lang))
602 yyerror("lang too long: %s", $2);
603 free($2);
605 | LOG bool { loc->disable_log = !$2; }
606 | REQUIRE CLIENT CA string {
607 ensure_absolute_path($4);
608 loc->reqca_path = $4;
610 | ROOT string {
611 if (strlcpy(loc->dir, $2, sizeof(loc->dir))
612 >= sizeof(loc->dir))
613 yyerror("root path too long: %s", $2);
614 free($2);
616 | STRIP NUM { loc->strip = check_strip_no($2); }
619 fastcgi : FASTCGI '{' optnl fastcgiopts '}'
620 | FASTCGI fastcgiopt
621 | FASTCGI OFF {
622 loc->fcgi = -1;
623 loc->nofcgi = 1;
625 | FASTCGI string {
626 yywarn("`fastcgi path' is deprecated. "
627 "Please use `fastcgi socket path' instead.");
628 loc->fcgi = fastcgi_conf($2, NULL);
629 free($2);
633 fastcgiopts : /* empty */
634 | fastcgiopts fastcgiopt optnl
637 fastcgiopt : PARAM string '=' string {
638 add_param($2, $4);
640 | SOCKET string {
641 loc->fcgi = fastcgi_conf($2, NULL);
642 free($2);
644 | SOCKET TCP string PORT NUM {
645 char *c;
647 if (asprintf(&c, "%d", $5) == -1)
648 fatal("asprintf");
649 loc->fcgi = fastcgi_conf($3, c);
650 free($3);
651 free(c);
653 | SOCKET TCP string {
654 loc->fcgi = fastcgi_conf($3, "9000");
656 | SOCKET TCP string PORT string {
657 loc->fcgi = fastcgi_conf($3, $5);
658 free($3);
659 free($5);
661 | STRIP NUM {
662 loc->fcgi_strip = $2;
666 types : TYPES '{' optnl mediaopts_l '}' ;
668 mediaopts_l : mediaopts_l mediaoptsl nl
669 | mediaoptsl nl
672 mediaoptsl : STRING {
673 free(current_media);
674 current_media = $1;
675 } medianames_l
676 | include
679 medianames_l : medianames_l medianamesl
680 | medianamesl
683 medianamesl : numberstring {
684 if (add_mime(&conf->mime, current_media, $1) == -1)
685 fatal("add_mime");
686 free($1);
690 nl : '\n' optnl
691 | ';' optnl
694 optnl : nl
695 | /*empty*/
698 %%
700 static const struct keyword {
701 const char *word;
702 int token;
703 } keywords[] = {
704 /* these MUST be sorted */
705 {"access", ACCESS},
706 {"alias", ALIAS},
707 {"auto", AUTO},
708 {"block", BLOCK},
709 {"ca", CA},
710 {"cert", CERT},
711 {"cgi", CGI},
712 {"chroot", CHROOT},
713 {"client", CLIENT},
714 {"default", DEFAULT},
715 {"facility", FACILITY},
716 {"fastcgi", FASTCGI},
717 {"for-host", FOR_HOST},
718 {"include", INCLUDE},
719 {"index", INDEX},
720 {"ipv6", IPV6},
721 {"key", KEY},
722 {"lang", LANG},
723 {"listen", LISTEN},
724 {"location", LOCATION},
725 {"log", LOG},
726 {"ocsp", OCSP},
727 {"off", OFF},
728 {"on", ON},
729 {"param", PARAM},
730 {"port", PORT},
731 {"prefork", PREFORK},
732 {"proto", PROTO},
733 {"protocols", PROTOCOLS},
734 {"proxy", PROXY},
735 {"proxy-v1", PROXYV1},
736 {"relay-to", RELAY_TO},
737 {"require", REQUIRE},
738 {"return", RETURN},
739 {"root", ROOT},
740 {"server", SERVER},
741 {"sni", SNI},
742 {"socket", SOCKET},
743 {"strip", STRIP},
744 {"style", STYLE},
745 {"syslog", SYSLOG},
746 {"tcp", TCP},
747 {"to-ext", TOEXT},
748 {"type", TYPE},
749 {"types", TYPES},
750 {"use-tls", USE_TLS},
751 {"user", USER},
752 {"verifyname", VERIFYNAME},
753 };
755 void
756 yyerror(const char *msg, ...)
758 va_list ap;
760 file->errors++;
762 va_start(ap, msg);
763 fprintf(stderr, "%s:%d error: ", config_path, yylval.lineno);
764 vfprintf(stderr, msg, ap);
765 fprintf(stderr, "\n");
766 va_end(ap);
769 void
770 yywarn(const char *msg, ...)
772 va_list ap;
774 va_start(ap, msg);
775 fprintf(stderr, "%s:%d warning: ", config_path, yylval.lineno);
776 vfprintf(stderr, msg, ap);
777 fprintf(stderr, "\n");
778 va_end(ap);
781 int
782 kw_cmp(const void *k, const void *e)
784 return strcmp(k, ((struct keyword *)e)->word);
787 int
788 lookup(char *s)
790 const struct keyword *p;
792 p = bsearch(s, keywords, sizeof(keywords)/sizeof(keywords[0]),
793 sizeof(keywords[0]), kw_cmp);
795 if (p)
796 return p->token;
797 else
798 return STRING;
801 #define START_EXPAND 1
802 #define DONE_EXPAND 2
804 static int expanding;
806 int
807 igetc(void)
809 int c;
811 while (1) {
812 if (file->ungetpos > 0)
813 c = file->ungetbuf[--file->ungetpos];
814 else
815 c = getc(file->stream);
817 if (c == START_EXPAND)
818 expanding = 1;
819 else if (c == DONE_EXPAND)
820 expanding = 0;
821 else
822 break;
824 return c;
827 int
828 lgetc(int quotec)
830 int c, next;
832 if (quotec) {
833 if ((c = igetc()) == EOF) {
834 yyerror("reached end of file while parsing "
835 "quoted string");
836 if (file == topfile || popfile() == EOF)
837 return EOF;
838 return quotec;
840 return c;
843 while ((c = igetc()) == '\\') {
844 next = igetc();
845 if (next != '\n') {
846 c = next;
847 break;
849 yylval.lineno = file->lineno;
850 file->lineno++;
853 if (c == EOF) {
854 /*
855 * Fake EOL when hit EOF for the first time. This gets line
856 * count right if last line in included file is syntactically
857 * invalid and has no newline.
858 */
859 if (file->eof_reached == 0) {
860 file->eof_reached = 1;
861 return '\n';
863 while (c == EOF) {
864 if (file == topfile || popfile() == EOF)
865 return EOF;
866 c = igetc();
869 return c;
872 void
873 lungetc(int c)
875 if (c == EOF)
876 return;
878 if (file->ungetpos >= file->ungetsize) {
879 void *p = reallocarray(file->ungetbuf, file->ungetsize, 2);
880 if (p == NULL)
881 fatal("lungetc");
882 file->ungetbuf = p;
883 file->ungetsize *= 2;
885 file->ungetbuf[file->ungetpos++] = c;
888 int
889 findeol(void)
891 int c;
893 /* Skip to either EOF or the first real EOL. */
894 while (1) {
895 c = lgetc(0);
896 if (c == '\n') {
897 file->lineno++;
898 break;
900 if (c == EOF)
901 break;
903 return ERROR;
906 int
907 yylex(void)
909 char buf[8096];
910 char *p, *val;
911 int quotec, next, c;
912 int token;
914 top:
915 p = buf;
916 while ((c = lgetc(0)) == ' ' || c == '\t')
917 ; /* nothing */
919 yylval.lineno = file->lineno;
920 if (c == '#')
921 while ((c = lgetc(0)) != '\n' && c != EOF)
922 ; /* nothing */
923 if (c == '$' && !expanding) {
924 while (1) {
925 if ((c = lgetc(0)) == EOF)
926 return 0;
927 if (p + 1 >= buf + sizeof(buf) -1) {
928 yyerror("string too long");
929 return findeol();
931 if (isalnum(c) || c == '_') {
932 *p++ = c;
933 continue;
935 *p = '\0';
936 lungetc(c);
937 break;
939 val = symget(buf);
940 if (val == NULL) {
941 yyerror("macro `%s' not defined", buf);
942 return findeol();
944 yylval.v.string = xstrdup(val);
945 return STRING;
947 if (c == '@' && !expanding) {
948 while (1) {
949 if ((c = lgetc(0)) == EOF)
950 return 0;
952 if (p + 1 >= buf + sizeof(buf) - 1) {
953 yyerror("string too long");
954 return findeol();
956 if (isalnum(c) || c == '_') {
957 *p++ = c;
958 continue;
960 *p = '\0';
961 lungetc(c);
962 break;
964 val = symget(buf);
965 if (val == NULL) {
966 yyerror("macro '%s' not defined", buf);
967 return findeol();
969 p = val + strlen(val) - 1;
970 lungetc(DONE_EXPAND);
971 while (p >= val) {
972 lungetc(*p);
973 p--;
975 lungetc(START_EXPAND);
976 goto top;
979 switch (c) {
980 case '\'':
981 case '"':
982 quotec = c;
983 while (1) {
984 if ((c = lgetc(quotec)) == EOF)
985 return 0;
986 if (c == '\n') {
987 file->lineno++;
988 continue;
989 } else if (c == '\\') {
990 if ((next = lgetc(quotec)) == EOF)
991 return (0);
992 if (next == quotec || next == ' ' ||
993 next == '\t')
994 c = next;
995 else if (next == '\n') {
996 file->lineno++;
997 continue;
998 } else
999 lungetc(next);
1000 } else if (c == quotec) {
1001 *p = '\0';
1002 break;
1003 } else if (c == '\0') {
1004 yyerror("invalid syntax");
1005 return findeol();
1007 if (p + 1 >= buf + sizeof(buf) - 1) {
1008 yyerror("string too long");
1009 return findeol();
1011 *p++ = c;
1013 yylval.v.string = strdup(buf);
1014 if (yylval.v.string == NULL)
1015 fatal("yylex: strdup");
1016 return STRING;
1019 #define allowed_to_end_number(x) \
1020 (isspace(x) || x == ')' || x ==',' || x == '/' || x == '}' || x == '=')
1022 if (c == '-' || isdigit(c)) {
1023 do {
1024 *p++ = c;
1025 if ((size_t)(p-buf) >= sizeof(buf)) {
1026 yyerror("string too long");
1027 return findeol();
1029 } while ((c = lgetc(0)) != EOF && isdigit(c));
1030 lungetc(c);
1031 if (p == buf + 1 && buf[0] == '-')
1032 goto nodigits;
1033 if (c == EOF || allowed_to_end_number(c)) {
1034 const char *errstr = NULL;
1036 *p = '\0';
1037 yylval.v.number = strtonum(buf, LLONG_MIN,
1038 LLONG_MAX, &errstr);
1039 if (errstr) {
1040 yyerror("\"%s\" invalid number: %s",
1041 buf, errstr);
1042 return findeol();
1044 return NUM;
1045 } else {
1046 nodigits:
1047 while (p > buf + 1)
1048 lungetc(*--p);
1049 c = *--p;
1050 if (c == '-')
1051 return c;
1055 #define allowed_in_string(x) \
1056 (isalnum(x) || (ispunct(x) && x != '(' && x != ')' && \
1057 x != '{' && x != '}' && \
1058 x != '!' && x != '=' && x != '#' && \
1059 x != ',' && x != ';'))
1061 if (isalnum(c) || c == ':' || c == '_') {
1062 do {
1063 *p++ = c;
1064 if ((size_t)(p-buf) >= sizeof(buf)) {
1065 yyerror("string too long");
1066 return findeol();
1068 } while ((c = lgetc(0)) != EOF && (allowed_in_string(c)));
1069 lungetc(c);
1070 *p = '\0';
1071 if ((token = lookup(buf)) == STRING)
1072 yylval.v.string = xstrdup(buf);
1073 return token;
1075 if (c == '\n') {
1076 yylval.lineno = file->lineno;
1077 file->lineno++;
1079 if (c == EOF)
1080 return 0;
1081 return c;
1084 struct file *
1085 pushfile(const char *name, int secret)
1087 struct file *nfile;
1089 nfile = xcalloc(1, sizeof(*nfile));
1090 nfile->name = xstrdup(name);
1091 if ((nfile->stream = fopen(nfile->name, "r")) == NULL) {
1092 log_warn("can't open %s", nfile->name);
1093 free(nfile->name);
1094 free(nfile);
1095 return NULL;
1097 nfile->lineno = TAILQ_EMPTY(&files) ? 1 : 0;
1098 nfile->ungetsize = 16;
1099 nfile->ungetbuf = xcalloc(1, nfile->ungetsize);
1100 TAILQ_INSERT_TAIL(&files, nfile, entry);
1101 return nfile;
1104 int
1105 popfile(void)
1107 struct file *prev;
1109 if ((prev = TAILQ_PREV(file, files, entry)) != NULL)
1110 prev->errors += file->errors;
1112 TAILQ_REMOVE(&files, file, entry);
1113 fclose(file->stream);
1114 free(file->name);
1115 free(file->ungetbuf);
1116 free(file);
1117 file = prev;
1118 return file ? 0 : EOF;
1121 int
1122 parse_conf(struct conf *c, const char *filename)
1124 struct sym *sym, *next;
1126 default_host = NULL;
1127 default_port = 1965;
1129 conf = c;
1131 file = pushfile(filename, 0);
1132 if (file == NULL)
1133 return -1;
1134 topfile = file;
1136 yyparse();
1137 errors = file->errors;
1138 popfile();
1140 /* Free macros and check which have not been used. */
1141 TAILQ_FOREACH_SAFE(sym, &symhead, entry, next) {
1142 /* TODO: warn if !sym->used */
1143 if (!sym->persist) {
1144 free(sym->name);
1145 free(sym->val);
1146 TAILQ_REMOVE(&symhead, sym, entry);
1147 free(sym);
1151 if (errors)
1152 return -1;
1153 return 0;
1156 int
1157 symset(const char *name, const char *val, int persist)
1159 struct sym *sym;
1161 TAILQ_FOREACH(sym, &symhead, entry) {
1162 if (!strcmp(name, sym->name))
1163 break;
1166 if (sym != NULL) {
1167 if (sym->persist)
1168 return 0;
1169 else {
1170 free(sym->name);
1171 free(sym->val);
1172 TAILQ_REMOVE(&symhead, sym, entry);
1173 free(sym);
1177 sym = xcalloc(1, sizeof(*sym));
1178 sym->name = xstrdup(name);
1179 sym->val = xstrdup(val);
1180 sym->used = 0;
1181 sym->persist = persist;
1183 TAILQ_INSERT_TAIL(&symhead, sym, entry);
1184 return 0;
1187 int
1188 cmdline_symset(char *s)
1190 char *sym, *val;
1191 int ret;
1193 if ((val = strrchr(s, '=')) == NULL)
1194 return -1;
1195 sym = xcalloc(1, val - s + 1);
1196 memcpy(sym, s, val - s);
1197 ret = symset(sym, val + 1, 1);
1198 free(sym);
1199 return ret;
1202 char *
1203 symget(const char *nam)
1205 struct sym *sym;
1207 TAILQ_FOREACH(sym, &symhead, entry) {
1208 if (strcmp(nam, sym->name) == 0) {
1209 sym->used = 1;
1210 return sym->val;
1213 return NULL;
1216 char *
1217 ensure_absolute_path(char *path)
1219 if (path == NULL || *path != '/')
1220 yyerror("not an absolute path: %s", path);
1221 return path;
1224 int
1225 check_block_code(int n)
1227 if (n < 10 || n >= 70 || (n >= 20 && n <= 29))
1228 yyerror("invalid block code %d", n);
1229 return n;
1232 char *
1233 check_block_fmt(char *fmt)
1235 char *s;
1237 for (s = fmt; *s; ++s) {
1238 if (*s != '%')
1239 continue;
1240 switch (*++s) {
1241 case '%':
1242 case 'p':
1243 case 'q':
1244 case 'P':
1245 case 'N':
1246 break;
1247 default:
1248 yyerror("invalid format specifier %%%c", *s);
1252 return fmt;
1255 int
1256 check_strip_no(int n)
1258 if (n <= 0)
1259 yyerror("invalid strip number %d", n);
1260 return n;
1263 int
1264 check_prefork_num(int n)
1266 if (n <= 0 || n >= PROC_MAX_INSTANCES)
1267 yyerror("invalid prefork number %d", n);
1268 return n;
1271 void
1272 advance_loc(void)
1274 loc = new_location();
1275 TAILQ_INSERT_TAIL(&host->locations, loc, locations);
1278 void
1279 advance_proxy(void)
1281 proxy = new_proxy();
1282 TAILQ_INSERT_TAIL(&host->proxies, proxy, proxies);
1285 int
1286 fastcgi_conf(const char *path, const char *port)
1288 struct fcgi *f;
1289 int i = 0;
1291 TAILQ_FOREACH(f, &conf->fcgi, fcgi) {
1292 if (!strcmp(f->path, path) &&
1293 ((port == NULL && *f->port == '\0') ||
1294 !strcmp(f->port, port)))
1295 return i;
1296 ++i;
1299 f = xcalloc(1, sizeof(*f));
1300 f->id = i;
1301 if (strlcpy(f->path, path, sizeof(f->path)) >= sizeof(f->path))
1302 yyerror("fastcgi path is too long: %s", path);
1303 if (port != NULL &&
1304 strlcpy(f->port, port, sizeof(f->port)) >= sizeof(f->port))
1305 yyerror("port too long: %s", port);
1306 TAILQ_INSERT_TAIL(&conf->fcgi, f, fcgi);
1308 return f->id;
1311 void
1312 add_param(char *name, char *val)
1314 struct envlist *e;
1315 struct envhead *h = &loc->params;
1317 e = xcalloc(1, sizeof(*e));
1318 if (strlcpy(e->name, name, sizeof(e->name)) >= sizeof(e->name))
1319 yyerror("parameter name too long: %s", name);
1320 if (strlcpy(e->value, val, sizeof(e->value)) >= sizeof(e->value))
1321 yyerror("param value too long: %s", val);
1322 TAILQ_INSERT_TAIL(h, e, envs);
1325 int
1326 getservice(const char *n)
1328 struct servent *s;
1329 const char *errstr;
1330 long long llval;
1332 llval = strtonum(n, 0, UINT16_MAX, &errstr);
1333 if (errstr) {
1334 s = getservbyname(n, "tcp");
1335 if (s == NULL)
1336 s = getservbyname(n, "udp");
1337 if (s == NULL)
1338 return (-1);
1339 return (ntohs(s->s_port));
1342 return ((unsigned short)llval);
1345 static void
1346 add_to_addr_queue(struct addrhead *a, struct addrinfo *ai, const char *pp,
1347 int proxy)
1349 struct address *addr;
1350 struct sockaddr_in *sin;
1351 struct sockaddr_in6 *sin6;
1353 if (ai->ai_addrlen > sizeof(addr->ss))
1354 fatalx("ai_addrlen larger than a sockaddr_storage");
1356 TAILQ_FOREACH(addr, a, addrs) {
1357 if (addr->ai_flags == ai->ai_flags &&
1358 addr->ai_family == ai->ai_family &&
1359 addr->ai_socktype == ai->ai_socktype &&
1360 addr->ai_protocol == ai->ai_protocol &&
1361 addr->slen == ai->ai_addrlen &&
1362 !memcmp(&addr->ss, ai->ai_addr, addr->slen)) {
1363 if (addr->proxy != proxy)
1364 yyerror("can't specify the same listen"
1365 " address both with and without"
1366 " `proxy-v1'.");
1367 return;
1371 addr = xcalloc(1, sizeof(*addr));
1372 addr->ai_flags = ai->ai_flags;
1373 addr->ai_family = ai->ai_family;
1374 addr->ai_socktype = ai->ai_socktype;
1375 addr->ai_protocol = ai->ai_protocol;
1376 addr->slen = ai->ai_addrlen;
1377 memcpy(&addr->ss, ai->ai_addr, ai->ai_addrlen);
1378 strlcpy(addr->pp, pp, sizeof(addr->pp));
1379 addr->proxy = proxy;
1381 /* for commodity */
1382 switch (addr->ai_family) {
1383 case AF_INET:
1384 sin = (struct sockaddr_in *)&addr->ss;
1385 addr->port = ntohs(sin->sin_port);
1386 break;
1387 case AF_INET6:
1388 sin6 = (struct sockaddr_in6 *)&addr->ss;
1389 addr->port = ntohs(sin6->sin6_port);
1390 break;
1391 default:
1392 fatalx("unknown socket family %d", addr->ai_family);
1395 addr->sock = -1;
1397 TAILQ_INSERT_HEAD(a, addr, addrs);
1400 void
1401 listen_on(const char *hostname, const char *servname, int proxy)
1403 struct addrinfo hints, *res, *res0;
1404 char pp[NI_MAXHOST];
1405 int error;
1407 memset(&hints, 0, sizeof(hints));
1408 hints.ai_family = AF_UNSPEC;
1409 hints.ai_socktype = SOCK_STREAM;
1410 hints.ai_flags = AI_PASSIVE;
1411 error = getaddrinfo(hostname, servname, &hints, &res0);
1412 if (error) {
1413 yyerror("listen on \"%s\" port %s: %s", hostname, servname,
1414 gai_strerror(errno));
1415 return;
1418 for (res = res0; res; res = res->ai_next) {
1419 if (getnameinfo(res->ai_addr, res->ai_addrlen, pp, sizeof(pp),
1420 NULL, 0, NI_NUMERICHOST) == -1) {
1421 yyerror("getnameinfo failed: %s", strerror(errno));
1422 break;
1425 add_to_addr_queue(&host->addrs, res, pp, proxy);
1426 add_to_addr_queue(&conf->addrs, res, pp, proxy);
1429 freeaddrinfo(res0);