diff --git a/include/common/standard.h b/include/common/standard.h index 3990e6ffa..712d94128 100644 --- a/include/common/standard.h +++ b/include/common/standard.h @@ -158,7 +158,7 @@ struct sockaddr_un *str2sun(const char *str); * The format is "addr:port", where "addr" can be a dotted IPv4 address, * a host name, or empty or "*" to indicate INADDR_ANY. */ -struct sockaddr_in *str2sa(char *str); +struct sockaddr_storage *str2sa(char *str); /* * converts to a struct sockaddr_in* which is locally allocated, and a @@ -169,7 +169,7 @@ struct sockaddr_in *str2sa(char *str); * "addr[:port[-port]]", where "addr" can be a dotted IPv4 address, a host * name, or empty or "*" to indicate INADDR_ANY. */ -struct sockaddr_in *str2sa_range(char *str, int *low, int *high); +struct sockaddr_storage *str2sa_range(char *str, int *low, int *high); /* converts to a struct in_addr containing a network mask. It can be * passed in dotted form (255.255.255.0) or in CIDR form (24). It returns 1 @@ -188,12 +188,12 @@ int str2net(const char *str, struct in_addr *addr, struct in_addr *mask); /* * Parse IP address found in url. */ -int url2ip(const char *addr, struct in_addr *dst); +int url2ipv4(const char *addr, struct in_addr *dst); /* - * Resolve destination server from URL. Convert to a sockaddr_in*. + * Resolve destination server from URL. Convert to a sockaddr_storage*. */ -int url2sa(const char *url, int ulen, struct sockaddr_in *addr); +int url2sa(const char *url, int ulen, struct sockaddr_storage *addr); /* will try to encode the string replacing all characters tagged in * with the hexadecimal representation of their ASCII-code (2 digits) @@ -466,4 +466,22 @@ static inline unsigned int __full_hash(unsigned int a) return a * 3221225473U; } +/* returns non-zero if addr has a valid and non-null IPv4 or IPv6 address, + * otherwise zero. + */ +static inline int is_addr(struct sockaddr_storage *addr) +{ + int i; + + switch (addr->ss_family) { + case AF_INET: + return *(int *)&((struct sockaddr_in *)&addr)->sin_addr; + case AF_INET6: + for (i = 0; i < sizeof(struct in6_addr) / sizeof(int); i++) + if (((int *)&((struct sockaddr_in6 *)addr)->sin6_addr)[i] != 0) + return ((int *)&((struct sockaddr_in6 *)addr)->sin6_addr)[i]; + } + return 0; +} + #endif /* _COMMON_STANDARD_H */ diff --git a/include/proto/proto_tcp.h b/include/proto/proto_tcp.h index b871003a4..26e06df8c 100644 --- a/include/proto/proto_tcp.h +++ b/include/proto/proto_tcp.h @@ -27,10 +27,10 @@ #include #include -int tcpv4_bind_socket(int fd, int flags, struct sockaddr_in *local, struct sockaddr_in *remote); +int tcp_bind_socket(int fd, int flags, struct sockaddr_storage *local, struct sockaddr_storage *remote); void tcpv4_add_listener(struct listener *listener); void tcpv6_add_listener(struct listener *listener); -int tcpv4_connect_server(struct stream_interface *si); +int tcp_connect_server(struct stream_interface *si); int tcp_inspect_request(struct session *s, struct buffer *req, int an_bit); int tcp_inspect_response(struct session *s, struct buffer *rep, int an_bit); int tcp_persist_rdp_cookie(struct session *s, struct buffer *req, int an_bit); diff --git a/include/types/peers.h b/include/types/peers.h index b5b92c2b7..24ab804b1 100644 --- a/include/types/peers.h +++ b/include/types/peers.h @@ -72,7 +72,7 @@ struct peer { int line; /* line where the section appears */ } conf; /* config information */ time_t last_change; - struct sockaddr_in addr; /* peer address */ + struct sockaddr_storage addr; /* peer address */ struct peer *next; /* next peer in the list */ }; diff --git a/include/types/proxy.h b/include/types/proxy.h index e724a25b4..95736b183 100644 --- a/include/types/proxy.h +++ b/include/types/proxy.h @@ -182,7 +182,7 @@ struct proxy { unsigned int fe_req_ana, be_req_ana; /* bitmap of common request protocol analysers for the frontend and backend */ unsigned int fe_rsp_ana, be_rsp_ana; /* bitmap of common response protocol analysers for the frontend and backend */ int mode; /* mode = PR_MODE_TCP, PR_MODE_HTTP or PR_MODE_HEALTH */ - struct sockaddr_in dispatch_addr; /* the default address to connect to */ + struct sockaddr_storage dispatch_addr; /* the default address to connect to */ union { struct proxy *be; /* default backend, or NULL if none set */ char *name; /* default backend name during config parse */ @@ -271,9 +271,9 @@ struct proxy { int conn_retries; /* maximum number of connect retries */ int cap; /* supported capabilities (PR_CAP_*) */ - struct sockaddr_in source_addr; /* the address to which we want to bind for connect() */ + struct sockaddr_storage source_addr; /* the address to which we want to bind for connect() */ #if defined(CONFIG_HAP_CTTPROXY) || defined(CONFIG_HAP_LINUX_TPROXY) - struct sockaddr_in tproxy_addr; /* non-local address we want to bind to for connect() */ + struct sockaddr_storage tproxy_addr; /* non-local address we want to bind to for connect() */ char *bind_hdr_name; /* bind to this header name if defined */ int bind_hdr_len; /* length of the name of the header above */ int bind_hdr_occ; /* occurrence number of header above: >0 = from first, <0 = from end, 0=disabled */ diff --git a/include/types/server.h b/include/types/server.h index 00251d8ec..c697457ea 100644 --- a/include/types/server.h +++ b/include/types/server.h @@ -102,10 +102,10 @@ struct server { struct list pendconns; /* pending connections */ struct task *check; /* the task associated to the health check processing */ - struct sockaddr_in addr; /* the address to connect to */ - struct sockaddr_in source_addr; /* the address to which we want to bind for connect() */ + struct sockaddr_storage addr; /* the address to connect to */ + struct sockaddr_storage source_addr; /* the address to which we want to bind for connect() */ #if defined(CONFIG_HAP_CTTPROXY) || defined(CONFIG_HAP_LINUX_TPROXY) - struct sockaddr_in tproxy_addr; /* non-local address we want to bind to for connect() */ + struct sockaddr_storage tproxy_addr; /* non-local address we want to bind to for connect() */ char *bind_hdr_name; /* bind to this header name if defined */ int bind_hdr_len; /* length of the name of the header above */ int bind_hdr_occ; /* occurrence number of header above: >0 = from first, <0 = from end, 0=disabled */ @@ -116,7 +116,7 @@ struct server { struct server *tracknext, *tracked; /* next server in a tracking list, tracked server */ char *trackit; /* temporary variable to make assignment deferrable */ - struct sockaddr_in check_addr; /* the address to check, if different from */ + struct sockaddr_storage check_addr; /* the address to check, if different from */ short check_port; /* the port to use for the health checks */ int health; /* 0->rise-1 = bad; rise->rise+fall-1 = good */ int consecutive_errors; /* current number of consecutive errors */ diff --git a/include/types/stream_interface.h b/include/types/stream_interface.h index 39caf8fe7..16a261fb5 100644 --- a/include/types/stream_interface.h +++ b/include/types/stream_interface.h @@ -183,8 +183,8 @@ struct stream_interface { struct sockaddr_storage to; /* the address reached by the client if SN_FRT_ADDR_SET is set */ } c; /* client side */ struct { - struct sockaddr_in from; /* the address to spoof when connecting to the server (transparent mode) */ - struct sockaddr_in to; /* the address to connect to */ + struct sockaddr_storage from; /* the address to spoof when connecting to the server (transparent mode) */ + struct sockaddr_storage to; /* the address to connect to */ } s; /* server side */ } addr; /* addresses of the remote side */ }; diff --git a/src/backend.c b/src/backend.c index 7a9e0c6b3..e2ad5b2b4 100644 --- a/src/backend.c +++ b/src/backend.c @@ -621,7 +621,8 @@ int assign_server(struct session *s) else if ((s->be->options2 & PR_O2_DISPATCH) || (s->be->options & PR_O_TRANSP)) { set_target_proxy(&s->target, s->be); } - else if ((s->be->options & PR_O_HTTP_PROXY) && s->req->cons->addr.s.to.sin_addr.s_addr) { + else if ((s->be->options & PR_O_HTTP_PROXY) && + is_addr(&s->req->cons->addr.s.to)) { /* in proxy mode, we need a valid destination address */ set_target_proxy(&s->target, s->be); } @@ -677,7 +678,7 @@ int assign_server_address(struct session *s) s->req->cons->addr.s.to = target_srv(&s->target)->addr; - if (!s->req->cons->addr.s.to.sin_addr.s_addr) { + if (!is_addr(&s->req->cons->addr.s.to)) { /* if the server has no address, we use the same address * the client asked, which is handy for remapping ports * locally on multiple addresses at once. @@ -686,22 +687,36 @@ int assign_server_address(struct session *s) get_frt_addr(s); if (s->req->prod->addr.c.to.ss_family == AF_INET) { - s->req->cons->addr.s.to.sin_addr = ((struct sockaddr_in *)&s->req->prod->addr.c.to)->sin_addr; + ((struct sockaddr_in *)&s->req->cons->addr.s.to)->sin_addr = ((struct sockaddr_in *)&s->req->prod->addr.c.to)->sin_addr; + } else if (s->req->prod->addr.c.to.ss_family == AF_INET6) { + ((struct sockaddr_in6 *)&s->req->cons->addr.s.to)->sin6_addr = ((struct sockaddr_in6 *)&s->req->prod->addr.c.to)->sin6_addr; } } /* if this server remaps proxied ports, we'll use * the port the client connected to with an offset. */ if (target_srv(&s->target)->state & SRV_MAPPORTS) { + int base_port; + if (!(s->be->options & PR_O_TRANSP) && !(s->flags & SN_FRT_ADDR_SET)) get_frt_addr(s); - if (s->req->prod->addr.c.to.ss_family == AF_INET) { - s->req->cons->addr.s.to.sin_port = htons(ntohs(s->req->cons->addr.s.to.sin_port) + - ntohs(((struct sockaddr_in *)&s->req->prod->addr.c.to)->sin_port)); + + /* First, retrieve the port from the incoming connection */ + if (s->req->prod->addr.c.to.ss_family == AF_INET) + base_port = ntohs(((struct sockaddr_in *)&s->req->prod->addr.c.to)->sin_port); + else if (s->req->prod->addr.c.to.ss_family == AF_INET6) + base_port = ntohs(((struct sockaddr_in6 *)&s->req->prod->addr.c.to)->sin6_port); + else + base_port = 0; + + /* Second, assign the outgoing connection's port */ + if (s->req->cons->addr.c.to.ss_family == AF_INET) { + ((struct sockaddr_in *)&s->req->cons->addr.s.to)->sin_port = + htons(base_port + ntohs(((struct sockaddr_in *)&s->req->cons->addr.s.to)->sin_port)); } else if (s->req->prod->addr.c.to.ss_family == AF_INET6) { - s->req->cons->addr.s.to.sin_port = htons(ntohs(s->req->cons->addr.s.to.sin_port) + - ntohs(((struct sockaddr_in6 *)&s->req->prod->addr.c.to)->sin6_port)); + ((struct sockaddr_in6 *)&s->req->cons->addr.s.to)->sin6_port = + htons(base_port + ntohs(((struct sockaddr_in6 *)&s->req->cons->addr.s.to)->sin6_port)); } } } @@ -714,7 +729,7 @@ int assign_server_address(struct session *s) if (!(s->flags & SN_FRT_ADDR_SET)) get_frt_addr(s); - if (s->req->prod->addr.c.to.ss_family == AF_INET) { + if (s->req->prod->addr.c.to.ss_family == AF_INET || s->req->prod->addr.c.to.ss_family == AF_INET6) { memcpy(&s->req->cons->addr.s.to, &s->req->prod->addr.c.to, MIN(sizeof(s->req->cons->addr.s.to), sizeof(s->req->prod->addr.c.to))); } /* when we support IPv6 on the backend, we may add other tests */ @@ -869,22 +884,23 @@ static void assign_tproxy_address(struct session *s) if (srv && srv->state & SRV_BIND_SRC) { switch (srv->state & SRV_TPROXY_MASK) { case SRV_TPROXY_ADDR: - s->req->cons->addr.s.from = *(struct sockaddr_in *)&srv->tproxy_addr; + s->req->cons->addr.s.from = srv->tproxy_addr; break; case SRV_TPROXY_CLI: case SRV_TPROXY_CIP: /* FIXME: what can we do if the client connects in IPv6 or unix socket ? */ - s->req->cons->addr.s.from = *(struct sockaddr_in *)&s->req->prod->addr.c.from; + s->req->cons->addr.s.from = s->req->prod->addr.c.from; break; case SRV_TPROXY_DYN: if (srv->bind_hdr_occ) { /* bind to the IP in a header */ - s->req->cons->addr.s.from.sin_port = 0; - s->req->cons->addr.s.from.sin_addr.s_addr = htonl(get_ip_from_hdr2(&s->txn.req, - srv->bind_hdr_name, - srv->bind_hdr_len, - &s->txn.hdr_idx, - srv->bind_hdr_occ)); + ((struct sockaddr_in *)&s->req->cons->addr.s.from)->sin_port = 0; + ((struct sockaddr_in *)&s->req->cons->addr.s.from)->sin_addr.s_addr = + htonl(get_ip_from_hdr2(&s->txn.req, + srv->bind_hdr_name, + srv->bind_hdr_len, + &s->txn.hdr_idx, + srv->bind_hdr_occ)); } break; default: @@ -894,22 +910,23 @@ static void assign_tproxy_address(struct session *s) else if (s->be->options & PR_O_BIND_SRC) { switch (s->be->options & PR_O_TPXY_MASK) { case PR_O_TPXY_ADDR: - s->req->cons->addr.s.from = *(struct sockaddr_in *)&s->be->tproxy_addr; + s->req->cons->addr.s.from = s->be->tproxy_addr; break; case PR_O_TPXY_CLI: case PR_O_TPXY_CIP: /* FIXME: what can we do if the client connects in IPv6 or socket unix? */ - s->req->cons->addr.s.from = *(struct sockaddr_in *)&s->req->prod->addr.c.from; + s->req->cons->addr.s.from = s->req->prod->addr.c.from; break; case PR_O_TPXY_DYN: if (s->be->bind_hdr_occ) { /* bind to the IP in a header */ - s->req->cons->addr.s.from.sin_port = 0; - s->req->cons->addr.s.from.sin_addr.s_addr = htonl(get_ip_from_hdr2(&s->txn.req, - s->be->bind_hdr_name, - s->be->bind_hdr_len, - &s->txn.hdr_idx, - s->be->bind_hdr_occ)); + ((struct sockaddr_in *)&s->req->cons->addr.s.from)->sin_port = 0; + ((struct sockaddr_in *)&s->req->cons->addr.s.from)->sin_addr.s_addr = + htonl(get_ip_from_hdr2(&s->txn.req, + s->be->bind_hdr_name, + s->be->bind_hdr_len, + &s->txn.hdr_idx, + s->be->bind_hdr_occ)); } break; default: @@ -951,7 +968,7 @@ int connect_server(struct session *s) * session's freshly assigned target with the stream interface's. */ stream_sock_prepare_interface(s->req->cons); - s->req->cons->connect = tcpv4_connect_server; + s->req->cons->connect = tcp_connect_server; copy_target(&s->req->cons->target, &s->target); assign_tproxy_address(s); diff --git a/src/cfgparse.c b/src/cfgparse.c index 18cfda39d..aeec87ba2 100644 --- a/src/cfgparse.c +++ b/src/cfgparse.c @@ -937,13 +937,13 @@ int cfg_parse_global(const char *file, int linenum, char **args, int kwm) logsrv.u.un = *sk; logsrv.u.addr.sa_family = AF_UNIX; } else { - struct sockaddr_in *sk = str2sa(args[1]); - if (!sk) { + struct sockaddr_storage *sk = str2sa(args[1]); + if (!sk || sk->ss_family != AF_INET) { Alert("parsing [%s:%d] : Unknown host in '%s'\n", file, linenum, args[1]); err_code |= ERR_ALERT | ERR_FATAL; goto out; } - logsrv.u.in = *sk; + logsrv.u.in = *(struct sockaddr_in *)sk; logsrv.u.addr.sa_family = AF_INET; if (!logsrv.u.in.sin_port) logsrv.u.in.sin_port = htons(SYSLOG_PORT); @@ -1233,7 +1233,7 @@ int cfg_parse_peers(const char *file, int linenum, char **args, int kwm) else if (strcmp(args[0], "peer") == 0) { /* peer definition */ char *rport, *raddr; short realport = 0; - struct sockaddr_in *sk; + struct sockaddr_storage *sk; if (!*args[2]) { Alert("parsing [%s:%d] : '%s' expects and [:] as arguments.\n", @@ -1287,7 +1287,15 @@ int cfg_parse_peers(const char *file, int linenum, char **args, int kwm) goto out; } newpeer->addr = *sk; - newpeer->addr.sin_port = htons(realport); + + switch (newpeer->addr.ss_family) { + case AF_INET: + ((struct sockaddr_in *)&newpeer->addr)->sin_port = htons(realport); + break; + case AF_INET6: + ((struct sockaddr_in6 *)&newpeer->addr)->sin6_port = htons(realport); + break; + } if (strcmp(newpeer->id, localpeer) == 0) { /* Current is local peer, it define a frontend */ @@ -3843,7 +3851,7 @@ stats_error_parsing: curproxy->grace = val; } else if (!strcmp(args[0], "dispatch")) { /* dispatch address */ - struct sockaddr_in *sk; + struct sockaddr_storage *sk; if (curproxy == &defproxy) { Alert("parsing [%s:%d] : '%s' not allowed in 'defaults' section.\n", file, linenum, args[0]); err_code |= ERR_ALERT | ERR_FATAL; @@ -3929,7 +3937,7 @@ stats_error_parsing: } if (!defsrv) { - struct sockaddr_in *sk; + struct sockaddr_storage *sk; if ((newsrv = (struct server *)calloc(1, sizeof(struct server))) == NULL) { Alert("parsing [%s:%d] : out of memory.\n", file, linenum); @@ -3975,7 +3983,15 @@ stats_error_parsing: goto out; } newsrv->addr = *sk; - newsrv->addr.sin_port = htons(realport); + + switch (newsrv->addr.ss_family) { + case AF_INET: + ((struct sockaddr_in *)&newsrv->addr)->sin_port = htons(realport); + break; + case AF_INET6: + ((struct sockaddr_in6 *)&newsrv->addr)->sin6_port = htons(realport); + break; + } newsrv->check_port = curproxy->defsrv.check_port; newsrv->inter = curproxy->defsrv.inter; @@ -4135,7 +4151,7 @@ stats_error_parsing: cur_arg += 2; } else if (!defsrv && !strcmp(args[cur_arg], "addr")) { - struct sockaddr_in *sk = str2sa(args[cur_arg + 1]); + struct sockaddr_storage *sk = str2sa(args[cur_arg + 1]); if (!sk) { Alert("parsing [%s:%d] : Unknown host in '%s'\n", file, linenum, args[cur_arg + 1]); err_code |= ERR_ALERT | ERR_FATAL; @@ -4280,7 +4296,7 @@ stats_error_parsing: } else if (!defsrv && !strcmp(args[cur_arg], "source")) { /* address to which we bind when connecting */ int port_low, port_high; - struct sockaddr_in *sk; + struct sockaddr_storage *sk; if (!*args[cur_arg + 1]) { #if defined(CONFIG_HAP_CTTPROXY) || defined(CONFIG_HAP_LINUX_TPROXY) @@ -4381,7 +4397,7 @@ stats_error_parsing: goto out; } } else { - struct sockaddr_in *sk = str2sa(args[cur_arg + 1]); + struct sockaddr_storage *sk = str2sa(args[cur_arg + 1]); if (!sk) { Alert("parsing [%s:%d] : Unknown host in '%s'\n", file, linenum, args[cur_arg + 1]); err_code |= ERR_ALERT | ERR_FATAL; @@ -4458,8 +4474,16 @@ stats_error_parsing: goto out; } - if (!newsrv->check_port && newsrv->check_addr.sin_port) - newsrv->check_port = newsrv->check_addr.sin_port; + switch (newsrv->check_addr.ss_family) { + case AF_INET: + if (!newsrv->check_port && ((struct sockaddr_in *)&newsrv->check_addr)->sin_port) + newsrv->check_port = ntohs(((struct sockaddr_in *)&newsrv->check_addr)->sin_port); + break; + case AF_INET6: + if (!newsrv->check_port && ((struct sockaddr_in6 *)&newsrv->check_addr)->sin6_port) + newsrv->check_port = ntohs(((struct sockaddr_in6 *)&newsrv->check_addr)->sin6_port); + break; + } if (!newsrv->check_port && !(newsrv->state & SRV_MAPPORTS)) newsrv->check_port = realport; /* by default */ @@ -4557,13 +4581,13 @@ stats_error_parsing: logsrv.u.un = *sk; logsrv.u.addr.sa_family = AF_UNIX; } else { - struct sockaddr_in *sk = str2sa(args[1]); - if (!sk) { + struct sockaddr_storage *sk = str2sa(args[1]); + if (!sk || sk->ss_family != AF_INET) { Alert("parsing [%s:%d] : Unknown host in '%s'\n", file, linenum, args[1]); err_code |= ERR_ALERT | ERR_FATAL; goto out; } - logsrv.u.in = *sk; + logsrv.u.in = *(struct sockaddr_in *)sk; logsrv.u.addr.sa_family = AF_INET; if (!logsrv.u.in.sin_port) { logsrv.u.in.sin_port = @@ -4598,7 +4622,7 @@ stats_error_parsing: } else if (!strcmp(args[0], "source")) { /* address to which we bind when connecting */ int cur_arg; - struct sockaddr_in *sk; + struct sockaddr_storage *sk; if (warnifnotcap(curproxy, PR_CAP_BE, file, linenum, args[0], NULL)) err_code |= ERR_WARN; @@ -4690,7 +4714,7 @@ stats_error_parsing: goto out; } } else { - struct sockaddr_in *sk = str2sa(args[cur_arg + 1]); + struct sockaddr_storage *sk = str2sa(args[cur_arg + 1]); if (!sk) { Alert("parsing [%s:%d] : Unknown host in '%s'\n", file, linenum, args[cur_arg + 1]); err_code |= ERR_ALERT | ERR_FATAL; diff --git a/src/checks.c b/src/checks.c index ba3e745e9..e4982ca5a 100644 --- a/src/checks.c +++ b/src/checks.c @@ -810,10 +810,21 @@ static int event_srv_chk_w(int fd) * - connected (EISCONN, 0) */ - struct sockaddr_in sa; + struct sockaddr_storage sa; - sa = (s->check_addr.sin_addr.s_addr) ? s->check_addr : s->addr; - sa.sin_port = htons(s->check_port); + if (is_addr(&s->check_addr)) + sa = s->check_addr; + else + sa = s->addr; + + switch (s->check_addr.ss_family) { + case AF_INET: + ((struct sockaddr_in *)&sa)->sin_port = htons(s->check_port); + break; + case AF_INET6: + ((struct sockaddr_in6 *)&sa)->sin6_port = htons(s->check_port); + break; + } if (connect(fd, (struct sockaddr *)&sa, sizeof(sa)) == 0) errno = 0; @@ -1193,7 +1204,7 @@ struct task *process_chk(struct task *t) { int attempts = 0; struct server *s = t->context; - struct sockaddr_in sa; + struct sockaddr_storage sa; int fd; int rv; @@ -1223,7 +1234,7 @@ struct task *process_chk(struct task *t) /* we'll initiate a new check */ set_server_check_status(s, HCHK_STATUS_START, NULL); - if ((fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) != -1) { + if ((fd = socket(s->addr.ss_family, SOCK_STREAM, IPPROTO_TCP)) != -1) { if ((fd < global.maxsock) && (fcntl(fd, F_SETFL, O_NONBLOCK) != -1) && (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char *) &one, sizeof(one)) != -1)) { @@ -1233,28 +1244,35 @@ struct task *process_chk(struct task *t) /* We don't want to useless data */ setsockopt(fd, SOL_SOCKET, SO_LINGER, (struct linger *) &nolinger, sizeof(struct linger)); } - - if (s->check_addr.sin_addr.s_addr) + + if (is_addr(&s->check_addr)) /* we'll connect to the check addr specified on the server */ sa = s->check_addr; else /* we'll connect to the addr on the server */ sa = s->addr; - /* we'll connect to the check port on the server */ - sa.sin_port = htons(s->check_port); - + switch (s->check_addr.ss_family) { + case AF_INET: + /* we'll connect to the check port on the server */ + ((struct sockaddr_in *)&sa)->sin_port = htons(s->check_port); + break; + case AF_INET6: + /* we'll connect to the check port on the server */ + ((struct sockaddr_in6 *)&sa)->sin6_port = htons(s->check_port); + break; + } /* allow specific binding : * - server-specific at first * - proxy-specific next */ if (s->state & SRV_BIND_SRC) { - struct sockaddr_in *remote = NULL; + struct sockaddr_storage *remote = NULL; int ret, flags = 0; #if defined(CONFIG_HAP_CTTPROXY) || defined(CONFIG_HAP_LINUX_TPROXY) if ((s->state & SRV_TPROXY_MASK) == SRV_TPROXY_ADDR) { - remote = (struct sockaddr_in *)&s->tproxy_addr; + remote = &s->tproxy_addr; flags = 3; } #endif @@ -1266,7 +1284,7 @@ struct task *process_chk(struct task *t) #endif if (s->sport_range) { int bind_attempts = 10; /* should be more than enough to find a spare port */ - struct sockaddr_in src; + struct sockaddr_storage src; ret = 1; src = s->source_addr; @@ -1287,13 +1305,21 @@ struct task *process_chk(struct task *t) break; fdinfo[fd].port_range = s->sport_range; - src.sin_port = htons(fdinfo[fd].local_port); - ret = tcpv4_bind_socket(fd, flags, &src, remote); + switch (src.ss_family) { + case AF_INET: + ((struct sockaddr_in *)&src)->sin_port = htons(fdinfo[fd].local_port); + break; + case AF_INET6: + ((struct sockaddr_in6 *)&src)->sin6_port = htons(fdinfo[fd].local_port); + break; + } + + ret = tcp_bind_socket(fd, flags, &src, remote); } while (ret != 0); /* binding NOK */ } else { - ret = tcpv4_bind_socket(fd, flags, &s->source_addr, remote); + ret = tcp_bind_socket(fd, flags, &s->source_addr, remote); } if (ret) { @@ -1311,12 +1337,12 @@ struct task *process_chk(struct task *t) } } else if (s->proxy->options & PR_O_BIND_SRC) { - struct sockaddr_in *remote = NULL; + struct sockaddr_storage *remote = NULL; int ret, flags = 0; #if defined(CONFIG_HAP_CTTPROXY) || defined(CONFIG_HAP_LINUX_TPROXY) if ((s->proxy->options & PR_O_TPXY_MASK) == PR_O_TPXY_ADDR) { - remote = (struct sockaddr_in *)&s->proxy->tproxy_addr; + remote = &s->proxy->tproxy_addr; flags = 3; } #endif @@ -1326,7 +1352,7 @@ struct task *process_chk(struct task *t) setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, s->proxy->iface_name, s->proxy->iface_len + 1); #endif - ret = tcpv4_bind_socket(fd, flags, &s->proxy->source_addr, remote); + ret = tcp_bind_socket(fd, flags, &s->proxy->source_addr, remote); if (ret) { set_server_check_status(s, HCHK_STATUS_SOCKERR, NULL); switch (ret) { diff --git a/src/dumpstats.c b/src/dumpstats.c index ed577ad67..b04b297fa 100644 --- a/src/dumpstats.c +++ b/src/dumpstats.c @@ -2111,10 +2111,20 @@ int stats_dump_proxy(struct stream_interface *si, struct proxy *px, struct uri_a chunk_printf(&msg, " title=\"IP: "); /* IP */ - if (inet_ntop(sv->addr.sin_family, &sv->addr.sin_addr, str, sizeof(str))) - chunk_printf(&msg, "%s:%d", str, htons(sv->addr.sin_port)); - else - chunk_printf(&msg, "(%s)", strerror(errno)); + switch (sv->addr.ss_family) { + case AF_INET: + if (inet_ntop(AF_INET, (const void *)&((struct sockaddr_in *)&sv->addr)->sin_addr, str, sizeof(str))) + chunk_printf(&msg, "%s:%d", str, htons(((struct sockaddr_in *)&sv->addr)->sin_port)); + else + chunk_printf(&msg, "(%s)", strerror(errno)); + break; + case AF_INET6: + if (inet_ntop(AF_INET6, (const void *)&((struct sockaddr_in6 *)&sv->addr)->sin6_addr, str, sizeof(str))) + chunk_printf(&msg, "%s:%d", str, htons(((struct sockaddr_in6 *)&sv->addr)->sin6_port)); + else + chunk_printf(&msg, "(%s)", strerror(errno)); + break; + } /* id */ chunk_printf(&msg, ", id: %d", sv->puid); diff --git a/src/peers.c b/src/peers.c index 5b8e7490a..739c09b05 100644 --- a/src/peers.c +++ b/src/peers.c @@ -1174,7 +1174,7 @@ struct session *peer_session_create(struct peer *peer, struct peer_session *ps) s->si[1].conn_retries = p->conn_retries; s->si[1].err_type = SI_ET_NONE; s->si[1].err_loc = NULL; - s->si[1].connect = tcpv4_connect_server; + s->si[1].connect = tcp_connect_server; set_target_proxy(&s->si[1].target, s->be); s->si[1].exp = TICK_ETERNITY; s->si[1].flags = SI_FL_NONE; diff --git a/src/proto_http.c b/src/proto_http.c index cbfdca287..ff2f44cac 100644 --- a/src/proto_http.c +++ b/src/proto_http.c @@ -8037,9 +8037,9 @@ acl_fetch_hdr_ip(struct proxy *px, struct session *l4, void *l7, char *sol, test->flags |= ACL_TEST_F_FETCH_MORE; test->flags |= ACL_TEST_F_VOL_HDR; /* Same optimization as url_ip */ - memset(&l4->req->cons->addr.s.to.sin_addr, 0, sizeof(l4->req->cons->addr.s.to.sin_addr)); - url2ip((char *)ctx->line + ctx->val, &l4->req->cons->addr.s.to.sin_addr); - test->ptr = (void *)&l4->req->cons->addr.s.to.sin_addr; + memset(&((struct sockaddr_in *)&l4->req->cons->addr.s.to)->sin_addr, 0, sizeof(((struct sockaddr_in *)&l4->req->cons->addr.s.to)->sin_addr)); + url2ipv4((char *)ctx->line + ctx->val, &((struct sockaddr_in *)&l4->req->cons->addr.s.to)->sin_addr); + test->ptr = (void *)&((struct sockaddr_in *)&l4->req->cons->addr.s.to)->sin_addr; test->i = AF_INET; return 1; } diff --git a/src/proto_tcp.c b/src/proto_tcp.c index 2d79e22d2..04562f104 100644 --- a/src/proto_tcp.c +++ b/src/proto_tcp.c @@ -100,7 +100,7 @@ static struct protocol proto_tcpv6 = { }; -/* Binds ipv4 address to socket , unless is set, in which +/* Binds ipv4/ipv6 address to socket , unless is set, in which * case we try to bind . is a 2-bit field consisting of : * - 0 : ignore remote address (may even be a NULL pointer) * - 1 : use provided address @@ -114,9 +114,9 @@ static struct protocol proto_tcpv6 = { * This function returns 0 when everything's OK, 1 if it could not bind, to the * local address, 2 if it could not bind to the foreign address. */ -int tcpv4_bind_socket(int fd, int flags, struct sockaddr_in *local, struct sockaddr_in *remote) +int tcp_bind_socket(int fd, int flags, struct sockaddr_storage *local, struct sockaddr_storage *remote) { - struct sockaddr_in bind_addr; + struct sockaddr_storage bind_addr; int foreign_ok = 0; int ret; @@ -132,10 +132,20 @@ int tcpv4_bind_socket(int fd, int flags, struct sockaddr_in *local, struct socka #endif if (flags) { memset(&bind_addr, 0, sizeof(bind_addr)); - if (flags & 1) - bind_addr.sin_addr = remote->sin_addr; - if (flags & 2) - bind_addr.sin_port = remote->sin_port; + switch (remote->ss_family) { + case AF_INET: + if (flags & 1) + ((struct sockaddr_in *)&bind_addr)->sin_addr = ((struct sockaddr_in *)remote)->sin_addr; + if (flags & 2) + ((struct sockaddr_in *)&bind_addr)->sin_port = ((struct sockaddr_in *)remote)->sin_port; + break; + case AF_INET6: + if (flags & 1) + ((struct sockaddr_in6 *)&bind_addr)->sin6_addr = ((struct sockaddr_in6 *)remote)->sin6_addr; + if (flags & 2) + ((struct sockaddr_in6 *)&bind_addr)->sin6_port = ((struct sockaddr_in6 *)remote)->sin6_port; + break; + } } setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *) &one, sizeof(one)); @@ -198,7 +208,7 @@ int tcpv4_bind_socket(int fd, int flags, struct sockaddr_in *local, struct socka * Additionnally, in the case of SN_ERR_RESOURCE, an emergency log will be emitted. */ -int tcpv4_connect_server(struct stream_interface *si) +int tcp_connect_server(struct stream_interface *si) { int fd; struct server *srv; @@ -217,7 +227,7 @@ int tcpv4_connect_server(struct stream_interface *si) return SN_ERR_INTERNAL; } - if ((fd = si->fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) { + if ((fd = si->fd = socket(si->addr.s.to.ss_family, SOCK_STREAM, IPPROTO_TCP)) == -1) { qfprintf(stderr, "Cannot get a server socket.\n"); if (errno == ENFILE) @@ -284,7 +294,7 @@ int tcpv4_connect_server(struct stream_interface *si) if (srv->sport_range) { int attempts = 10; /* should be more than enough to find a spare port */ - struct sockaddr_in src; + struct sockaddr_storage src; ret = 1; src = srv->source_addr; @@ -305,13 +315,20 @@ int tcpv4_connect_server(struct stream_interface *si) break; fdinfo[fd].port_range = srv->sport_range; - src.sin_port = htons(fdinfo[fd].local_port); + switch (src.ss_family) { + case AF_INET: + ((struct sockaddr_in *)&src)->sin_port = htons(fdinfo[fd].local_port); + break; + case AF_INET6: + ((struct sockaddr_in6 *)&src)->sin6_port = htons(fdinfo[fd].local_port); + break; + } - ret = tcpv4_bind_socket(fd, flags, &src, (struct sockaddr_in *)&si->addr.s.from); + ret = tcp_bind_socket(fd, flags, &src, &si->addr.s.from); } while (ret != 0); /* binding NOK */ } else { - ret = tcpv4_bind_socket(fd, flags, &srv->source_addr, (struct sockaddr_in *)&si->addr.s.from); + ret = tcp_bind_socket(fd, flags, &srv->source_addr, &si->addr.s.from); } if (ret) { @@ -354,7 +371,7 @@ int tcpv4_connect_server(struct stream_interface *si) if (be->iface_name) setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, be->iface_name, be->iface_len + 1); #endif - ret = tcpv4_bind_socket(fd, flags, &be->source_addr, (struct sockaddr_in *)&si->addr.s.from); + ret = tcp_bind_socket(fd, flags, &be->source_addr, &si->addr.s.from); if (ret) { close(fd); if (ret == 1) { @@ -389,7 +406,7 @@ int tcpv4_connect_server(struct stream_interface *si) if (global.tune.server_rcvbuf) setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &global.tune.server_rcvbuf, sizeof(global.tune.server_rcvbuf)); - if ((connect(fd, (struct sockaddr *)&si->addr.s.to, sizeof(struct sockaddr_in)) == -1) && + if ((connect(fd, (struct sockaddr *)&si->addr.s.to, sizeof(struct sockaddr_storage)) == -1) && (errno != EINPROGRESS) && (errno != EALREADY) && (errno != EISCONN)) { if (errno == EAGAIN || errno == EADDRINUSE) { @@ -432,7 +449,7 @@ int tcpv4_connect_server(struct stream_interface *si) fdtab[fd].cb[DIR_WR].b = si->ob; fdinfo[fd].peeraddr = (struct sockaddr *)&si->addr.s.to; - fdinfo[fd].peerlen = sizeof(struct sockaddr_in); + fdinfo[fd].peerlen = sizeof(struct sockaddr_storage); fd_insert(fd); EV_FD_SET(fd, DIR_WR); /* for connect status */ diff --git a/src/standard.c b/src/standard.c index 1ab219412..27fa374ea 100644 --- a/src/standard.c +++ b/src/standard.c @@ -219,10 +219,10 @@ const char *invalid_domainchar(const char *name) { * a host name, or empty or "*" to indicate INADDR_ANY. NULL is returned * if the host part cannot be resolved. */ -struct sockaddr_in *str2sa(char *str) +struct sockaddr_storage *str2sa(char *str) { - static struct sockaddr_in sa; - struct sockaddr_in *ret = NULL; + static struct sockaddr_storage sa; + struct sockaddr_storage *ret = NULL; char *c; int port; @@ -238,17 +238,17 @@ struct sockaddr_in *str2sa(char *str) else port = 0; + sa.ss_family = AF_INET; + ((struct sockaddr_in *)&sa)->sin_port = htons(port); if (*str == '*' || *str == '\0') { /* INADDR_ANY */ - sa.sin_addr.s_addr = INADDR_ANY; + ((struct sockaddr_in *)&sa)->sin_addr.s_addr = INADDR_ANY; } - else if (!inet_pton(AF_INET, str, &sa.sin_addr)) { + else if (!inet_pton(sa.ss_family, str, &((struct sockaddr_in *)&sa)->sin_addr)) { struct hostent *he = gethostbyname(str); if (!he) goto out; - sa.sin_addr = *(struct in_addr *) *(he->h_addr_list); + ((struct sockaddr_in *)&sa)->sin_addr = *(struct in_addr *) *(he->h_addr_list); } - sa.sin_port = htons(port); - sa.sin_family = AF_INET; ret = &sa; out: free(str); @@ -265,10 +265,10 @@ struct sockaddr_in *str2sa(char *str) * name, or empty or "*" to indicate INADDR_ANY. NULL is returned if the host * part cannot be resolved. */ -struct sockaddr_in *str2sa_range(char *str, int *low, int *high) +struct sockaddr_storage *str2sa_range(char *str, int *low, int *high) { - static struct sockaddr_in sa; - struct sockaddr_in *ret = NULL; + static struct sockaddr_storage sa; + struct sockaddr_storage *ret = NULL; char *c; int portl, porth; @@ -293,17 +293,17 @@ struct sockaddr_in *str2sa_range(char *str, int *low, int *high) porth = 0; } + sa.ss_family = AF_INET; + ((struct sockaddr_in *)&sa)->sin_port = htonl(portl); if (*str == '*' || *str == '\0') { /* INADDR_ANY */ - sa.sin_addr.s_addr = INADDR_ANY; + ((struct sockaddr_in *)&sa)->sin_addr.s_addr = INADDR_ANY; } - else if (!inet_pton(AF_INET, str, &sa.sin_addr)) { + else if (!inet_pton(sa.ss_family, str, &((struct sockaddr_in *)&sa)->sin_addr)) { struct hostent *he = gethostbyname(str); if (!he) goto out; - sa.sin_addr = *(struct in_addr *) *(he->h_addr_list); + ((struct sockaddr_in *)&sa)->sin_addr = *(struct in_addr *) *(he->h_addr_list); } - sa.sin_port = htons(portl); - sa.sin_family = AF_INET; ret = &sa; *low = portl; @@ -387,9 +387,9 @@ int str2net(const char *str, struct in_addr *addr, struct in_addr *mask) /* - * Parse IP address found in url. + * Parse IPv4 address found in url. */ -int url2ip(const char *addr, struct in_addr *dst) +int url2ipv4(const char *addr, struct in_addr *dst) { int saw_digit, octets, ch; u_char tmp[4], *tp; @@ -430,18 +430,20 @@ int url2ip(const char *addr, struct in_addr *dst) } /* - * Resolve destination server from URL. Convert to a sockaddr_in*. + * Resolve destination server from URL. Convert to a sockaddr_storage*. */ -int url2sa(const char *url, int ulen, struct sockaddr_in *addr) +int url2sa(const char *url, int ulen, struct sockaddr_storage *addr) { const char *curr = url, *cp = url; int ret, url_code = 0; unsigned int http_code = 0; /* Cleanup the room */ - addr->sin_family = AF_INET; - addr->sin_addr.s_addr = 0; - addr->sin_port = 0; + + /* FIXME: assume IPv4 only for now */ + ((struct sockaddr_in *)addr)->sin_family = AF_INET; + ((struct sockaddr_in *)addr)->sin_addr.s_addr = 0; + ((struct sockaddr_in *)addr)->sin_port = 0; /* Firstly, try to find :// pattern */ while (curr < url+ulen && url_code != 0x3a2f2f) { @@ -467,12 +469,12 @@ int url2sa(const char *url, int ulen, struct sockaddr_in *addr) * be warned this can slow down global daemon performances * while handling lagging dns responses. */ - ret = url2ip(curr, &addr->sin_addr); + ret = url2ipv4(curr, &((struct sockaddr_in *)&addr)->sin_addr); if (!ret) return -1; curr += ret; - addr->sin_port = (*curr == ':') ? str2uic(++curr) : 80; - addr->sin_port = htons(addr->sin_port); + ((struct sockaddr_in *)addr)->sin_port = (*curr == ':') ? str2uic(++curr) : 80; + ((struct sockaddr_in *)addr)->sin_port = htons(((struct sockaddr_in *)&addr)->sin_port); } return 0; }