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 int host
(const char *, struct server
*,
104 int, in_port_t
, const char *, int);
105 int host_if
(const char *, struct server
*,
106 int, in_port_t
, const char *, int);
107 int is_if_in_group
(const char *, const char *);
120 %token LISTEN WWW_PATH MAX_REPOS SITE_NAME SITE_OWNER SITE_LINK LOGO
121 %token LOGO_URL SHOW_REPO_OWNER SHOW_REPO_AGE SHOW_REPO_DESCRIPTION
122 %token MAX_REPOS_DISPLAY REPOS_PATH MAX_COMMITS_DISPLAY ON ERROR
123 %token SHOW_SITE_OWNER SHOW_REPO_CLONEURL PORT PREFORK RESPECT_EXPORTOK
124 %token UNIX_SOCKET UNIX_SOCKET_NAME SERVER CHROOT CUSTOM_CSS SOCKET
126 %token
<v.
string> STRING
127 %type
<v.port
> fcgiport
128 %token
<v.number
> NUMBER
129 %type
<v.number
> boolean
133 grammar
: /* empty */
135 | grammar varset
'\n'
137 | grammar server
'\n'
138 | grammar
error '\n' { file
->errors
++; }
141 varset
: STRING
'=' STRING
{
144 if
(isspace
((unsigned char)*s
)) {
145 yyerror("macro name cannot contain "
152 if
(symset
($1, $3, 0) == -1)
153 fatal
("cannot store variable");
160 if
(strcasecmp
($1, "1") == 0 ||
161 strcasecmp
($1, "on") == 0)
163 else if
(strcasecmp
($1, "0") == 0 ||
164 strcasecmp
($1, "off") == 0)
167 yyerror("invalid boolean value '%s'", $1);
175 if
($1 != 0 && $1 != 1) {
176 yyerror("invalid boolean value '%lld'", $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 if
($2 <= 0 ||
$2 > PROC_MAX_INSTANCES
) {
206 yyerror("prefork is %s: %lld",
207 $2 <= 0 ?
"too small" : "too large", $2);
210 gotwebd
->prefork_gotwebd
= $2;
214 yyerror("chroot path can't be an empty"
220 n
= strlcpy
(gotwebd
->httpd_chroot
, $2,
221 sizeof
(gotwebd
->httpd_chroot
));
222 if
(n
>= sizeof
(gotwebd
->httpd_chroot
)) {
223 yyerror("%s: httpd_chroot truncated", __func__
);
229 | UNIX_SOCKET boolean
{
230 gotwebd
->unix_socket
= $2;
232 | UNIX_SOCKET_NAME STRING
{
233 n
= snprintf
(gotwebd
->unix_socket_name
,
234 sizeof
(gotwebd
->unix_socket_name
), "%s%s",
235 gotwebd
->httpd_chroot
, $2);
237 (size_t)n
>= sizeof
(gotwebd
->unix_socket_name
)) {
238 yyerror("%s: unix_socket_name truncated",
247 server
: SERVER STRING
{
250 TAILQ_FOREACH
(srv
, &gotwebd
->servers
, entry
) {
251 if
(strcmp
(srv
->name
, $2) == 0) {
252 yyerror("server name exists '%s'", $2);
258 new_srv
= conf_new_server
($2);
259 log_debug
("adding server %s", $2);
265 TAILQ_FOREACH
(srv
, &gotwebd
->servers
, entry
) {
266 if
(strcmp
(srv
->name
, $2) == 0) {
267 yyerror("server name exists '%s'", $2);
273 new_srv
= conf_new_server
($2);
274 log_debug
("adding server %s", $2);
276 } '{' optnl serveropts2
'}' {
280 serveropts1
: REPOS_PATH STRING
{
281 n
= strlcpy
(new_srv
->repos_path
, $2,
282 sizeof
(new_srv
->repos_path
));
283 if
(n
>= sizeof
(new_srv
->repos_path
)) {
284 yyerror("%s: repos_path truncated", __func__
);
291 n
= strlcpy
(new_srv
->site_name
, $2,
292 sizeof
(new_srv
->site_name
));
293 if
(n
>= sizeof
(new_srv
->site_name
)) {
294 yyerror("%s: site_name truncated", __func__
);
300 | SITE_OWNER STRING
{
301 n
= strlcpy
(new_srv
->site_owner
, $2,
302 sizeof
(new_srv
->site_owner
));
303 if
(n
>= sizeof
(new_srv
->site_owner
)) {
304 yyerror("%s: site_owner truncated", __func__
);
311 n
= strlcpy
(new_srv
->site_link
, $2,
312 sizeof
(new_srv
->site_link
));
313 if
(n
>= sizeof
(new_srv
->site_link
)) {
314 yyerror("%s: site_link truncated", __func__
);
321 n
= strlcpy
(new_srv
->logo
, $2, sizeof
(new_srv
->logo
));
322 if
(n
>= sizeof
(new_srv
->logo
)) {
323 yyerror("%s: logo truncated", __func__
);
330 n
= strlcpy
(new_srv
->logo_url
, $2,
331 sizeof
(new_srv
->logo_url
));
332 if
(n
>= sizeof
(new_srv
->logo_url
)) {
333 yyerror("%s: logo_url truncated", __func__
);
339 | CUSTOM_CSS STRING
{
340 n
= strlcpy
(new_srv
->custom_css
, $2,
341 sizeof
(new_srv
->custom_css
));
342 if
(n
>= sizeof
(new_srv
->custom_css
)) {
343 yyerror("%s: custom_css truncated", __func__
);
349 | LISTEN ON STRING fcgiport
{
350 if
(get_addrs
($3, new_srv
, $4) == -1) {
351 yyerror("could not get addrs");
354 new_srv
->fcgi_socket
= 1;
356 | LISTEN ON SOCKET STRING
{
357 if
(strcasecmp
($4, "off") == 0) {
358 new_srv
->unix_socket
= 0;
363 new_srv
->unix_socket
= 1;
365 n
= snprintf
(new_srv
->unix_socket_name
,
366 sizeof
(new_srv
->unix_socket_name
), "%s%s",
367 gotwebd
->httpd_chroot
, $4);
369 (size_t)n
>= sizeof
(new_srv
->unix_socket_name
)) {
370 yyerror("%s: unix_socket_name truncated",
379 yyerror("max_repos is too small: %lld", $2);
382 new_srv
->max_repos
= $2;
384 | SHOW_SITE_OWNER boolean
{
385 new_srv
->show_site_owner
= $2;
387 | SHOW_REPO_OWNER boolean
{
388 new_srv
->show_repo_owner
= $2;
390 | SHOW_REPO_AGE boolean
{
391 new_srv
->show_repo_age
= $2;
393 | SHOW_REPO_DESCRIPTION boolean
{
394 new_srv
->show_repo_description
= $2;
396 | SHOW_REPO_CLONEURL boolean
{
397 new_srv
->show_repo_cloneurl
= $2;
399 | RESPECT_EXPORTOK boolean
{
400 new_srv
->respect_exportok
= $2;
402 | MAX_REPOS_DISPLAY NUMBER
{
404 yyerror("max_repos_display is too small: %lld",
408 new_srv
->max_repos_display
= $2;
410 | MAX_COMMITS_DISPLAY NUMBER
{
412 yyerror("max_commits_display is too small:"
416 new_srv
->max_commits_display
= $2;
420 serveropts2
: serveropts2 serveropts1 nl
427 optnl
: '\n' optnl
/* zero or more newlines */
439 yyerror(const char *fmt
, ...
)
446 if
(vasprintf
(&msg
, fmt
, ap
) == -1)
447 fatalx
("yyerror vasprintf");
449 logit
(LOG_CRIT
, "%s:%d: %s", file
->name
, yylval.lineno
, msg
);
455 kw_cmp
(const void *k
, const void *e
)
457 return
(strcmp
(k
, ((const struct keywords
*)e
)->k_name
));
463 /* This has to be sorted always. */
464 static const struct keywords keywords
[] = {
465 { "chroot", CHROOT
},
466 { "custom_css", CUSTOM_CSS
},
467 { "listen", LISTEN
},
469 { "logo_url", LOGO_URL
},
470 { "max_commits_display", MAX_COMMITS_DISPLAY
},
471 { "max_repos", MAX_REPOS
},
472 { "max_repos_display", MAX_REPOS_DISPLAY
},
475 { "prefork", PREFORK
},
476 { "repos_path", REPOS_PATH
},
477 { "respect_exportok", RESPECT_EXPORTOK
},
478 { "server", SERVER
},
479 { "show_repo_age", SHOW_REPO_AGE
},
480 { "show_repo_cloneurl", SHOW_REPO_CLONEURL
},
481 { "show_repo_description", SHOW_REPO_DESCRIPTION
},
482 { "show_repo_owner", SHOW_REPO_OWNER
},
483 { "show_site_owner", SHOW_SITE_OWNER
},
484 { "site_link", SITE_LINK
},
485 { "site_name", SITE_NAME
},
486 { "site_owner", SITE_OWNER
},
487 { "socket", SOCKET
},
488 { "unix_socket", UNIX_SOCKET
},
489 { "unix_socket_name", UNIX_SOCKET_NAME
},
491 const struct keywords
*p
;
493 p
= bsearch
(s
, keywords
, sizeof
(keywords
)/sizeof
(keywords
[0]),
494 sizeof
(keywords
[0]), kw_cmp
);
502 #define MAXPUSHBACK 128
504 unsigned char *parsebuf
;
506 unsigned char pushback_buffer
[MAXPUSHBACK
];
507 int pushback_index
= 0;
515 /* Read character from the parsebuffer instead of input. */
516 if
(parseindex
>= 0) {
517 c
= parsebuf
[parseindex
++];
526 return
(pushback_buffer
[--pushback_index
]);
529 c
= getc
(file
->stream
);
531 yyerror("reached end of file while parsing "
536 c
= getc
(file
->stream
);
538 next
= getc
(file
->stream
);
543 yylval.lineno
= file
->lineno
;
545 c
= getc
(file
->stream
);
561 if
(pushback_index
< MAXPUSHBACK
-1)
562 return
(pushback_buffer
[pushback_index
++] = c
);
574 /* Skip to either EOF or the first real EOL. */
577 c
= pushback_buffer
[--pushback_index
];
593 unsigned char buf
[8096];
594 unsigned char *p
, *val
;
601 while
(c
== ' ' || c
== '\t')
602 c
= lgetc
(0); /* nothing */
604 yylval.lineno
= file
->lineno
;
607 while
(c
!= '\n' && c
!= EOF
)
608 c
= lgetc
(0); /* nothing */
610 if
(c
== '$' && parsebuf
== NULL
) {
616 if
(p
+ 1 >= buf
+ sizeof
(buf
) - 1) {
617 yyerror("string too long");
620 if
(isalnum
(c
) || c
== '_') {
630 yyerror("macro '%s' not defined", buf
);
649 } else if
(c
== '\\') {
650 next
= lgetc
(quotec
);
653 if
(next
== quotec || c
== ' ' || c
== '\t')
655 else if
(next
== '\n') {
660 } else if
(c
== quotec
) {
663 } else if
(c
== '\0') {
664 yyerror("syntax error");
667 if
(p
+ 1 >= buf
+ sizeof
(buf
) - 1) {
668 yyerror("string too long");
673 yylval.v.
string = strdup
(buf
);
674 if
(yylval.v.
string == NULL
)
675 err
(1, "yylex: strdup");
679 #define allowed_to_end_number(x) \
680 (isspace
(x
) || x
== ')' || x
==',' || x
== '/' || x
== '}' || x
== '=')
682 if
(c
== '-' || isdigit
(c
)) {
685 if
((unsigned)(p
-buf
) >= sizeof
(buf
)) {
686 yyerror("string too long");
690 } while
(c
!= EOF
&& isdigit
(c
));
692 if
(p
== buf
+ 1 && buf
[0] == '-')
694 if
(c
== EOF || allowed_to_end_number
(c
)) {
695 const char *errstr
= NULL
;
698 yylval.v.number
= strtonum
(buf
, LLONG_MIN
,
701 yyerror("\"%s\" invalid number: %s",
716 #define allowed_in_string(x) \
717 (isalnum
(x
) ||
(ispunct
(x
) && x
!= '(' && x
!= ')' && \
718 x
!= '{' && x
!= '}' && \
719 x
!= '!' && x
!= '=' && x
!= '#' && \
722 if
(isalnum
(c
) || c
== ':' || c
== '_') {
725 if
((unsigned)(p
-buf
) >= sizeof
(buf
)) {
726 yyerror("string too long");
730 } while
(c
!= EOF
&& (allowed_in_string
(c
)));
734 if
(token
== STRING
) {
735 yylval.v.
string = strdup
(buf
);
736 if
(yylval.v.
string == NULL
)
737 err
(1, "yylex: strdup");
742 yylval.lineno
= file
->lineno
;
751 check_file_secrecy
(int fd
, const char *fname
)
755 if
(fstat
(fd
, &st
)) {
756 log_warn
("cannot stat %s", fname
);
759 if
(st.st_uid
!= 0 && st.st_uid
!= getuid
()) {
760 log_warnx
("%s: owner not root or current user", fname
);
763 if
(st.st_mode
& (S_IWGRP | S_IXGRP | S_IRWXO
)) {
764 log_warnx
("%s: group writable or world read/writable", fname
);
771 newfile
(const char *name
, int secret
)
775 nfile
= calloc
(1, sizeof
(struct file
));
780 nfile
->name
= strdup
(name
);
781 if
(nfile
->name
== NULL
) {
786 nfile
->stream
= fopen
(nfile
->name
, "r");
787 if
(nfile
->stream
== NULL
) {
788 /* no warning, we don't require a conf file */
793 check_file_secrecy
(fileno
(nfile
->stream
), nfile
->name
)) {
794 fclose
(nfile
->stream
);
804 closefile
(struct file
*xfile
)
806 fclose
(xfile
->stream
);
812 add_default_server
(void)
814 new_srv
= conf_new_server
(D_SITENAME
);
815 log_debug
("%s: adding default server %s", __func__
, D_SITENAME
);
819 parse_config
(const char *filename
, struct gotwebd
*env
)
821 struct sym
*sym
, *next
;
823 if
(config_init
(env
) == -1)
824 fatalx
("failed to initialize configuration");
828 file
= newfile
(filename
, 0);
830 add_default_server
();
831 sockets_parse_sockets
(env
);
832 /* just return, as we don't require a conf file */
837 errors
= file
->errors
;
840 /* Free macros and check which have not been used. */
841 TAILQ_FOREACH_SAFE
(sym
, &symhead
, entry
, next
) {
842 if
((gotwebd
->gotwebd_verbose
> 1) && !sym
->used
)
843 fprintf
(stderr
, "warning: macro '%s' not used\n",
848 TAILQ_REMOVE
(&symhead
, sym
, entry
);
856 /* just add default server if no config specified */
857 if
(gotwebd
->server_cnt
== 0)
858 add_default_server
();
860 /* setup our listening sockets */
861 sockets_parse_sockets
(env
);
867 conf_new_server
(const char *name
)
869 struct server
*srv
= NULL
;
871 srv
= calloc
(1, sizeof
(*srv
));
873 fatalx
("%s: calloc", __func__
);
875 n
= strlcpy
(srv
->name
, name
, sizeof
(srv
->name
));
876 if
(n
>= sizeof
(srv
->name
))
877 fatalx
("%s: strlcpy", __func__
);
878 n
= snprintf
(srv
->unix_socket_name
,
879 sizeof
(srv
->unix_socket_name
), "%s%s", D_HTTPD_CHROOT
,
881 if
(n
< 0 ||
(size_t)n
>= sizeof
(srv
->unix_socket_name
))
882 fatalx
("%s: snprintf", __func__
);
883 n
= strlcpy
(srv
->repos_path
, D_GOTPATH
,
884 sizeof
(srv
->repos_path
));
885 if
(n
>= sizeof
(srv
->repos_path
))
886 fatalx
("%s: strlcpy", __func__
);
887 n
= strlcpy
(srv
->site_name
, D_SITENAME
,
888 sizeof
(srv
->site_name
));
889 if
(n
>= sizeof
(srv
->site_name
))
890 fatalx
("%s: strlcpy", __func__
);
891 n
= strlcpy
(srv
->site_owner
, D_SITEOWNER
,
892 sizeof
(srv
->site_owner
));
893 if
(n
>= sizeof
(srv
->site_owner
))
894 fatalx
("%s: strlcpy", __func__
);
895 n
= strlcpy
(srv
->site_link
, D_SITELINK
,
896 sizeof
(srv
->site_link
));
897 if
(n
>= sizeof
(srv
->site_link
))
898 fatalx
("%s: strlcpy", __func__
);
899 n
= strlcpy
(srv
->logo
, D_GOTLOGO
,
901 if
(n
>= sizeof
(srv
->logo
))
902 fatalx
("%s: strlcpy", __func__
);
903 n
= strlcpy
(srv
->logo_url
, D_GOTURL
, sizeof
(srv
->logo_url
));
904 if
(n
>= sizeof
(srv
->logo_url
))
905 fatalx
("%s: strlcpy", __func__
);
906 n
= strlcpy
(srv
->custom_css
, D_GOTWEBCSS
, sizeof
(srv
->custom_css
));
907 if
(n
>= sizeof
(srv
->custom_css
))
908 fatalx
("%s: strlcpy", __func__
);
910 srv
->show_site_owner
= D_SHOWSOWNER
;
911 srv
->show_repo_owner
= D_SHOWROWNER
;
912 srv
->show_repo_age
= D_SHOWAGE
;
913 srv
->show_repo_description
= D_SHOWDESC
;
914 srv
->show_repo_cloneurl
= D_SHOWURL
;
915 srv
->respect_exportok
= D_RESPECTEXPORTOK
;
917 srv
->max_repos_display
= D_MAXREPODISP
;
918 srv
->max_commits_display
= D_MAXCOMMITDISP
;
919 srv
->max_repos
= D_MAXREPO
;
921 srv
->unix_socket
= 1;
922 srv
->fcgi_socket
= 0;
924 TAILQ_INIT
(&srv
->al
);
925 TAILQ_INSERT_TAIL
(&gotwebd
->servers
, srv
, entry
);
926 gotwebd
->server_cnt
++;
932 symset
(const char *nam
, const char *val
, int persist
)
936 TAILQ_FOREACH
(sym
, &symhead
, entry
) {
937 if
(strcmp
(nam
, sym
->nam
) == 0)
942 if
(sym
->persist
== 1)
947 TAILQ_REMOVE
(&symhead
, sym
, entry
);
951 sym
= calloc
(1, sizeof
(*sym
));
955 sym
->nam
= strdup
(nam
);
956 if
(sym
->nam
== NULL
) {
960 sym
->val
= strdup
(val
);
961 if
(sym
->val
== NULL
) {
967 sym
->persist
= persist
;
968 TAILQ_INSERT_TAIL
(&symhead
, sym
, entry
);
973 cmdline_symset
(char *s
)
978 val
= strrchr
(s
, '=');
982 sym
= strndup
(s
, val
- s
);
984 fatal
("%s: strndup", __func__
);
986 ret
= symset
(sym
, val
+ 1, 1);
993 symget
(const char *nam
)
997 TAILQ_FOREACH
(sym
, &symhead
, entry
) {
998 if
(strcmp
(nam
, sym
->nam
) == 0) {
1007 getservice
(const char *n
)
1013 llval
= strtonum
(n
, 0, UINT16_MAX
, &errstr
);
1015 s
= getservbyname
(n
, "tcp");
1017 s
= getservbyname
(n
, "udp");
1020 return ntohs
(s
->s_port
);
1023 return
(unsigned short)llval
;
1027 host
(const char *s
, struct server
*new_srv
, int max
,
1028 in_port_t port
, const char *ifname
, int ipproto
)
1030 struct addrinfo hints
, *res0
, *res
;
1032 struct sockaddr_in
*sain
;
1033 struct sockaddr_in6
*sin6
;
1036 if
((cnt
= host_if
(s
, new_srv
, max
, port
, ifname
, ipproto
)) != 0)
1039 memset
(&hints
, 0, sizeof
(hints
));
1040 hints.ai_family
= AF_UNSPEC
;
1041 hints.ai_socktype
= SOCK_STREAM
; /* DUMMY */
1042 hints.ai_flags
= AI_ADDRCONFIG
;
1043 error = getaddrinfo
(s
, NULL
, &hints
, &res0
);
1044 if
(error == EAI_AGAIN ||
error == EAI_NODATA ||
error == EAI_NONAME
)
1047 log_warnx
("%s: could not parse \"%s\": %s", __func__
, s
,
1048 gai_strerror
(error));
1052 for
(res
= res0
; res
&& cnt
< max
; res
= res
->ai_next
) {
1053 if
(res
->ai_family
!= AF_INET
&&
1054 res
->ai_family
!= AF_INET6
)
1056 if
((h
= calloc
(1, sizeof
(*h
))) == NULL
)
1061 if
(ifname
!= NULL
) {
1062 if
(strlcpy
(h
->ifname
, ifname
, sizeof
(h
->ifname
)) >=
1063 sizeof
(h
->ifname
)) {
1064 log_warnx
("%s: interface name truncated",
1072 h
->ipproto
= ipproto
;
1073 h
->ss.ss_family
= res
->ai_family
;
1075 if
(res
->ai_family
== AF_INET
) {
1076 struct sockaddr_in
*ra
;
1077 sain
= (struct sockaddr_in
*)&h
->ss
;
1078 ra
= (struct sockaddr_in
*)res
->ai_addr
;
1079 got_sockaddr_inet_init
(sain
, &ra
->sin_addr
);
1081 struct sockaddr_in6
*ra
;
1082 sin6
= (struct sockaddr_in6
*)&h
->ss
;
1083 ra
= (struct sockaddr_in6
*)res
->ai_addr
;
1084 got_sockaddr_inet6_init
(sin6
, &ra
->sin6_addr
, 0);
1087 if
(add_addr
(new_srv
, h
))
1091 if
(cnt
== max
&& res
) {
1092 log_warnx
("%s: %s resolves to more than %d hosts", __func__
,
1100 host_if
(const char *s
, struct server
*new_srv
, int max
,
1101 in_port_t port
, const char *ifname
, int ipproto
)
1103 struct ifaddrs
*ifap
, *p
;
1104 struct sockaddr_in
*sain
;
1105 struct sockaddr_in6
*sin6
;
1109 if
(getifaddrs
(&ifap
) == -1)
1110 fatal
("getifaddrs");
1112 /* First search for IPv4 addresses */
1116 for
(p
= ifap
; p
!= NULL
&& cnt
< max
; p
= p
->ifa_next
) {
1117 if
(p
->ifa_addr
== NULL ||
1118 p
->ifa_addr
->sa_family
!= af ||
1119 (strcmp
(s
, p
->ifa_name
) != 0 &&
1120 !is_if_in_group
(p
->ifa_name
, s
)))
1122 if
((h
= calloc
(1, sizeof
(*h
))) == NULL
)
1127 if
(ifname
!= NULL
) {
1128 if
(strlcpy
(h
->ifname
, ifname
, sizeof
(h
->ifname
)) >=
1129 sizeof
(h
->ifname
)) {
1130 log_warnx
("%s: interface name truncated",
1138 h
->ipproto
= ipproto
;
1139 h
->ss.ss_family
= af
;
1141 if
(af
== AF_INET
) {
1142 struct sockaddr_in
*ra
;
1143 sain
= (struct sockaddr_in
*)&h
->ss
;
1144 ra
= (struct sockaddr_in
*)p
->ifa_addr
;
1145 got_sockaddr_inet_init
(sain
, &ra
->sin_addr
);
1147 struct sockaddr_in6
*ra
;
1148 sin6
= (struct sockaddr_in6
*)&h
->ss
;
1149 ra
= (struct sockaddr_in6
*)p
->ifa_addr
;
1150 got_sockaddr_inet6_init
(sin6
, &ra
->sin6_addr
,
1154 if
(add_addr
(new_srv
, h
))
1158 if
(af
== AF_INET
) {
1159 /* Next search for IPv6 addresses */
1165 log_warnx
("%s: %s resolves to more than %d hosts", __func__
,
1173 is_if_in_group
(const char *ifname
, const char *groupname
)
1175 /* TA: Check this... */
1176 #ifdef HAVE_STRUCT_IFGROUPREQ
1178 struct ifgroupreq ifgr
;
1179 struct ifg_req
*ifg
;
1183 if
((s
= socket
(AF_INET
, SOCK_DGRAM
, 0)) == -1)
1186 memset
(&ifgr
, 0, sizeof
(ifgr
));
1187 if
(strlcpy
(ifgr.ifgr_name
, ifname
, IFNAMSIZ
) >= IFNAMSIZ
)
1189 if
(ioctl
(s
, SIOCGIFGROUP
, (caddr_t
)&ifgr
) == -1) {
1190 if
(errno
== EINVAL || errno
== ENOTTY
)
1192 err
(1, "SIOCGIFGROUP");
1195 len
= ifgr.ifgr_len
;
1196 ifgr.ifgr_groups
= calloc
(len
/ sizeof
(struct ifg_req
),
1197 sizeof
(struct ifg_req
));
1198 if
(ifgr.ifgr_groups
== NULL
)
1199 err
(1, "getifgroups");
1200 if
(ioctl
(s
, SIOCGIFGROUP
, (caddr_t
)&ifgr
) == -1)
1201 err
(1, "SIOCGIFGROUP");
1203 ifg
= ifgr.ifgr_groups
;
1204 for
(; ifg
&& len
>= sizeof
(struct ifg_req
); ifg
++) {
1205 len
-= sizeof
(struct ifg_req
);
1206 if
(strcmp
(ifg
->ifgrq_group
, groupname
) == 0) {
1211 free
(ifgr.ifgr_groups
);
1222 get_addrs
(const char *addr
, struct server
*new_srv
, in_port_t port
)
1224 if
(strcmp
("", addr
) == 0) {
1225 if
(host
("127.0.0.1", new_srv
, 1, port
, "127.0.0.1",
1227 yyerror("invalid listen ip: %s",
1231 if
(host
("::1", new_srv
, 1, port
, "::1", -1) <= 0) {
1232 yyerror("invalid listen ip: %s", "::1");
1236 if
(host
(addr
, new_srv
, GOTWEBD_MAXIFACE
, port
, addr
,
1238 yyerror("invalid listen ip: %s", addr
);
1246 addr_dup_check
(struct addresslist
*al
, struct address
*h
, const char *new_srv
,
1247 const char *other_srv
)
1251 char buf
[INET6_ADDRSTRLEN
];
1252 const char *addrstr
;
1254 TAILQ_FOREACH
(a
, al
, entry
) {
1255 if
(memcmp
(&a
->ss
, &h
->ss
, sizeof
(h
->ss
)) != 0 ||
1259 switch
(h
->ss.ss_family
) {
1261 ia
= &((struct sockaddr_in
*)(&h
->ss
))->sin_addr
;
1264 ia
= &((struct sockaddr_in6
*)(&h
->ss
))->sin6_addr
;
1267 yyerror("unknown address family: %d", h
->ss.ss_family
);
1270 addrstr
= inet_ntop
(h
->ss.ss_family
, ia
, buf
, sizeof
(buf
));
1273 yyerror("server %s: duplicate fcgi listen "
1274 "address %s:%d, already used by server %s",
1275 new_srv
, addrstr
, h
->port
, other_srv
);
1277 log_warnx
("server: %s: duplicate fcgi listen "
1278 "address %s:%d", new_srv
, addrstr
, h
->port
);
1282 yyerror("server: %s: duplicate fcgi listen "
1283 "address, already used by server %s",
1284 new_srv
, other_srv
);
1286 log_warnx
("server %s: duplicate fcgi listen "
1287 "address", new_srv
);
1298 add_addr
(struct server
*new_srv
, struct address
*h
)
1302 /* Address cannot be shared between different servers. */
1303 TAILQ_FOREACH
(srv
, &gotwebd
->servers
, entry
) {
1306 if
(addr_dup_check
(&srv
->al
, h
, new_srv
->name
, srv
->name
))
1310 /* Tolerate duplicate address lines within the scope of a server. */
1311 if
(addr_dup_check
(&new_srv
->al
, h
, NULL
, NULL
) == 0)
1312 TAILQ_INSERT_TAIL
(&new_srv
->al
, h
, entry
);