2 * Copyright (c) 2016-2019, 2020-2021 Tracey Emery <tracey@traceyemery.net>
3 * Copyright (c) 2004, 2005 Esben Norby <norby@openbsd.org>
4 * Copyright (c) 2004 Ryan McBride <mcbride@openbsd.org>
5 * Copyright (c) 2002, 2003, 2004 Henning Brauer <henning@openbsd.org>
6 * Copyright (c) 2001 Markus Friedl. All rights reserved.
7 * Copyright (c) 2001 Daniel Hartmeier. All rights reserved.
8 * Copyright (c) 2001 Theo de Raadt. All rights reserved.
10 * Permission to use, copy, modify, and distribute this software for any
11 * purpose with or without fee is hereby granted, provided that the above
12 * copyright notice and this permission notice appear in all copies.
14 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
15 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
16 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
17 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
18 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
19 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
20 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
24 #include "got_compat.h"
26 #include <sys/ioctl.h>
27 #include <sys/types.h>
28 #include <sys/queue.h>
29 #include <sys/socket.h>
33 #include <netinet/in.h>
35 #include <arpa/inet.h>
51 #include "got_sockaddr.h"
52 #include "got_reference.h"
57 TAILQ_HEAD
(files
, file
) files
= TAILQ_HEAD_INITIALIZER
(files
);
59 TAILQ_ENTRY
(file
) entry
;
65 struct file
*newfile
(const char *, int);
66 static void closefile
(struct file
*);
67 int check_file_secrecy
(int, const char *);
70 int yyerror(const char *, ...
)
71 __attribute__
((__format__
(printf
, 1, 2)))
72 __attribute__
((__nonnull__
(1)));
73 int kw_cmp
(const void *, const void *);
79 TAILQ_HEAD
(symhead
, sym
) symhead
= TAILQ_HEAD_INITIALIZER
(symhead
);
81 TAILQ_ENTRY
(sym
) entry
;
88 int symset
(const char *, const char *, int);
89 char *symget
(const char *);
93 static struct gotwebd
*gotwebd
;
94 static struct server
*new_srv
;
95 static struct server
*conf_new_server
(const char *);
96 int getservice
(const char *);
99 int get_addrs
(const char *, struct server
*, in_port_t
);
100 int addr_dup_check
(struct addresslist
*, struct address
*,
101 const char *, const char *);
102 int add_addr
(struct server
*, struct address
*);
103 struct address
*host_v4
(const char *);
104 struct address
*host_v6
(const char *);
105 int host_dns
(const char *, struct server
*,
106 int, in_port_t
, const char *, int);
107 int host_if
(const char *, struct server
*,
108 int, in_port_t
, const char *, int);
109 int host
(const char *, struct server
*,
110 int, in_port_t
, const char *, int);
111 int is_if_in_group
(const char *, const char *);
124 %token LISTEN WWW_PATH MAX_REPOS SITE_NAME SITE_OWNER SITE_LINK LOGO
125 %token LOGO_URL SHOW_REPO_OWNER SHOW_REPO_AGE SHOW_REPO_DESCRIPTION
126 %token MAX_REPOS_DISPLAY REPOS_PATH MAX_COMMITS_DISPLAY ON ERROR
127 %token SHOW_SITE_OWNER SHOW_REPO_CLONEURL PORT PREFORK RESPECT_EXPORTOK
128 %token UNIX_SOCKET UNIX_SOCKET_NAME SERVER CHROOT CUSTOM_CSS SOCKET
130 %token
<v.
string> STRING
131 %type
<v.port
> fcgiport
132 %token
<v.number
> NUMBER
133 %type
<v.number
> boolean
137 grammar
: /* empty */
139 | grammar varset
'\n'
141 | grammar server
'\n'
142 | grammar
error '\n' { file
->errors
++; }
145 varset
: STRING
'=' STRING
{
148 if
(isspace
((unsigned char)*s
)) {
149 yyerror("macro name cannot contain "
156 if
(symset
($1, $3, 0) == -1)
157 fatal
("cannot store variable");
164 if
(strcasecmp
($1, "1") == 0 ||
165 strcasecmp
($1, "yes") == 0 ||
166 strcasecmp
($1, "on") == 0)
168 else if
(strcasecmp
($1, "0") == 0 ||
169 strcasecmp
($1, "off") == 0 ||
170 strcasecmp
($1, "no") == 0)
173 yyerror("invalid boolean value '%s'", $1);
180 | NUMBER
{ $$
= $1; }
183 fcgiport
: PORT NUMBER
{
184 if
($2 <= 0 ||
$2 > (int)USHRT_MAX
) {
185 yyerror("invalid port: %lld", $2);
193 if
((val
= getservice
($2)) == -1) {
194 yyerror("invalid port: %s", $2);
204 main
: PREFORK NUMBER
{
205 gotwebd
->prefork_gotwebd
= $2;
208 n
= strlcpy
(gotwebd
->httpd_chroot
, $2,
209 sizeof
(gotwebd
->httpd_chroot
));
210 if
(n
>= sizeof
(gotwebd
->httpd_chroot
)) {
211 yyerror("%s: httpd_chroot truncated", __func__
);
217 | UNIX_SOCKET boolean
{
218 gotwebd
->unix_socket
= $2;
220 | UNIX_SOCKET_NAME STRING
{
221 n
= snprintf
(gotwebd
->unix_socket_name
,
222 sizeof
(gotwebd
->unix_socket_name
), "%s%s",
223 strlen
(gotwebd
->httpd_chroot
) ?
224 gotwebd
->httpd_chroot
: D_HTTPD_CHROOT
, $2);
226 (size_t)n
>= sizeof
(gotwebd
->unix_socket_name
)) {
227 yyerror("%s: unix_socket_name truncated",
236 server
: SERVER STRING
{
239 TAILQ_FOREACH
(srv
, &gotwebd
->servers
, entry
) {
240 if
(strcmp
(srv
->name
, $2) == 0) {
241 yyerror("server name exists '%s'", $2);
247 new_srv
= conf_new_server
($2);
248 log_debug
("adding server %s", $2);
254 TAILQ_FOREACH
(srv
, &gotwebd
->servers
, entry
) {
255 if
(strcmp
(srv
->name
, $2) == 0) {
256 yyerror("server name exists '%s'", $2);
262 new_srv
= conf_new_server
($2);
263 log_debug
("adding server %s", $2);
265 } '{' optnl serveropts2
'}' {
269 serveropts1
: REPOS_PATH STRING
{
270 n
= strlcpy
(new_srv
->repos_path
, $2,
271 sizeof
(new_srv
->repos_path
));
272 if
(n
>= sizeof
(new_srv
->repos_path
)) {
273 yyerror("%s: repos_path truncated", __func__
);
280 n
= strlcpy
(new_srv
->site_name
, $2,
281 sizeof
(new_srv
->site_name
));
282 if
(n
>= sizeof
(new_srv
->site_name
)) {
283 yyerror("%s: site_name truncated", __func__
);
289 | SITE_OWNER STRING
{
290 n
= strlcpy
(new_srv
->site_owner
, $2,
291 sizeof
(new_srv
->site_owner
));
292 if
(n
>= sizeof
(new_srv
->site_owner
)) {
293 yyerror("%s: site_owner truncated", __func__
);
300 n
= strlcpy
(new_srv
->site_link
, $2,
301 sizeof
(new_srv
->site_link
));
302 if
(n
>= sizeof
(new_srv
->site_link
)) {
303 yyerror("%s: site_link truncated", __func__
);
310 n
= strlcpy
(new_srv
->logo
, $2, sizeof
(new_srv
->logo
));
311 if
(n
>= sizeof
(new_srv
->logo
)) {
312 yyerror("%s: logo truncated", __func__
);
319 n
= strlcpy
(new_srv
->logo_url
, $2,
320 sizeof
(new_srv
->logo_url
));
321 if
(n
>= sizeof
(new_srv
->logo_url
)) {
322 yyerror("%s: logo_url truncated", __func__
);
328 | CUSTOM_CSS STRING
{
329 n
= strlcpy
(new_srv
->custom_css
, $2,
330 sizeof
(new_srv
->custom_css
));
331 if
(n
>= sizeof
(new_srv
->custom_css
)) {
332 yyerror("%s: custom_css truncated", __func__
);
338 | LISTEN ON STRING fcgiport
{
339 if
(get_addrs
($3, new_srv
, $4) == -1) {
340 yyerror("could not get addrs");
343 new_srv
->fcgi_socket
= 1;
345 | LISTEN ON SOCKET STRING
{
346 if
(!strcasecmp
($4, "off") ||
347 !strcasecmp
($4, "no")) {
348 new_srv
->unix_socket
= 0;
353 new_srv
->unix_socket
= 1;
355 n
= snprintf
(new_srv
->unix_socket_name
,
356 sizeof
(new_srv
->unix_socket_name
), "%s%s",
357 strlen
(gotwebd
->httpd_chroot
) ?
358 gotwebd
->httpd_chroot
: D_HTTPD_CHROOT
, $4);
360 (size_t)n
>= sizeof
(new_srv
->unix_socket_name
)) {
361 yyerror("%s: unix_socket_name truncated",
370 new_srv
->max_repos
= $2;
372 | SHOW_SITE_OWNER boolean
{
373 new_srv
->show_site_owner
= $2;
375 | SHOW_REPO_OWNER boolean
{
376 new_srv
->show_repo_owner
= $2;
378 | SHOW_REPO_AGE boolean
{
379 new_srv
->show_repo_age
= $2;
381 | SHOW_REPO_DESCRIPTION boolean
{
382 new_srv
->show_repo_description
= $2;
384 | SHOW_REPO_CLONEURL boolean
{
385 new_srv
->show_repo_cloneurl
= $2;
387 | RESPECT_EXPORTOK boolean
{
388 new_srv
->respect_exportok
= $2;
390 | MAX_REPOS_DISPLAY NUMBER
{
391 new_srv
->max_repos_display
= $2;
393 | MAX_COMMITS_DISPLAY NUMBER
{
395 new_srv
->max_commits_display
= $2;
399 serveropts2
: serveropts2 serveropts1 nl
406 optnl
: '\n' optnl
/* zero or more newlines */
418 yyerror(const char *fmt
, ...
)
425 if
(vasprintf
(&msg
, fmt
, ap
) == -1)
426 fatalx
("yyerror vasprintf");
428 logit
(LOG_CRIT
, "%s:%d: %s", file
->name
, yylval.lineno
, msg
);
434 kw_cmp
(const void *k
, const void *e
)
436 return
(strcmp
(k
, ((const struct keywords
*)e
)->k_name
));
442 /* This has to be sorted always. */
443 static const struct keywords keywords
[] = {
444 { "chroot", CHROOT
},
445 { "custom_css", CUSTOM_CSS
},
446 { "listen", LISTEN
},
448 { "logo_url", LOGO_URL
},
449 { "max_commits_display", MAX_COMMITS_DISPLAY
},
450 { "max_repos", MAX_REPOS
},
451 { "max_repos_display", MAX_REPOS_DISPLAY
},
454 { "prefork", PREFORK
},
455 { "repos_path", REPOS_PATH
},
456 { "respect_exportok", RESPECT_EXPORTOK
},
457 { "server", SERVER
},
458 { "show_repo_age", SHOW_REPO_AGE
},
459 { "show_repo_cloneurl", SHOW_REPO_CLONEURL
},
460 { "show_repo_description", SHOW_REPO_DESCRIPTION
},
461 { "show_repo_owner", SHOW_REPO_OWNER
},
462 { "show_site_owner", SHOW_SITE_OWNER
},
463 { "site_link", SITE_LINK
},
464 { "site_name", SITE_NAME
},
465 { "site_owner", SITE_OWNER
},
466 { "socket", SOCKET
},
467 { "unix_socket", UNIX_SOCKET
},
468 { "unix_socket_name", UNIX_SOCKET_NAME
},
470 const struct keywords
*p
;
472 p
= bsearch
(s
, keywords
, sizeof
(keywords
)/sizeof
(keywords
[0]),
473 sizeof
(keywords
[0]), kw_cmp
);
481 #define MAXPUSHBACK 128
483 unsigned char *parsebuf
;
485 unsigned char pushback_buffer
[MAXPUSHBACK
];
486 int pushback_index
= 0;
494 /* Read character from the parsebuffer instead of input. */
495 if
(parseindex
>= 0) {
496 c
= parsebuf
[parseindex
++];
505 return
(pushback_buffer
[--pushback_index
]);
508 c
= getc
(file
->stream
);
510 yyerror("reached end of file while parsing "
515 c
= getc
(file
->stream
);
517 next
= getc
(file
->stream
);
522 yylval.lineno
= file
->lineno
;
524 c
= getc
(file
->stream
);
540 if
(pushback_index
< MAXPUSHBACK
-1)
541 return
(pushback_buffer
[pushback_index
++] = c
);
553 /* Skip to either EOF or the first real EOL. */
556 c
= pushback_buffer
[--pushback_index
];
572 unsigned char buf
[8096];
573 unsigned char *p
, *val
;
580 while
(c
== ' ' || c
== '\t')
581 c
= lgetc
(0); /* nothing */
583 yylval.lineno
= file
->lineno
;
586 while
(c
!= '\n' && c
!= EOF
)
587 c
= lgetc
(0); /* nothing */
589 if
(c
== '$' && parsebuf
== NULL
) {
595 if
(p
+ 1 >= buf
+ sizeof
(buf
) - 1) {
596 yyerror("string too long");
599 if
(isalnum
(c
) || c
== '_') {
609 yyerror("macro '%s' not defined", buf
);
628 } else if
(c
== '\\') {
629 next
= lgetc
(quotec
);
632 if
(next
== quotec || c
== ' ' || c
== '\t')
634 else if
(next
== '\n') {
639 } else if
(c
== quotec
) {
642 } else if
(c
== '\0') {
643 yyerror("syntax error");
646 if
(p
+ 1 >= buf
+ sizeof
(buf
) - 1) {
647 yyerror("string too long");
652 yylval.v.
string = strdup
(buf
);
653 if
(yylval.v.
string == NULL
)
654 err
(1, "yylex: strdup");
658 #define allowed_to_end_number(x) \
659 (isspace
(x
) || x
== ')' || x
==',' || x
== '/' || x
== '}' || x
== '=')
661 if
(c
== '-' || isdigit
(c
)) {
664 if
((unsigned)(p
-buf
) >= sizeof
(buf
)) {
665 yyerror("string too long");
669 } while
(c
!= EOF
&& isdigit
(c
));
671 if
(p
== buf
+ 1 && buf
[0] == '-')
673 if
(c
== EOF || allowed_to_end_number
(c
)) {
674 const char *errstr
= NULL
;
677 yylval.v.number
= strtonum
(buf
, LLONG_MIN
,
680 yyerror("\"%s\" invalid number: %s",
695 #define allowed_in_string(x) \
696 (isalnum
(x
) ||
(ispunct
(x
) && x
!= '(' && x
!= ')' && \
697 x
!= '{' && x
!= '}' && \
698 x
!= '!' && x
!= '=' && x
!= '#' && \
701 if
(isalnum
(c
) || c
== ':' || c
== '_') {
704 if
((unsigned)(p
-buf
) >= sizeof
(buf
)) {
705 yyerror("string too long");
709 } while
(c
!= EOF
&& (allowed_in_string
(c
)));
713 if
(token
== STRING
) {
714 yylval.v.
string = strdup
(buf
);
715 if
(yylval.v.
string == NULL
)
716 err
(1, "yylex: strdup");
721 yylval.lineno
= file
->lineno
;
730 check_file_secrecy
(int fd
, const char *fname
)
734 if
(fstat
(fd
, &st
)) {
735 log_warn
("cannot stat %s", fname
);
738 if
(st.st_uid
!= 0 && st.st_uid
!= getuid
()) {
739 log_warnx
("%s: owner not root or current user", fname
);
742 if
(st.st_mode
& (S_IWGRP | S_IXGRP | S_IRWXO
)) {
743 log_warnx
("%s: group writable or world read/writable", fname
);
750 newfile
(const char *name
, int secret
)
754 nfile
= calloc
(1, sizeof
(struct file
));
759 nfile
->name
= strdup
(name
);
760 if
(nfile
->name
== NULL
) {
765 nfile
->stream
= fopen
(nfile
->name
, "r");
766 if
(nfile
->stream
== NULL
) {
767 /* no warning, we don't require a conf file */
772 check_file_secrecy
(fileno
(nfile
->stream
), nfile
->name
)) {
773 fclose
(nfile
->stream
);
783 closefile
(struct file
*xfile
)
785 fclose
(xfile
->stream
);
791 add_default_server
(void)
793 new_srv
= conf_new_server
(D_SITENAME
);
794 log_debug
("%s: adding default server %s", __func__
, D_SITENAME
);
798 parse_config
(const char *filename
, struct gotwebd
*env
)
800 struct sym
*sym
, *next
;
802 if
(config_init
(env
) == -1)
803 fatalx
("failed to initialize configuration");
807 file
= newfile
(filename
, 0);
809 add_default_server
();
810 sockets_parse_sockets
(env
);
811 /* just return, as we don't require a conf file */
816 errors
= file
->errors
;
819 /* Free macros and check which have not been used. */
820 TAILQ_FOREACH_SAFE
(sym
, &symhead
, entry
, next
) {
821 if
((gotwebd
->gotwebd_verbose
> 1) && !sym
->used
)
822 fprintf
(stderr
, "warning: macro '%s' not used\n",
827 TAILQ_REMOVE
(&symhead
, sym
, entry
);
835 /* just add default server if no config specified */
836 if
(gotwebd
->server_cnt
== 0)
837 add_default_server
();
839 /* setup our listening sockets */
840 sockets_parse_sockets
(env
);
846 conf_new_server
(const char *name
)
848 struct server
*srv
= NULL
;
850 srv
= calloc
(1, sizeof
(*srv
));
852 fatalx
("%s: calloc", __func__
);
854 n
= strlcpy
(srv
->name
, name
, sizeof
(srv
->name
));
855 if
(n
>= sizeof
(srv
->name
))
856 fatalx
("%s: strlcpy", __func__
);
857 n
= snprintf
(srv
->unix_socket_name
,
858 sizeof
(srv
->unix_socket_name
), "%s%s", D_HTTPD_CHROOT
,
860 if
(n
< 0 ||
(size_t)n
>= sizeof
(srv
->unix_socket_name
))
861 fatalx
("%s: snprintf", __func__
);
862 n
= strlcpy
(srv
->repos_path
, D_GOTPATH
,
863 sizeof
(srv
->repos_path
));
864 if
(n
>= sizeof
(srv
->repos_path
))
865 fatalx
("%s: strlcpy", __func__
);
866 n
= strlcpy
(srv
->site_name
, D_SITENAME
,
867 sizeof
(srv
->site_name
));
868 if
(n
>= sizeof
(srv
->site_name
))
869 fatalx
("%s: strlcpy", __func__
);
870 n
= strlcpy
(srv
->site_owner
, D_SITEOWNER
,
871 sizeof
(srv
->site_owner
));
872 if
(n
>= sizeof
(srv
->site_owner
))
873 fatalx
("%s: strlcpy", __func__
);
874 n
= strlcpy
(srv
->site_link
, D_SITELINK
,
875 sizeof
(srv
->site_link
));
876 if
(n
>= sizeof
(srv
->site_link
))
877 fatalx
("%s: strlcpy", __func__
);
878 n
= strlcpy
(srv
->logo
, D_GOTLOGO
,
880 if
(n
>= sizeof
(srv
->logo
))
881 fatalx
("%s: strlcpy", __func__
);
882 n
= strlcpy
(srv
->logo_url
, D_GOTURL
, sizeof
(srv
->logo_url
));
883 if
(n
>= sizeof
(srv
->logo_url
))
884 fatalx
("%s: strlcpy", __func__
);
885 n
= strlcpy
(srv
->custom_css
, D_GOTWEBCSS
, sizeof
(srv
->custom_css
));
886 if
(n
>= sizeof
(srv
->custom_css
))
887 fatalx
("%s: strlcpy", __func__
);
889 srv
->show_site_owner
= D_SHOWSOWNER
;
890 srv
->show_repo_owner
= D_SHOWROWNER
;
891 srv
->show_repo_age
= D_SHOWAGE
;
892 srv
->show_repo_description
= D_SHOWDESC
;
893 srv
->show_repo_cloneurl
= D_SHOWURL
;
894 srv
->respect_exportok
= D_RESPECTEXPORTOK
;
896 srv
->max_repos_display
= D_MAXREPODISP
;
897 srv
->max_commits_display
= D_MAXCOMMITDISP
;
898 srv
->max_repos
= D_MAXREPO
;
900 srv
->unix_socket
= 1;
901 srv
->fcgi_socket
= 0;
903 TAILQ_INIT
(&srv
->al
);
904 TAILQ_INSERT_TAIL
(&gotwebd
->servers
, srv
, entry
);
905 gotwebd
->server_cnt
++;
911 symset
(const char *nam
, const char *val
, int persist
)
915 TAILQ_FOREACH
(sym
, &symhead
, entry
) {
916 if
(strcmp
(nam
, sym
->nam
) == 0)
921 if
(sym
->persist
== 1)
926 TAILQ_REMOVE
(&symhead
, sym
, entry
);
930 sym
= calloc
(1, sizeof
(*sym
));
934 sym
->nam
= strdup
(nam
);
935 if
(sym
->nam
== NULL
) {
939 sym
->val
= strdup
(val
);
940 if
(sym
->val
== NULL
) {
946 sym
->persist
= persist
;
947 TAILQ_INSERT_TAIL
(&symhead
, sym
, entry
);
952 cmdline_symset
(char *s
)
957 val
= strrchr
(s
, '=');
961 sym
= strndup
(s
, val
- s
);
963 fatal
("%s: strndup", __func__
);
965 ret
= symset
(sym
, val
+ 1, 1);
972 symget
(const char *nam
)
976 TAILQ_FOREACH
(sym
, &symhead
, entry
) {
977 if
(strcmp
(nam
, sym
->nam
) == 0) {
986 getservice
(const char *n
)
992 llval
= strtonum
(n
, 0, UINT16_MAX
, &errstr
);
994 s
= getservbyname
(n
, "tcp");
996 s
= getservbyname
(n
, "udp");
999 return ntohs
(s
->s_port
);
1002 return
(unsigned short)llval
;
1006 host_v4
(const char *s
)
1009 struct sockaddr_in
*sain
;
1012 memset
(&ina
, 0, sizeof
(ina
));
1013 if
(inet_pton
(AF_INET
, s
, &ina
) != 1)
1016 if
((h
= calloc
(1, sizeof
(*h
))) == NULL
)
1018 sain
= (struct sockaddr_in
*)&h
->ss
;
1019 got_sockaddr_inet_init
(sain
, &ina
);
1020 if
(sain
->sin_addr.s_addr
== INADDR_ANY
)
1021 h
->prefixlen
= 0; /* 0.0.0.0 address */
1023 h
->prefixlen
= -1; /* host address */
1028 host_v6
(const char *s
)
1030 struct addrinfo hints
, *res
;
1031 struct sockaddr_in6
*sa_in6
, *ra
;
1032 struct address
*h
= NULL
;
1034 memset
(&hints
, 0, sizeof
(hints
));
1035 hints.ai_family
= AF_INET6
;
1036 hints.ai_socktype
= SOCK_DGRAM
; /* dummy */
1037 hints.ai_flags
= AI_NUMERICHOST
;
1038 if
(getaddrinfo
(s
, "0", &hints
, &res
) == 0) {
1039 if
((h
= calloc
(1, sizeof
(*h
))) == NULL
)
1041 sa_in6
= (struct sockaddr_in6
*)&h
->ss
;
1042 ra
= (struct sockaddr_in6
*)res
->ai_addr
;
1043 got_sockaddr_inet6_init
(sa_in6
, &ra
->sin6_addr
,
1045 if
(memcmp
(&sa_in6
->sin6_addr
, &in6addr_any
,
1046 sizeof
(sa_in6
->sin6_addr
)) == 0)
1047 h
->prefixlen
= 0; /* any address */
1049 h
->prefixlen
= -1; /* host address */
1057 host_dns
(const char *s
, struct server
*new_srv
, int max
,
1058 in_port_t port
, const char *ifname
, int ipproto
)
1060 struct addrinfo hints
, *res0
, *res
;
1062 struct sockaddr_in
*sain
;
1063 struct sockaddr_in6
*sin6
;
1066 if
((cnt
= host_if
(s
, new_srv
, max
, port
, ifname
, ipproto
)) != 0)
1069 memset
(&hints
, 0, sizeof
(hints
));
1070 hints.ai_family
= PF_UNSPEC
;
1071 hints.ai_socktype
= SOCK_DGRAM
; /* DUMMY */
1072 hints.ai_flags
= AI_ADDRCONFIG
;
1073 error = getaddrinfo
(s
, NULL
, &hints
, &res0
);
1074 if
(error == EAI_AGAIN ||
error == EAI_NODATA ||
error == EAI_NONAME
)
1077 log_warnx
("%s: could not parse \"%s\": %s", __func__
, s
,
1078 gai_strerror
(error));
1082 for
(res
= res0
; res
&& cnt
< max
; res
= res
->ai_next
) {
1083 if
(res
->ai_family
!= AF_INET
&&
1084 res
->ai_family
!= AF_INET6
)
1086 if
((h
= calloc
(1, sizeof
(*h
))) == NULL
)
1091 if
(ifname
!= NULL
) {
1092 if
(strlcpy
(h
->ifname
, ifname
, sizeof
(h
->ifname
)) >=
1093 sizeof
(h
->ifname
)) {
1094 log_warnx
("%s: interface name truncated",
1102 h
->ipproto
= ipproto
;
1103 h
->ss.ss_family
= res
->ai_family
;
1104 h
->prefixlen
= -1; /* host address */
1106 if
(res
->ai_family
== AF_INET
) {
1107 struct sockaddr_in
*ra
;
1108 sain
= (struct sockaddr_in
*)&h
->ss
;
1109 ra
= (struct sockaddr_in
*)res
->ai_addr
;
1110 got_sockaddr_inet_init
(sain
, &ra
->sin_addr
);
1112 struct sockaddr_in6
*ra
;
1113 sin6
= (struct sockaddr_in6
*)&h
->ss
;
1114 ra
= (struct sockaddr_in6
*)res
->ai_addr
;
1115 got_sockaddr_inet6_init
(sin6
, &ra
->sin6_addr
, 0);
1118 if
(add_addr
(new_srv
, h
))
1122 if
(cnt
== max
&& res
) {
1123 log_warnx
("%s: %s resolves to more than %d hosts", __func__
,
1131 host_if
(const char *s
, struct server
*new_srv
, int max
,
1132 in_port_t port
, const char *ifname
, int ipproto
)
1134 struct ifaddrs
*ifap
, *p
;
1135 struct sockaddr_in
*sain
;
1136 struct sockaddr_in6
*sin6
;
1140 if
(getifaddrs
(&ifap
) == -1)
1141 fatal
("getifaddrs");
1143 /* First search for IPv4 addresses */
1147 for
(p
= ifap
; p
!= NULL
&& cnt
< max
; p
= p
->ifa_next
) {
1148 if
(p
->ifa_addr
== NULL ||
1149 p
->ifa_addr
->sa_family
!= af ||
1150 (strcmp
(s
, p
->ifa_name
) != 0 &&
1151 !is_if_in_group
(p
->ifa_name
, s
)))
1153 if
((h
= calloc
(1, sizeof
(*h
))) == NULL
)
1158 if
(ifname
!= NULL
) {
1159 if
(strlcpy
(h
->ifname
, ifname
, sizeof
(h
->ifname
)) >=
1160 sizeof
(h
->ifname
)) {
1161 log_warnx
("%s: interface name truncated",
1169 h
->ipproto
= ipproto
;
1170 h
->ss.ss_family
= af
;
1171 h
->prefixlen
= -1; /* host address */
1173 if
(af
== AF_INET
) {
1174 struct sockaddr_in
*ra
;
1175 sain
= (struct sockaddr_in
*)&h
->ss
;
1176 ra
= (struct sockaddr_in
*)p
->ifa_addr
;
1177 got_sockaddr_inet_init
(sain
, &ra
->sin_addr
);
1179 struct sockaddr_in6
*ra
;
1180 sin6
= (struct sockaddr_in6
*)&h
->ss
;
1181 ra
= (struct sockaddr_in6
*)p
->ifa_addr
;
1182 got_sockaddr_inet6_init
(sin6
, &ra
->sin6_addr
,
1186 if
(add_addr
(new_srv
, h
))
1190 if
(af
== AF_INET
) {
1191 /* Next search for IPv6 addresses */
1197 log_warnx
("%s: %s resolves to more than %d hosts", __func__
,
1205 host
(const char *s
, struct server
*new_srv
, int max
,
1206 in_port_t port
, const char *ifname
, int ipproto
)
1219 if
(ifname
!= NULL
) {
1220 if
(strlcpy
(h
->ifname
, ifname
, sizeof
(h
->ifname
)) >=
1221 sizeof
(h
->ifname
)) {
1222 log_warnx
("%s: interface name truncated",
1229 h
->ipproto
= ipproto
;
1231 if
(add_addr
(new_srv
, h
))
1236 return
(host_dns
(s
, new_srv
, max
, port
, ifname
, ipproto
));
1240 is_if_in_group
(const char *ifname
, const char *groupname
)
1242 /* TA: Check this... */
1243 #ifdef HAVE_STRUCT_IFGROUPREQ
1245 struct ifgroupreq ifgr
;
1246 struct ifg_req
*ifg
;
1250 if
((s
= socket
(AF_INET
, SOCK_DGRAM
, 0)) == -1)
1253 memset
(&ifgr
, 0, sizeof
(ifgr
));
1254 if
(strlcpy
(ifgr.ifgr_name
, ifname
, IFNAMSIZ
) >= IFNAMSIZ
)
1256 if
(ioctl
(s
, SIOCGIFGROUP
, (caddr_t
)&ifgr
) == -1) {
1257 if
(errno
== EINVAL || errno
== ENOTTY
)
1259 err
(1, "SIOCGIFGROUP");
1262 len
= ifgr.ifgr_len
;
1263 ifgr.ifgr_groups
= calloc
(len
/ sizeof
(struct ifg_req
),
1264 sizeof
(struct ifg_req
));
1265 if
(ifgr.ifgr_groups
== NULL
)
1266 err
(1, "getifgroups");
1267 if
(ioctl
(s
, SIOCGIFGROUP
, (caddr_t
)&ifgr
) == -1)
1268 err
(1, "SIOCGIFGROUP");
1270 ifg
= ifgr.ifgr_groups
;
1271 for
(; ifg
&& len
>= sizeof
(struct ifg_req
); ifg
++) {
1272 len
-= sizeof
(struct ifg_req
);
1273 if
(strcmp
(ifg
->ifgrq_group
, groupname
) == 0) {
1278 free
(ifgr.ifgr_groups
);
1289 get_addrs
(const char *addr
, struct server
*new_srv
, in_port_t port
)
1291 if
(strcmp
("", addr
) == 0) {
1292 if
(host
("127.0.0.1", new_srv
, 1, port
, "127.0.0.1",
1294 yyerror("invalid listen ip: %s",
1298 if
(host
("::1", new_srv
, 1, port
, "::1", -1) <= 0) {
1299 yyerror("invalid listen ip: %s", "::1");
1303 if
(host
(addr
, new_srv
, GOTWEBD_MAXIFACE
, port
, addr
,
1305 yyerror("invalid listen ip: %s", addr
);
1313 addr_dup_check
(struct addresslist
*al
, struct address
*h
, const char *new_srv
,
1314 const char *other_srv
)
1318 char buf
[INET6_ADDRSTRLEN
];
1319 const char *addrstr
;
1321 TAILQ_FOREACH
(a
, al
, entry
) {
1322 if
(memcmp
(&a
->ss
, &h
->ss
, sizeof
(h
->ss
)) != 0 ||
1326 switch
(h
->ss.ss_family
) {
1328 ia
= &((struct sockaddr_in
*)(&h
->ss
))->sin_addr
;
1331 ia
= &((struct sockaddr_in6
*)(&h
->ss
))->sin6_addr
;
1334 yyerror("unknown address family: %d", h
->ss.ss_family
);
1337 addrstr
= inet_ntop
(h
->ss.ss_family
, ia
, buf
, sizeof
(buf
));
1340 yyerror("server %s: duplicate fcgi listen "
1341 "address %s:%d, already used by server %s",
1342 new_srv
, addrstr
, h
->port
, other_srv
);
1344 log_warnx
("server: %s: duplicate fcgi listen "
1345 "address %s:%d", new_srv
, addrstr
, h
->port
);
1349 yyerror("server: %s: duplicate fcgi listen "
1350 "address, already used by server %s",
1351 new_srv
, other_srv
);
1353 log_warnx
("server %s: duplicate fcgi listen "
1354 "address", new_srv
);
1365 add_addr
(struct server
*new_srv
, struct address
*h
)
1369 /* Address cannot be shared between different servers. */
1370 TAILQ_FOREACH
(srv
, &gotwebd
->servers
, entry
) {
1373 if
(addr_dup_check
(&srv
->al
, h
, new_srv
->name
, srv
->name
))
1377 /* Tolerate duplicate address lines within the scope of a server. */
1378 if
(addr_dup_check
(&new_srv
->al
, h
, NULL
, NULL
) == 0)
1379 TAILQ_INSERT_TAIL
(&new_srv
->al
, h
, entry
);