[BUG] stream_sock: use get_addr_len() instead of sizeof() on sockaddr_storage
John Helliwell reported a runtime issue on Solaris since 1.5-dev5. Traces
show that connect() returns EINVAL, which means the socket length is not
appropriate for the family. Solaris does not like being called with sizeof
and needs the address family's size on sockaddr_storage.
The fix consists in adding a get_addr_len() function which returns the
socket's address length based on its family. Tests show that this works
for both IPv4 and IPv6 addresses.
diff --git a/include/common/standard.h b/include/common/standard.h
index e7a1052..0082087 100644
--- a/include/common/standard.h
+++ b/include/common/standard.h
@@ -525,6 +525,20 @@
return 0;
}
+/* returns address len for <addr>'s family, 0 for unknown families */
+static inline int get_addr_len(const struct sockaddr_storage *addr)
+{
+ switch (addr->ss_family) {
+ case AF_INET:
+ return sizeof(struct sockaddr_in);
+ case AF_INET6:
+ return sizeof(struct sockaddr_in6);
+ case AF_UNIX:
+ return sizeof(struct sockaddr_un);
+ }
+ return 0;
+}
+
/* set port in host byte order */
static inline int set_net_port(struct sockaddr_storage *addr, int port)
{
diff --git a/src/checks.c b/src/checks.c
index 147a7ab..4285624 100644
--- a/src/checks.c
+++ b/src/checks.c
@@ -817,16 +817,9 @@
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;
- }
+ set_host_port(&sa, s->check_port);
- if (connect(fd, (struct sockaddr *)&sa, sizeof(sa)) == 0)
+ if (connect(fd, (struct sockaddr *)&sa, get_addr_len(&sa)) == 0)
errno = 0;
if (errno == EALREADY || errno == EINPROGRESS)
@@ -1377,7 +1370,7 @@
if (s->proxy->options2 & PR_O2_SMARTCON)
setsockopt(fd, IPPROTO_TCP, TCP_QUICKACK, (char *) &zero, sizeof(zero));
#endif
- if ((connect(fd, (struct sockaddr *)&sa, sizeof(sa)) != -1) || (errno == EINPROGRESS)) {
+ if ((connect(fd, (struct sockaddr *)&sa, get_addr_len(&sa)) != -1) || (errno == EINPROGRESS)) {
/* OK, connection in progress or established */
//fprintf(stderr, "process_chk: 4\n");
@@ -1390,7 +1383,7 @@
fdtab[fd].cb[DIR_WR].f = &event_srv_chk_w;
fdtab[fd].cb[DIR_WR].b = NULL;
fdinfo[fd].peeraddr = (struct sockaddr *)&sa;
- fdinfo[fd].peerlen = sizeof(sa);
+ fdinfo[fd].peerlen = get_addr_len(&sa);
fdtab[fd].state = FD_STCONN; /* connection in progress */
fdtab[fd].flags = FD_FL_TCP | FD_FL_TCP_NODELAY;
EV_FD_SET(fd, DIR_WR); /* for connect status */
diff --git a/src/log.c b/src/log.c
index eba9adf..58d884a 100644
--- a/src/log.c
+++ b/src/log.c
@@ -289,7 +289,8 @@
/* the total syslog message now starts at logptr, for dataptr+data_len-logptr */
sent = sendto(*plogfd, log_ptr, dataptr + data_len - log_ptr,
- MSG_DONTWAIT | MSG_NOSIGNAL, (struct sockaddr *)&logsrv->addr, sizeof(logsrv->addr));
+ MSG_DONTWAIT | MSG_NOSIGNAL,
+ (struct sockaddr *)&logsrv->addr, get_addr_len(&logsrv->addr));
if (sent < 0) {
Alert("sendto logger #%d failed: %s (errno=%d)\n",
nblogger, strerror(errno), errno);
diff --git a/src/proto_tcp.c b/src/proto_tcp.c
index ca993a6..13ea210 100644
--- a/src/proto_tcp.c
+++ b/src/proto_tcp.c
@@ -150,12 +150,12 @@
setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *) &one, sizeof(one));
if (foreign_ok) {
- ret = bind(fd, (struct sockaddr *)&bind_addr, sizeof(bind_addr));
+ ret = bind(fd, (struct sockaddr *)&bind_addr, get_addr_len(&bind_addr));
if (ret < 0)
return 2;
}
else {
- ret = bind(fd, (struct sockaddr *)local, sizeof(*local));
+ ret = bind(fd, (struct sockaddr *)local, get_addr_len(local));
if (ret < 0)
return 1;
}
@@ -406,7 +406,7 @@
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_storage)) == -1) &&
+ if ((connect(fd, (struct sockaddr *)&si->addr.s.to, get_addr_len(&si->addr.s.to)) == -1) &&
(errno != EINPROGRESS) && (errno != EALREADY) && (errno != EISCONN)) {
if (errno == EAGAIN || errno == EADDRINUSE) {
@@ -449,7 +449,7 @@
fdtab[fd].cb[DIR_WR].b = si->ob;
fdinfo[fd].peeraddr = (struct sockaddr *)&si->addr.s.to;
- fdinfo[fd].peerlen = sizeof(struct sockaddr_storage);
+ fdinfo[fd].peerlen = get_addr_len(&si->addr.s.to);
fd_insert(fd);
EV_FD_SET(fd, DIR_WR); /* for connect status */