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>
53 #include "got_sockaddr.h"
55 TAILQ_HEAD
(files
, file
) files
= TAILQ_HEAD_INITIALIZER
(files
);
57 TAILQ_ENTRY
(file
) entry
;
63 struct file
*newfile
(const char *, int);
64 static void closefile
(struct file
*);
65 int check_file_secrecy
(int, const char *);
68 int yyerror(const char *, ...
)
69 __attribute__
((__format__
(printf
, 1, 2)))
70 __attribute__
((__nonnull__
(1)));
71 int kw_cmp
(const void *, const void *);
77 TAILQ_HEAD
(symhead
, sym
) symhead
= TAILQ_HEAD_INITIALIZER
(symhead
);
79 TAILQ_ENTRY
(sym
) entry
;
86 int symset
(const char *, const char *, int);
87 char *symget
(const char *);
91 static struct gotwebd
*gotwebd
;
92 static struct server
*new_srv
;
93 static struct server
*conf_new_server
(const char *);
94 int getservice
(const char *);
97 int get_addrs
(const char *, struct server
*, in_port_t
);
98 int addr_dup_check
(struct addresslist
*, struct address
*,
99 const char *, const char *);
100 int add_addr
(struct server
*, struct address
*);
101 struct address
*host_v4
(const char *);
102 struct address
*host_v6
(const char *);
103 int host_dns
(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 host
(const char *, struct server
*,
108 int, in_port_t
, const char *, int);
109 int is_if_in_group
(const char *, const char *);
122 %token LISTEN WWW_PATH MAX_REPOS SITE_NAME SITE_OWNER SITE_LINK LOGO
123 %token LOGO_URL SHOW_REPO_OWNER SHOW_REPO_AGE SHOW_REPO_DESCRIPTION
124 %token MAX_REPOS_DISPLAY REPOS_PATH MAX_COMMITS_DISPLAY ON ERROR
125 %token SHOW_SITE_OWNER SHOW_REPO_CLONEURL PORT PREFORK RESPECT_EXPORTOK
126 %token UNIX_SOCKET UNIX_SOCKET_NAME SERVER CHROOT CUSTOM_CSS SOCKET
128 %token
<v.
string> STRING
129 %type
<v.port
> fcgiport
130 %token
<v.number
> NUMBER
131 %type
<v.number
> boolean
135 grammar
: /* empty */
137 | grammar varset
'\n'
139 | grammar server
'\n'
140 | grammar
error '\n' { file
->errors
++; }
143 varset
: STRING
'=' STRING
{
146 if
(isspace
((unsigned char)*s
)) {
147 yyerror("macro name cannot contain "
154 if
(symset
($1, $3, 0) == -1)
155 fatal
("cannot store variable");
162 if
(strcasecmp
($1, "1") == 0 ||
163 strcasecmp
($1, "yes") == 0 ||
164 strcasecmp
($1, "on") == 0)
166 else if
(strcasecmp
($1, "0") == 0 ||
167 strcasecmp
($1, "off") == 0 ||
168 strcasecmp
($1, "no") == 0)
171 yyerror("invalid boolean value '%s'", $1);
178 | NUMBER
{ $$
= $1; }
181 fcgiport
: PORT NUMBER
{
182 if
($2 <= 0 ||
$2 > (int)USHRT_MAX
) {
183 yyerror("invalid port: %lld", $2);
191 if
((val
= getservice
($2)) == -1) {
192 yyerror("invalid port: %s", $2);
202 main
: PREFORK NUMBER
{
203 gotwebd
->prefork_gotwebd
= $2;
206 n
= strlcpy
(gotwebd
->httpd_chroot
, $2,
207 sizeof
(gotwebd
->httpd_chroot
));
208 if
(n
>= sizeof
(gotwebd
->httpd_chroot
)) {
209 yyerror("%s: httpd_chroot truncated", __func__
);
215 | UNIX_SOCKET boolean
{
216 gotwebd
->unix_socket
= $2;
218 | UNIX_SOCKET_NAME STRING
{
219 n
= snprintf
(gotwebd
->unix_socket_name
,
220 sizeof
(gotwebd
->unix_socket_name
), "%s%s",
221 strlen
(gotwebd
->httpd_chroot
) ?
222 gotwebd
->httpd_chroot
: D_HTTPD_CHROOT
, $2);
224 (size_t)n
>= sizeof
(gotwebd
->unix_socket_name
)) {
225 yyerror("%s: unix_socket_name truncated",
234 server
: SERVER STRING
{
237 TAILQ_FOREACH
(srv
, &gotwebd
->servers
, entry
) {
238 if
(strcmp
(srv
->name
, $2) == 0) {
239 yyerror("server name exists '%s'", $2);
245 new_srv
= conf_new_server
($2);
246 log_debug
("adding server %s", $2);
252 TAILQ_FOREACH
(srv
, &gotwebd
->servers
, entry
) {
253 if
(strcmp
(srv
->name
, $2) == 0) {
254 yyerror("server name exists '%s'", $2);
260 new_srv
= conf_new_server
($2);
261 log_debug
("adding server %s", $2);
263 } '{' optnl serveropts2
'}' {
267 serveropts1
: REPOS_PATH STRING
{
268 n
= strlcpy
(new_srv
->repos_path
, $2,
269 sizeof
(new_srv
->repos_path
));
270 if
(n
>= sizeof
(new_srv
->repos_path
)) {
271 yyerror("%s: repos_path truncated", __func__
);
278 n
= strlcpy
(new_srv
->site_name
, $2,
279 sizeof
(new_srv
->site_name
));
280 if
(n
>= sizeof
(new_srv
->site_name
)) {
281 yyerror("%s: site_name truncated", __func__
);
287 | SITE_OWNER STRING
{
288 n
= strlcpy
(new_srv
->site_owner
, $2,
289 sizeof
(new_srv
->site_owner
));
290 if
(n
>= sizeof
(new_srv
->site_owner
)) {
291 yyerror("%s: site_owner truncated", __func__
);
298 n
= strlcpy
(new_srv
->site_link
, $2,
299 sizeof
(new_srv
->site_link
));
300 if
(n
>= sizeof
(new_srv
->site_link
)) {
301 yyerror("%s: site_link truncated", __func__
);
308 n
= strlcpy
(new_srv
->logo
, $2, sizeof
(new_srv
->logo
));
309 if
(n
>= sizeof
(new_srv
->logo
)) {
310 yyerror("%s: logo truncated", __func__
);
317 n
= strlcpy
(new_srv
->logo_url
, $2,
318 sizeof
(new_srv
->logo_url
));
319 if
(n
>= sizeof
(new_srv
->logo_url
)) {
320 yyerror("%s: logo_url truncated", __func__
);
326 | CUSTOM_CSS STRING
{
327 n
= strlcpy
(new_srv
->custom_css
, $2,
328 sizeof
(new_srv
->custom_css
));
329 if
(n
>= sizeof
(new_srv
->custom_css
)) {
330 yyerror("%s: custom_css truncated", __func__
);
336 | LISTEN ON STRING fcgiport
{
337 if
(get_addrs
($3, new_srv
, $4) == -1) {
338 yyerror("could not get addrs");
341 new_srv
->fcgi_socket
= 1;
343 | LISTEN ON SOCKET STRING
{
344 if
(!strcasecmp
($4, "off") ||
345 !strcasecmp
($4, "no")) {
346 new_srv
->unix_socket
= 0;
351 new_srv
->unix_socket
= 1;
353 n
= snprintf
(new_srv
->unix_socket_name
,
354 sizeof
(new_srv
->unix_socket_name
), "%s%s",
355 strlen
(gotwebd
->httpd_chroot
) ?
356 gotwebd
->httpd_chroot
: D_HTTPD_CHROOT
, $4);
358 (size_t)n
>= sizeof
(new_srv
->unix_socket_name
)) {
359 yyerror("%s: unix_socket_name truncated",
368 new_srv
->max_repos
= $2;
370 | SHOW_SITE_OWNER boolean
{
371 new_srv
->show_site_owner
= $2;
373 | SHOW_REPO_OWNER boolean
{
374 new_srv
->show_repo_owner
= $2;
376 | SHOW_REPO_AGE boolean
{
377 new_srv
->show_repo_age
= $2;
379 | SHOW_REPO_DESCRIPTION boolean
{
380 new_srv
->show_repo_description
= $2;
382 | SHOW_REPO_CLONEURL boolean
{
383 new_srv
->show_repo_cloneurl
= $2;
385 | RESPECT_EXPORTOK boolean
{
386 new_srv
->respect_exportok
= $2;
388 | MAX_REPOS_DISPLAY NUMBER
{
389 new_srv
->max_repos_display
= $2;
391 | MAX_COMMITS_DISPLAY NUMBER
{
393 new_srv
->max_commits_display
= $2;
397 serveropts2
: serveropts2 serveropts1 nl
404 optnl
: '\n' optnl
/* zero or more newlines */
416 yyerror(const char *fmt
, ...
)
423 if
(vasprintf
(&msg
, fmt
, ap
) == -1)
424 fatalx
("yyerror vasprintf");
426 logit
(LOG_CRIT
, "%s:%d: %s", file
->name
, yylval.lineno
, msg
);
432 kw_cmp
(const void *k
, const void *e
)
434 return
(strcmp
(k
, ((const struct keywords
*)e
)->k_name
));
440 /* This has to be sorted always. */
441 static const struct keywords keywords
[] = {
442 { "chroot", CHROOT
},
443 { "custom_css", CUSTOM_CSS
},
444 { "listen", LISTEN
},
446 { "logo_url", LOGO_URL
},
447 { "max_commits_display", MAX_COMMITS_DISPLAY
},
448 { "max_repos", MAX_REPOS
},
449 { "max_repos_display", MAX_REPOS_DISPLAY
},
452 { "prefork", PREFORK
},
453 { "repos_path", REPOS_PATH
},
454 { "respect_exportok", RESPECT_EXPORTOK
},
455 { "server", SERVER
},
456 { "show_repo_age", SHOW_REPO_AGE
},
457 { "show_repo_cloneurl", SHOW_REPO_CLONEURL
},
458 { "show_repo_description", SHOW_REPO_DESCRIPTION
},
459 { "show_repo_owner", SHOW_REPO_OWNER
},
460 { "show_site_owner", SHOW_SITE_OWNER
},
461 { "site_link", SITE_LINK
},
462 { "site_name", SITE_NAME
},
463 { "site_owner", SITE_OWNER
},
464 { "socket", SOCKET
},
465 { "unix_socket", UNIX_SOCKET
},
466 { "unix_socket_name", UNIX_SOCKET_NAME
},
468 const struct keywords
*p
;
470 p
= bsearch
(s
, keywords
, sizeof
(keywords
)/sizeof
(keywords
[0]),
471 sizeof
(keywords
[0]), kw_cmp
);
479 #define MAXPUSHBACK 128
481 unsigned char *parsebuf
;
483 unsigned char pushback_buffer
[MAXPUSHBACK
];
484 int pushback_index
= 0;
492 /* Read character from the parsebuffer instead of input. */
493 if
(parseindex
>= 0) {
494 c
= parsebuf
[parseindex
++];
503 return
(pushback_buffer
[--pushback_index
]);
506 c
= getc
(file
->stream
);
508 yyerror("reached end of file while parsing "
513 c
= getc
(file
->stream
);
515 next
= getc
(file
->stream
);
520 yylval.lineno
= file
->lineno
;
522 c
= getc
(file
->stream
);
538 if
(pushback_index
< MAXPUSHBACK
-1)
539 return
(pushback_buffer
[pushback_index
++] = c
);
551 /* Skip to either EOF or the first real EOL. */
554 c
= pushback_buffer
[--pushback_index
];
570 unsigned char buf
[8096];
571 unsigned char *p
, *val
;
578 while
(c
== ' ' || c
== '\t')
579 c
= lgetc
(0); /* nothing */
581 yylval.lineno
= file
->lineno
;
584 while
(c
!= '\n' && c
!= EOF
)
585 c
= lgetc
(0); /* nothing */
587 if
(c
== '$' && parsebuf
== NULL
) {
593 if
(p
+ 1 >= buf
+ sizeof
(buf
) - 1) {
594 yyerror("string too long");
597 if
(isalnum
(c
) || c
== '_') {
607 yyerror("macro '%s' not defined", buf
);
626 } else if
(c
== '\\') {
627 next
= lgetc
(quotec
);
630 if
(next
== quotec || c
== ' ' || c
== '\t')
632 else if
(next
== '\n') {
637 } else if
(c
== quotec
) {
640 } else if
(c
== '\0') {
641 yyerror("syntax error");
644 if
(p
+ 1 >= buf
+ sizeof
(buf
) - 1) {
645 yyerror("string too long");
650 yylval.v.
string = strdup
(buf
);
651 if
(yylval.v.
string == NULL
)
652 err
(1, "yylex: strdup");
656 #define allowed_to_end_number(x) \
657 (isspace
(x
) || x
== ')' || x
==',' || x
== '/' || x
== '}' || x
== '=')
659 if
(c
== '-' || isdigit
(c
)) {
662 if
((unsigned)(p
-buf
) >= sizeof
(buf
)) {
663 yyerror("string too long");
667 } while
(c
!= EOF
&& isdigit
(c
));
669 if
(p
== buf
+ 1 && buf
[0] == '-')
671 if
(c
== EOF || allowed_to_end_number
(c
)) {
672 const char *errstr
= NULL
;
675 yylval.v.number
= strtonum
(buf
, LLONG_MIN
,
678 yyerror("\"%s\" invalid number: %s",
693 #define allowed_in_string(x) \
694 (isalnum
(x
) ||
(ispunct
(x
) && x
!= '(' && x
!= ')' && \
695 x
!= '{' && x
!= '}' && \
696 x
!= '!' && x
!= '=' && x
!= '#' && \
699 if
(isalnum
(c
) || c
== ':' || c
== '_') {
702 if
((unsigned)(p
-buf
) >= sizeof
(buf
)) {
703 yyerror("string too long");
707 } while
(c
!= EOF
&& (allowed_in_string
(c
)));
711 if
(token
== STRING
) {
712 yylval.v.
string = strdup
(buf
);
713 if
(yylval.v.
string == NULL
)
714 err
(1, "yylex: strdup");
719 yylval.lineno
= file
->lineno
;
728 check_file_secrecy
(int fd
, const char *fname
)
732 if
(fstat
(fd
, &st
)) {
733 log_warn
("cannot stat %s", fname
);
736 if
(st.st_uid
!= 0 && st.st_uid
!= getuid
()) {
737 log_warnx
("%s: owner not root or current user", fname
);
740 if
(st.st_mode
& (S_IWGRP | S_IXGRP | S_IRWXO
)) {
741 log_warnx
("%s: group writable or world read/writable", fname
);
748 newfile
(const char *name
, int secret
)
752 nfile
= calloc
(1, sizeof
(struct file
));
757 nfile
->name
= strdup
(name
);
758 if
(nfile
->name
== NULL
) {
763 nfile
->stream
= fopen
(nfile
->name
, "r");
764 if
(nfile
->stream
== NULL
) {
765 /* no warning, we don't require a conf file */
770 check_file_secrecy
(fileno
(nfile
->stream
), nfile
->name
)) {
771 fclose
(nfile
->stream
);
781 closefile
(struct file
*xfile
)
783 fclose
(xfile
->stream
);
789 add_default_server
(void)
791 new_srv
= conf_new_server
(D_SITENAME
);
792 log_debug
("%s: adding default server %s", __func__
, D_SITENAME
);
796 parse_config
(const char *filename
, struct gotwebd
*env
)
798 struct sym
*sym
, *next
;
800 if
(config_init
(env
) == -1)
801 fatalx
("failed to initialize configuration");
805 file
= newfile
(filename
, 0);
807 add_default_server
();
808 sockets_parse_sockets
(env
);
809 /* just return, as we don't require a conf file */
814 errors
= file
->errors
;
817 /* Free macros and check which have not been used. */
818 TAILQ_FOREACH_SAFE
(sym
, &symhead
, entry
, next
) {
819 if
((gotwebd
->gotwebd_verbose
> 1) && !sym
->used
)
820 fprintf
(stderr
, "warning: macro '%s' not used\n",
825 TAILQ_REMOVE
(&symhead
, sym
, entry
);
833 /* just add default server if no config specified */
834 if
(gotwebd
->server_cnt
== 0)
835 add_default_server
();
837 /* setup our listening sockets */
838 sockets_parse_sockets
(env
);
844 conf_new_server
(const char *name
)
846 struct server
*srv
= NULL
;
848 srv
= calloc
(1, sizeof
(*srv
));
850 fatalx
("%s: calloc", __func__
);
852 n
= strlcpy
(srv
->name
, name
, sizeof
(srv
->name
));
853 if
(n
>= sizeof
(srv
->name
))
854 fatalx
("%s: strlcpy", __func__
);
855 n
= snprintf
(srv
->unix_socket_name
,
856 sizeof
(srv
->unix_socket_name
), "%s%s", D_HTTPD_CHROOT
,
858 if
(n
< 0 ||
(size_t)n
>= sizeof
(srv
->unix_socket_name
))
859 fatalx
("%s: snprintf", __func__
);
860 n
= strlcpy
(srv
->repos_path
, D_GOTPATH
,
861 sizeof
(srv
->repos_path
));
862 if
(n
>= sizeof
(srv
->repos_path
))
863 fatalx
("%s: strlcpy", __func__
);
864 n
= strlcpy
(srv
->site_name
, D_SITENAME
,
865 sizeof
(srv
->site_name
));
866 if
(n
>= sizeof
(srv
->site_name
))
867 fatalx
("%s: strlcpy", __func__
);
868 n
= strlcpy
(srv
->site_owner
, D_SITEOWNER
,
869 sizeof
(srv
->site_owner
));
870 if
(n
>= sizeof
(srv
->site_owner
))
871 fatalx
("%s: strlcpy", __func__
);
872 n
= strlcpy
(srv
->site_link
, D_SITELINK
,
873 sizeof
(srv
->site_link
));
874 if
(n
>= sizeof
(srv
->site_link
))
875 fatalx
("%s: strlcpy", __func__
);
876 n
= strlcpy
(srv
->logo
, D_GOTLOGO
,
878 if
(n
>= sizeof
(srv
->logo
))
879 fatalx
("%s: strlcpy", __func__
);
880 n
= strlcpy
(srv
->logo_url
, D_GOTURL
, sizeof
(srv
->logo_url
));
881 if
(n
>= sizeof
(srv
->logo_url
))
882 fatalx
("%s: strlcpy", __func__
);
883 n
= strlcpy
(srv
->custom_css
, D_GOTWEBCSS
, sizeof
(srv
->custom_css
));
884 if
(n
>= sizeof
(srv
->custom_css
))
885 fatalx
("%s: strlcpy", __func__
);
887 srv
->show_site_owner
= D_SHOWSOWNER
;
888 srv
->show_repo_owner
= D_SHOWROWNER
;
889 srv
->show_repo_age
= D_SHOWAGE
;
890 srv
->show_repo_description
= D_SHOWDESC
;
891 srv
->show_repo_cloneurl
= D_SHOWURL
;
892 srv
->respect_exportok
= D_RESPECTEXPORTOK
;
894 srv
->max_repos_display
= D_MAXREPODISP
;
895 srv
->max_commits_display
= D_MAXCOMMITDISP
;
896 srv
->max_repos
= D_MAXREPO
;
898 srv
->unix_socket
= 1;
899 srv
->fcgi_socket
= 0;
901 TAILQ_INIT
(&srv
->al
);
902 TAILQ_INSERT_TAIL
(&gotwebd
->servers
, srv
, entry
);
903 gotwebd
->server_cnt
++;
909 symset
(const char *nam
, const char *val
, int persist
)
913 TAILQ_FOREACH
(sym
, &symhead
, entry
) {
914 if
(strcmp
(nam
, sym
->nam
) == 0)
919 if
(sym
->persist
== 1)
924 TAILQ_REMOVE
(&symhead
, sym
, entry
);
928 sym
= calloc
(1, sizeof
(*sym
));
932 sym
->nam
= strdup
(nam
);
933 if
(sym
->nam
== NULL
) {
937 sym
->val
= strdup
(val
);
938 if
(sym
->val
== NULL
) {
944 sym
->persist
= persist
;
945 TAILQ_INSERT_TAIL
(&symhead
, sym
, entry
);
950 cmdline_symset
(char *s
)
955 val
= strrchr
(s
, '=');
959 sym
= strndup
(s
, val
- s
);
961 fatal
("%s: strndup", __func__
);
963 ret
= symset
(sym
, val
+ 1, 1);
970 symget
(const char *nam
)
974 TAILQ_FOREACH
(sym
, &symhead
, entry
) {
975 if
(strcmp
(nam
, sym
->nam
) == 0) {
984 getservice
(const char *n
)
990 llval
= strtonum
(n
, 0, UINT16_MAX
, &errstr
);
992 s
= getservbyname
(n
, "tcp");
994 s
= getservbyname
(n
, "udp");
997 return ntohs
(s
->s_port
);
1000 return
(unsigned short)llval
;
1004 host_v4
(const char *s
)
1007 struct sockaddr_in
*sain
;
1010 memset
(&ina
, 0, sizeof
(ina
));
1011 if
(inet_pton
(AF_INET
, s
, &ina
) != 1)
1014 if
((h
= calloc
(1, sizeof
(*h
))) == NULL
)
1016 sain
= (struct sockaddr_in
*)&h
->ss
;
1017 got_sockaddr_inet_init
(sain
, &ina
);
1018 if
(sain
->sin_addr.s_addr
== INADDR_ANY
)
1019 h
->prefixlen
= 0; /* 0.0.0.0 address */
1021 h
->prefixlen
= -1; /* host address */
1026 host_v6
(const char *s
)
1028 struct addrinfo hints
, *res
;
1029 struct sockaddr_in6
*sa_in6
, *ra
;
1030 struct address
*h
= NULL
;
1032 memset
(&hints
, 0, sizeof
(hints
));
1033 hints.ai_family
= AF_INET6
;
1034 hints.ai_socktype
= SOCK_DGRAM
; /* dummy */
1035 hints.ai_flags
= AI_NUMERICHOST
;
1036 if
(getaddrinfo
(s
, "0", &hints
, &res
) == 0) {
1037 if
((h
= calloc
(1, sizeof
(*h
))) == NULL
)
1039 sa_in6
= (struct sockaddr_in6
*)&h
->ss
;
1040 ra
= (struct sockaddr_in6
*)res
->ai_addr
;
1041 got_sockaddr_inet6_init
(sa_in6
, &ra
->sin6_addr
,
1043 if
(memcmp
(&sa_in6
->sin6_addr
, &in6addr_any
,
1044 sizeof
(sa_in6
->sin6_addr
)) == 0)
1045 h
->prefixlen
= 0; /* any address */
1047 h
->prefixlen
= -1; /* host address */
1055 host_dns
(const char *s
, struct server
*new_srv
, int max
,
1056 in_port_t port
, const char *ifname
, int ipproto
)
1058 struct addrinfo hints
, *res0
, *res
;
1060 struct sockaddr_in
*sain
;
1061 struct sockaddr_in6
*sin6
;
1064 if
((cnt
= host_if
(s
, new_srv
, max
, port
, ifname
, ipproto
)) != 0)
1067 memset
(&hints
, 0, sizeof
(hints
));
1068 hints.ai_family
= PF_UNSPEC
;
1069 hints.ai_socktype
= SOCK_DGRAM
; /* DUMMY */
1070 hints.ai_flags
= AI_ADDRCONFIG
;
1071 error = getaddrinfo
(s
, NULL
, &hints
, &res0
);
1072 if
(error == EAI_AGAIN ||
error == EAI_NODATA ||
error == EAI_NONAME
)
1075 log_warnx
("%s: could not parse \"%s\": %s", __func__
, s
,
1076 gai_strerror
(error));
1080 for
(res
= res0
; res
&& cnt
< max
; res
= res
->ai_next
) {
1081 if
(res
->ai_family
!= AF_INET
&&
1082 res
->ai_family
!= AF_INET6
)
1084 if
((h
= calloc
(1, sizeof
(*h
))) == NULL
)
1089 if
(ifname
!= NULL
) {
1090 if
(strlcpy
(h
->ifname
, ifname
, sizeof
(h
->ifname
)) >=
1091 sizeof
(h
->ifname
)) {
1092 log_warnx
("%s: interface name truncated",
1100 h
->ipproto
= ipproto
;
1101 h
->ss.ss_family
= res
->ai_family
;
1102 h
->prefixlen
= -1; /* host address */
1104 if
(res
->ai_family
== AF_INET
) {
1105 struct sockaddr_in
*ra
;
1106 sain
= (struct sockaddr_in
*)&h
->ss
;
1107 ra
= (struct sockaddr_in
*)res
->ai_addr
;
1108 got_sockaddr_inet_init
(sain
, &ra
->sin_addr
);
1110 struct sockaddr_in6
*ra
;
1111 sin6
= (struct sockaddr_in6
*)&h
->ss
;
1112 ra
= (struct sockaddr_in6
*)res
->ai_addr
;
1113 got_sockaddr_inet6_init
(sin6
, &ra
->sin6_addr
, 0);
1116 if
(add_addr
(new_srv
, h
))
1120 if
(cnt
== max
&& res
) {
1121 log_warnx
("%s: %s resolves to more than %d hosts", __func__
,
1129 host_if
(const char *s
, struct server
*new_srv
, int max
,
1130 in_port_t port
, const char *ifname
, int ipproto
)
1132 struct ifaddrs
*ifap
, *p
;
1133 struct sockaddr_in
*sain
;
1134 struct sockaddr_in6
*sin6
;
1138 if
(getifaddrs
(&ifap
) == -1)
1139 fatal
("getifaddrs");
1141 /* First search for IPv4 addresses */
1145 for
(p
= ifap
; p
!= NULL
&& cnt
< max
; p
= p
->ifa_next
) {
1146 if
(p
->ifa_addr
== NULL ||
1147 p
->ifa_addr
->sa_family
!= af ||
1148 (strcmp
(s
, p
->ifa_name
) != 0 &&
1149 !is_if_in_group
(p
->ifa_name
, s
)))
1151 if
((h
= calloc
(1, sizeof
(*h
))) == NULL
)
1156 if
(ifname
!= NULL
) {
1157 if
(strlcpy
(h
->ifname
, ifname
, sizeof
(h
->ifname
)) >=
1158 sizeof
(h
->ifname
)) {
1159 log_warnx
("%s: interface name truncated",
1167 h
->ipproto
= ipproto
;
1168 h
->ss.ss_family
= af
;
1169 h
->prefixlen
= -1; /* host address */
1171 if
(af
== AF_INET
) {
1172 struct sockaddr_in
*ra
;
1173 sain
= (struct sockaddr_in
*)&h
->ss
;
1174 ra
= (struct sockaddr_in
*)p
->ifa_addr
;
1175 got_sockaddr_inet_init
(sain
, &ra
->sin_addr
);
1177 struct sockaddr_in6
*ra
;
1178 sin6
= (struct sockaddr_in6
*)&h
->ss
;
1179 ra
= (struct sockaddr_in6
*)p
->ifa_addr
;
1180 got_sockaddr_inet6_init
(sin6
, &ra
->sin6_addr
,
1184 if
(add_addr
(new_srv
, h
))
1188 if
(af
== AF_INET
) {
1189 /* Next search for IPv6 addresses */
1195 log_warnx
("%s: %s resolves to more than %d hosts", __func__
,
1203 host
(const char *s
, struct server
*new_srv
, int max
,
1204 in_port_t port
, const char *ifname
, int ipproto
)
1217 if
(ifname
!= NULL
) {
1218 if
(strlcpy
(h
->ifname
, ifname
, sizeof
(h
->ifname
)) >=
1219 sizeof
(h
->ifname
)) {
1220 log_warnx
("%s: interface name truncated",
1227 h
->ipproto
= ipproto
;
1229 if
(add_addr
(new_srv
, h
))
1234 return
(host_dns
(s
, new_srv
, max
, port
, ifname
, ipproto
));
1238 is_if_in_group
(const char *ifname
, const char *groupname
)
1240 /* TA: Check this... */
1241 #ifdef HAVE_STRUCT_IFGROUPREQ
1243 struct ifgroupreq ifgr
;
1244 struct ifg_req
*ifg
;
1248 if
((s
= socket
(AF_INET
, SOCK_DGRAM
, 0)) == -1)
1251 memset
(&ifgr
, 0, sizeof
(ifgr
));
1252 if
(strlcpy
(ifgr.ifgr_name
, ifname
, IFNAMSIZ
) >= IFNAMSIZ
)
1254 if
(ioctl
(s
, SIOCGIFGROUP
, (caddr_t
)&ifgr
) == -1) {
1255 if
(errno
== EINVAL || errno
== ENOTTY
)
1257 err
(1, "SIOCGIFGROUP");
1260 len
= ifgr.ifgr_len
;
1261 ifgr.ifgr_groups
= calloc
(len
/ sizeof
(struct ifg_req
),
1262 sizeof
(struct ifg_req
));
1263 if
(ifgr.ifgr_groups
== NULL
)
1264 err
(1, "getifgroups");
1265 if
(ioctl
(s
, SIOCGIFGROUP
, (caddr_t
)&ifgr
) == -1)
1266 err
(1, "SIOCGIFGROUP");
1268 ifg
= ifgr.ifgr_groups
;
1269 for
(; ifg
&& len
>= sizeof
(struct ifg_req
); ifg
++) {
1270 len
-= sizeof
(struct ifg_req
);
1271 if
(strcmp
(ifg
->ifgrq_group
, groupname
) == 0) {
1276 free
(ifgr.ifgr_groups
);
1287 get_addrs
(const char *addr
, struct server
*new_srv
, in_port_t port
)
1289 if
(strcmp
("", addr
) == 0) {
1290 if
(host
("127.0.0.1", new_srv
, 1, port
, "127.0.0.1",
1292 yyerror("invalid listen ip: %s",
1296 if
(host
("::1", new_srv
, 1, port
, "::1", -1) <= 0) {
1297 yyerror("invalid listen ip: %s", "::1");
1301 if
(host
(addr
, new_srv
, GOTWEBD_MAXIFACE
, port
, addr
,
1303 yyerror("invalid listen ip: %s", addr
);
1311 addr_dup_check
(struct addresslist
*al
, struct address
*h
, const char *new_srv
,
1312 const char *other_srv
)
1316 char buf
[INET6_ADDRSTRLEN
];
1317 const char *addrstr
;
1319 TAILQ_FOREACH
(a
, al
, entry
) {
1320 if
(memcmp
(&a
->ss
, &h
->ss
, sizeof
(h
->ss
)) != 0 ||
1324 switch
(h
->ss.ss_family
) {
1326 ia
= &((struct sockaddr_in
*)(&h
->ss
))->sin_addr
;
1329 ia
= &((struct sockaddr_in6
*)(&h
->ss
))->sin6_addr
;
1332 yyerror("unknown address family: %d", h
->ss.ss_family
);
1335 addrstr
= inet_ntop
(h
->ss.ss_family
, ia
, buf
, sizeof
(buf
));
1338 yyerror("server %s: duplicate fcgi listen "
1339 "address %s:%d, already used by server %s",
1340 new_srv
, addrstr
, h
->port
, other_srv
);
1342 log_warnx
("server: %s: duplicate fcgi listen "
1343 "address %s:%d", new_srv
, addrstr
, h
->port
);
1347 yyerror("server: %s: duplicate fcgi listen "
1348 "address, already used by server %s",
1349 new_srv
, other_srv
);
1351 log_warnx
("server %s: duplicate fcgi listen "
1352 "address", new_srv
);
1363 add_addr
(struct server
*new_srv
, struct address
*h
)
1367 /* Address cannot be shared between different servers. */
1368 TAILQ_FOREACH
(srv
, &gotwebd
->servers
, entry
) {
1371 if
(addr_dup_check
(&srv
->al
, h
, new_srv
->name
, srv
->name
))
1375 /* Tolerate duplicate address lines within the scope of a server. */
1376 if
(addr_dup_check
(&new_srv
->al
, h
, NULL
, NULL
) == 0)
1377 TAILQ_INSERT_TAIL
(&new_srv
->al
, h
, entry
);