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_reference.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 *, const char *, struct server
*);
98 int addr_dup_check
(struct addresslist
*, struct address
*,
99 const char *, const char *);
100 int add_addr
(struct server
*, struct address
*);
112 %token LISTEN WWW_PATH SITE_NAME SITE_OWNER SITE_LINK LOGO
113 %token LOGO_URL SHOW_REPO_OWNER SHOW_REPO_AGE SHOW_REPO_DESCRIPTION
114 %token MAX_REPOS_DISPLAY REPOS_PATH MAX_COMMITS_DISPLAY ON ERROR
115 %token SHOW_SITE_OWNER SHOW_REPO_CLONEURL PORT PREFORK RESPECT_EXPORTOK
116 %token UNIX_SOCKET UNIX_SOCKET_NAME SERVER CHROOT CUSTOM_CSS SOCKET
117 %token SUMMARY_COMMITS_DISPLAY SUMMARY_TAGS_DISPLAY
119 %token
<v.
string> STRING
120 %token
<v.number
> NUMBER
121 %type
<v.number
> boolean
122 %type
<v.
string> listen_addr
126 grammar
: /* empty */
128 | grammar varset
'\n'
130 | grammar server
'\n'
131 | grammar
error '\n' { file
->errors
++; }
134 varset
: STRING
'=' STRING
{
137 if
(isspace
((unsigned char)*s
)) {
138 yyerror("macro name cannot contain "
145 if
(symset
($1, $3, 0) == -1)
146 fatal
("cannot store variable");
153 if
(strcasecmp
($1, "1") == 0 ||
154 strcasecmp
($1, "on") == 0)
156 else if
(strcasecmp
($1, "0") == 0 ||
157 strcasecmp
($1, "off") == 0)
160 yyerror("invalid boolean value '%s'", $1);
168 if
($1 != 0 && $1 != 1) {
169 yyerror("invalid boolean value '%lld'", $1);
176 listen_addr
: '*' { $$
= NULL
; }
180 main
: PREFORK NUMBER
{
181 if
($2 <= 0 ||
$2 > PROC_MAX_INSTANCES
) {
182 yyerror("prefork is %s: %lld",
183 $2 <= 0 ?
"too small" : "too large", $2);
186 gotwebd
->prefork_gotwebd
= $2;
190 yyerror("chroot path can't be an empty"
196 n
= strlcpy
(gotwebd
->httpd_chroot
, $2,
197 sizeof
(gotwebd
->httpd_chroot
));
198 if
(n
>= sizeof
(gotwebd
->httpd_chroot
)) {
199 yyerror("%s: httpd_chroot truncated", __func__
);
205 | UNIX_SOCKET boolean
{
206 gotwebd
->unix_socket
= $2;
208 | UNIX_SOCKET_NAME STRING
{
209 n
= snprintf
(gotwebd
->unix_socket_name
,
210 sizeof
(gotwebd
->unix_socket_name
), "%s%s",
211 gotwebd
->httpd_chroot
, $2);
213 (size_t)n
>= sizeof
(gotwebd
->unix_socket_name
)) {
214 yyerror("%s: unix_socket_name truncated",
223 server
: SERVER STRING
{
226 TAILQ_FOREACH
(srv
, &gotwebd
->servers
, entry
) {
227 if
(strcmp
(srv
->name
, $2) == 0) {
228 yyerror("server name exists '%s'", $2);
234 new_srv
= conf_new_server
($2);
235 log_debug
("adding server %s", $2);
241 TAILQ_FOREACH
(srv
, &gotwebd
->servers
, entry
) {
242 if
(strcmp
(srv
->name
, $2) == 0) {
243 yyerror("server name exists '%s'", $2);
249 new_srv
= conf_new_server
($2);
250 log_debug
("adding server %s", $2);
252 } '{' optnl serveropts2
'}' {
256 serveropts1
: REPOS_PATH STRING
{
257 n
= strlcpy
(new_srv
->repos_path
, $2,
258 sizeof
(new_srv
->repos_path
));
259 if
(n
>= sizeof
(new_srv
->repos_path
)) {
260 yyerror("%s: repos_path truncated", __func__
);
267 n
= strlcpy
(new_srv
->site_name
, $2,
268 sizeof
(new_srv
->site_name
));
269 if
(n
>= sizeof
(new_srv
->site_name
)) {
270 yyerror("%s: site_name truncated", __func__
);
276 | SITE_OWNER STRING
{
277 n
= strlcpy
(new_srv
->site_owner
, $2,
278 sizeof
(new_srv
->site_owner
));
279 if
(n
>= sizeof
(new_srv
->site_owner
)) {
280 yyerror("%s: site_owner truncated", __func__
);
287 n
= strlcpy
(new_srv
->site_link
, $2,
288 sizeof
(new_srv
->site_link
));
289 if
(n
>= sizeof
(new_srv
->site_link
)) {
290 yyerror("%s: site_link truncated", __func__
);
297 n
= strlcpy
(new_srv
->logo
, $2, sizeof
(new_srv
->logo
));
298 if
(n
>= sizeof
(new_srv
->logo
)) {
299 yyerror("%s: logo truncated", __func__
);
306 n
= strlcpy
(new_srv
->logo_url
, $2,
307 sizeof
(new_srv
->logo_url
));
308 if
(n
>= sizeof
(new_srv
->logo_url
)) {
309 yyerror("%s: logo_url truncated", __func__
);
315 | CUSTOM_CSS STRING
{
316 n
= strlcpy
(new_srv
->custom_css
, $2,
317 sizeof
(new_srv
->custom_css
));
318 if
(n
>= sizeof
(new_srv
->custom_css
)) {
319 yyerror("%s: custom_css truncated", __func__
);
325 | LISTEN ON listen_addr PORT STRING
{
326 if
(get_addrs
($3, $5, new_srv
) == -1) {
327 yyerror("could not get addrs");
332 new_srv
->fcgi_socket
= 1;
334 | LISTEN ON listen_addr PORT NUMBER
{
338 n
= snprintf
(portno
, sizeof
(portno
), "%lld",
340 if
(n
< 0 ||
(size_t)n
>= sizeof
(portno
))
341 fatalx
("port number too long: %lld",
344 if
(get_addrs
($3, portno
, new_srv
) == -1) {
345 yyerror("could not get addrs");
349 new_srv
->fcgi_socket
= 1;
351 | LISTEN ON SOCKET STRING
{
352 if
(strcasecmp
($4, "off") == 0) {
353 new_srv
->unix_socket
= 0;
358 new_srv
->unix_socket
= 1;
360 n
= snprintf
(new_srv
->unix_socket_name
,
361 sizeof
(new_srv
->unix_socket_name
), "%s%s",
362 gotwebd
->httpd_chroot
, $4);
364 (size_t)n
>= sizeof
(new_srv
->unix_socket_name
)) {
365 yyerror("%s: unix_socket_name truncated",
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
{
392 yyerror("max_repos_display is too small: %lld",
396 new_srv
->max_repos_display
= $2;
398 | MAX_COMMITS_DISPLAY NUMBER
{
400 yyerror("max_commits_display is too small:"
404 new_srv
->max_commits_display
= $2;
406 | SUMMARY_COMMITS_DISPLAY NUMBER
{
408 yyerror("summary_commits_display is too small:"
412 new_srv
->summary_commits_display
= $2;
414 | SUMMARY_TAGS_DISPLAY NUMBER
{
416 yyerror("summary_tags_display is too small:"
420 new_srv
->summary_tags_display
= $2;
424 serveropts2
: serveropts2 serveropts1 nl
431 optnl
: '\n' optnl
/* zero or more newlines */
443 yyerror(const char *fmt
, ...
)
450 if
(vasprintf
(&msg
, fmt
, ap
) == -1)
451 fatalx
("yyerror vasprintf");
453 logit
(LOG_CRIT
, "%s:%d: %s", file
->name
, yylval.lineno
, msg
);
459 kw_cmp
(const void *k
, const void *e
)
461 return
(strcmp
(k
, ((const struct keywords
*)e
)->k_name
));
467 /* This has to be sorted always. */
468 static const struct keywords keywords
[] = {
469 { "chroot", CHROOT
},
470 { "custom_css", CUSTOM_CSS
},
471 { "listen", LISTEN
},
473 { "logo_url", LOGO_URL
},
474 { "max_commits_display", MAX_COMMITS_DISPLAY
},
475 { "max_repos_display", MAX_REPOS_DISPLAY
},
478 { "prefork", PREFORK
},
479 { "repos_path", REPOS_PATH
},
480 { "respect_exportok", RESPECT_EXPORTOK
},
481 { "server", SERVER
},
482 { "show_repo_age", SHOW_REPO_AGE
},
483 { "show_repo_cloneurl", SHOW_REPO_CLONEURL
},
484 { "show_repo_description", SHOW_REPO_DESCRIPTION
},
485 { "show_repo_owner", SHOW_REPO_OWNER
},
486 { "show_site_owner", SHOW_SITE_OWNER
},
487 { "site_link", SITE_LINK
},
488 { "site_name", SITE_NAME
},
489 { "site_owner", SITE_OWNER
},
490 { "socket", SOCKET
},
491 { "summary_commits_display", SUMMARY_COMMITS_DISPLAY
},
492 { "summary_tags_display", SUMMARY_TAGS_DISPLAY
},
493 { "unix_socket", UNIX_SOCKET
},
494 { "unix_socket_name", UNIX_SOCKET_NAME
},
496 const struct keywords
*p
;
498 p
= bsearch
(s
, keywords
, sizeof
(keywords
)/sizeof
(keywords
[0]),
499 sizeof
(keywords
[0]), kw_cmp
);
507 #define MAXPUSHBACK 128
509 unsigned char *parsebuf
;
511 unsigned char pushback_buffer
[MAXPUSHBACK
];
512 int pushback_index
= 0;
520 /* Read character from the parsebuffer instead of input. */
521 if
(parseindex
>= 0) {
522 c
= parsebuf
[parseindex
++];
531 return
(pushback_buffer
[--pushback_index
]);
534 c
= getc
(file
->stream
);
536 yyerror("reached end of file while parsing "
541 c
= getc
(file
->stream
);
543 next
= getc
(file
->stream
);
548 yylval.lineno
= file
->lineno
;
550 c
= getc
(file
->stream
);
566 if
(pushback_index
< MAXPUSHBACK
-1)
567 return
(pushback_buffer
[pushback_index
++] = c
);
579 /* Skip to either EOF or the first real EOL. */
582 c
= pushback_buffer
[--pushback_index
];
598 unsigned char buf
[8096];
599 unsigned char *p
, *val
;
606 while
(c
== ' ' || c
== '\t')
607 c
= lgetc
(0); /* nothing */
609 yylval.lineno
= file
->lineno
;
612 while
(c
!= '\n' && c
!= EOF
)
613 c
= lgetc
(0); /* nothing */
615 if
(c
== '$' && parsebuf
== NULL
) {
621 if
(p
+ 1 >= buf
+ sizeof
(buf
) - 1) {
622 yyerror("string too long");
625 if
(isalnum
(c
) || c
== '_') {
635 yyerror("macro '%s' not defined", buf
);
654 } else if
(c
== '\\') {
655 next
= lgetc
(quotec
);
658 if
(next
== quotec || c
== ' ' || c
== '\t')
660 else if
(next
== '\n') {
665 } else if
(c
== quotec
) {
668 } else if
(c
== '\0') {
669 yyerror("syntax error");
672 if
(p
+ 1 >= buf
+ sizeof
(buf
) - 1) {
673 yyerror("string too long");
678 yylval.v.
string = strdup
(buf
);
679 if
(yylval.v.
string == NULL
)
680 err
(1, "yylex: strdup");
684 #define allowed_to_end_number(x) \
685 (isspace
(x
) || x
== ')' || x
==',' || x
== '/' || x
== '}' || x
== '=')
687 if
(c
== '-' || isdigit
(c
)) {
690 if
((unsigned)(p
-buf
) >= sizeof
(buf
)) {
691 yyerror("string too long");
695 } while
(c
!= EOF
&& isdigit
(c
));
697 if
(p
== buf
+ 1 && buf
[0] == '-')
699 if
(c
== EOF || allowed_to_end_number
(c
)) {
700 const char *errstr
= NULL
;
703 yylval.v.number
= strtonum
(buf
, LLONG_MIN
,
706 yyerror("\"%s\" invalid number: %s",
721 #define allowed_in_string(x) \
722 (isalnum
(x
) ||
(ispunct
(x
) && x
!= '(' && x
!= ')' && \
723 x
!= '{' && x
!= '}' && \
724 x
!= '!' && x
!= '=' && x
!= '#' && \
727 if
(isalnum
(c
) || c
== ':' || c
== '_') {
730 if
((unsigned)(p
-buf
) >= sizeof
(buf
)) {
731 yyerror("string too long");
735 } while
(c
!= EOF
&& (allowed_in_string
(c
)));
739 if
(token
== STRING
) {
740 yylval.v.
string = strdup
(buf
);
741 if
(yylval.v.
string == NULL
)
742 err
(1, "yylex: strdup");
747 yylval.lineno
= file
->lineno
;
756 check_file_secrecy
(int fd
, const char *fname
)
760 if
(fstat
(fd
, &st
)) {
761 log_warn
("cannot stat %s", fname
);
764 if
(st.st_uid
!= 0 && st.st_uid
!= getuid
()) {
765 log_warnx
("%s: owner not root or current user", fname
);
768 if
(st.st_mode
& (S_IWGRP | S_IXGRP | S_IRWXO
)) {
769 log_warnx
("%s: group writable or world read/writable", fname
);
776 newfile
(const char *name
, int secret
)
780 nfile
= calloc
(1, sizeof
(struct file
));
785 nfile
->name
= strdup
(name
);
786 if
(nfile
->name
== NULL
) {
791 nfile
->stream
= fopen
(nfile
->name
, "r");
792 if
(nfile
->stream
== NULL
) {
793 /* no warning, we don't require a conf file */
798 check_file_secrecy
(fileno
(nfile
->stream
), nfile
->name
)) {
799 fclose
(nfile
->stream
);
809 closefile
(struct file
*xfile
)
811 fclose
(xfile
->stream
);
817 add_default_server
(void)
819 new_srv
= conf_new_server
(D_SITENAME
);
820 log_debug
("%s: adding default server %s", __func__
, D_SITENAME
);
824 parse_config
(const char *filename
, struct gotwebd
*env
)
826 struct sym
*sym
, *next
;
828 if
(config_init
(env
) == -1)
829 fatalx
("failed to initialize configuration");
833 file
= newfile
(filename
, 0);
835 add_default_server
();
836 sockets_parse_sockets
(env
);
837 /* just return, as we don't require a conf file */
842 errors
= file
->errors
;
845 /* Free macros and check which have not been used. */
846 TAILQ_FOREACH_SAFE
(sym
, &symhead
, entry
, next
) {
847 if
((gotwebd
->gotwebd_verbose
> 1) && !sym
->used
)
848 fprintf
(stderr
, "warning: macro '%s' not used\n",
853 TAILQ_REMOVE
(&symhead
, sym
, entry
);
861 /* just add default server if no config specified */
862 if
(gotwebd
->server_cnt
== 0)
863 add_default_server
();
865 /* setup our listening sockets */
866 sockets_parse_sockets
(env
);
872 conf_new_server
(const char *name
)
874 struct server
*srv
= NULL
;
876 srv
= calloc
(1, sizeof
(*srv
));
878 fatalx
("%s: calloc", __func__
);
880 n
= strlcpy
(srv
->name
, name
, sizeof
(srv
->name
));
881 if
(n
>= sizeof
(srv
->name
))
882 fatalx
("%s: strlcpy", __func__
);
883 n
= snprintf
(srv
->unix_socket_name
,
884 sizeof
(srv
->unix_socket_name
), "%s%s", D_HTTPD_CHROOT
,
886 if
(n
< 0 ||
(size_t)n
>= sizeof
(srv
->unix_socket_name
))
887 fatalx
("%s: snprintf", __func__
);
888 n
= strlcpy
(srv
->repos_path
, D_GOTPATH
,
889 sizeof
(srv
->repos_path
));
890 if
(n
>= sizeof
(srv
->repos_path
))
891 fatalx
("%s: strlcpy", __func__
);
892 n
= strlcpy
(srv
->site_name
, D_SITENAME
,
893 sizeof
(srv
->site_name
));
894 if
(n
>= sizeof
(srv
->site_name
))
895 fatalx
("%s: strlcpy", __func__
);
896 n
= strlcpy
(srv
->site_owner
, D_SITEOWNER
,
897 sizeof
(srv
->site_owner
));
898 if
(n
>= sizeof
(srv
->site_owner
))
899 fatalx
("%s: strlcpy", __func__
);
900 n
= strlcpy
(srv
->site_link
, D_SITELINK
,
901 sizeof
(srv
->site_link
));
902 if
(n
>= sizeof
(srv
->site_link
))
903 fatalx
("%s: strlcpy", __func__
);
904 n
= strlcpy
(srv
->logo
, D_GOTLOGO
,
906 if
(n
>= sizeof
(srv
->logo
))
907 fatalx
("%s: strlcpy", __func__
);
908 n
= strlcpy
(srv
->logo_url
, D_GOTURL
, sizeof
(srv
->logo_url
));
909 if
(n
>= sizeof
(srv
->logo_url
))
910 fatalx
("%s: strlcpy", __func__
);
911 n
= strlcpy
(srv
->custom_css
, D_GOTWEBCSS
, sizeof
(srv
->custom_css
));
912 if
(n
>= sizeof
(srv
->custom_css
))
913 fatalx
("%s: strlcpy", __func__
);
915 srv
->show_site_owner
= D_SHOWSOWNER
;
916 srv
->show_repo_owner
= D_SHOWROWNER
;
917 srv
->show_repo_age
= D_SHOWAGE
;
918 srv
->show_repo_description
= D_SHOWDESC
;
919 srv
->show_repo_cloneurl
= D_SHOWURL
;
920 srv
->respect_exportok
= D_RESPECTEXPORTOK
;
922 srv
->max_repos_display
= D_MAXREPODISP
;
923 srv
->max_commits_display
= D_MAXCOMMITDISP
;
924 srv
->summary_commits_display
= D_MAXSLCOMMDISP
;
925 srv
->summary_tags_display
= D_MAXSLTAGDISP
;
927 srv
->unix_socket
= 1;
928 srv
->fcgi_socket
= 0;
930 TAILQ_INIT
(&srv
->al
);
931 TAILQ_INSERT_TAIL
(&gotwebd
->servers
, srv
, entry
);
932 gotwebd
->server_cnt
++;
938 symset
(const char *nam
, const char *val
, int persist
)
942 TAILQ_FOREACH
(sym
, &symhead
, entry
) {
943 if
(strcmp
(nam
, sym
->nam
) == 0)
948 if
(sym
->persist
== 1)
953 TAILQ_REMOVE
(&symhead
, sym
, entry
);
957 sym
= calloc
(1, sizeof
(*sym
));
961 sym
->nam
= strdup
(nam
);
962 if
(sym
->nam
== NULL
) {
966 sym
->val
= strdup
(val
);
967 if
(sym
->val
== NULL
) {
973 sym
->persist
= persist
;
974 TAILQ_INSERT_TAIL
(&symhead
, sym
, entry
);
979 cmdline_symset
(char *s
)
984 val
= strrchr
(s
, '=');
988 sym
= strndup
(s
, val
- s
);
990 fatal
("%s: strndup", __func__
);
992 ret
= symset
(sym
, val
+ 1, 1);
999 symget
(const char *nam
)
1003 TAILQ_FOREACH
(sym
, &symhead
, entry
) {
1004 if
(strcmp
(nam
, sym
->nam
) == 0) {
1013 get_addrs
(const char *hostname
, const char *servname
, struct server
*new_srv
)
1015 struct addrinfo hints
, *res0
, *res
;
1017 struct sockaddr_in
*sin
;
1018 struct sockaddr_in6
*sin6
;
1021 memset
(&hints
, 0, sizeof
(hints
));
1022 hints.ai_family
= AF_UNSPEC
;
1023 hints.ai_socktype
= SOCK_STREAM
;
1024 hints.ai_flags
= AI_PASSIVE | AI_ADDRCONFIG
;
1025 error = getaddrinfo
(hostname
, servname
, &hints
, &res0
);
1027 log_warnx
("%s: could not parse \"%s:%s\": %s", __func__
,
1028 hostname
, servname
, gai_strerror
(error));
1032 for
(res
= res0
; res
; res
= res
->ai_next
) {
1033 if
((h
= calloc
(1, sizeof
(*h
))) == NULL
)
1036 if
(hostname
== NULL
) {
1037 strlcpy
(h
->ifname
, "*", sizeof
(h
->ifname
));
1039 if
(strlcpy
(h
->ifname
, hostname
, sizeof
(h
->ifname
)) >=
1040 sizeof
(h
->ifname
)) {
1041 log_warnx
("%s: address truncated: %s",
1042 __func__
, hostname
);
1049 h
->ai_family
= res
->ai_family
;
1050 h
->ai_socktype
= res
->ai_socktype
;
1051 h
->ai_protocol
= res
->ai_protocol
;
1052 memcpy
(&h
->ss
, res
->ai_addr
, res
->ai_addrlen
);
1053 h
->slen
= res
->ai_addrlen
;
1055 switch
(res
->ai_family
) {
1057 sin
= (struct sockaddr_in
*)res
->ai_addr
;
1058 h
->port
= ntohs
(sin
->sin_port
);
1061 sin6
= (struct sockaddr_in6
*)res
->ai_addr
;
1062 h
->port
= ntohs
(sin6
->sin6_port
);
1065 fatalx
("unknown address family %d", res
->ai_family
);
1068 if
(add_addr
(new_srv
, h
))
1076 addr_dup_check
(struct addresslist
*al
, struct address
*h
, const char *new_srv
,
1077 const char *other_srv
)
1081 char buf
[INET6_ADDRSTRLEN
];
1082 const char *addrstr
;
1084 TAILQ_FOREACH
(a
, al
, entry
) {
1085 if
(a
->ai_family
!= h
->ai_family ||
1086 a
->ai_socktype
!= h
->ai_socktype ||
1087 a
->ai_protocol
!= h
->ai_protocol ||
1088 a
->slen
!= h
->slen ||
1089 memcmp
(&a
->ss
, &h
->ss
, a
->slen
) != 0)
1092 switch
(h
->ss.ss_family
) {
1094 ia
= &((struct sockaddr_in
*)(&h
->ss
))->sin_addr
;
1097 ia
= &((struct sockaddr_in6
*)(&h
->ss
))->sin6_addr
;
1100 yyerror("unknown address family: %d", h
->ss.ss_family
);
1103 addrstr
= inet_ntop
(h
->ss.ss_family
, ia
, buf
, sizeof
(buf
));
1106 yyerror("server %s: duplicate fcgi listen "
1107 "address %s:%d, already used by server %s",
1108 new_srv
, addrstr
, h
->port
, other_srv
);
1110 log_warnx
("server: %s: duplicate fcgi listen "
1111 "address %s:%d", new_srv
, addrstr
, h
->port
);
1115 yyerror("server: %s: duplicate fcgi listen "
1116 "address, already used by server %s",
1117 new_srv
, other_srv
);
1119 log_warnx
("server %s: duplicate fcgi listen "
1120 "address", new_srv
);
1131 add_addr
(struct server
*new_srv
, struct address
*h
)
1135 /* Address cannot be shared between different servers. */
1136 TAILQ_FOREACH
(srv
, &gotwebd
->servers
, entry
) {
1139 if
(addr_dup_check
(&srv
->al
, h
, new_srv
->name
, srv
->name
))
1143 /* Tolerate duplicate address lines within the scope of a server. */
1144 if
(addr_dup_check
(&new_srv
->al
, h
, NULL
, NULL
) == 0)
1145 TAILQ_INSERT_TAIL
(&new_srv
->al
, h
, entry
);