* Access to sockaddr types without breaking C strict aliasing rules.
*/
union sockaddr_union {
-#ifdef NO_IPV6
- struct sockaddr_in storage;
-#else
struct sockaddr_storage storage;
- struct sockaddr_in6 sin6;
-#endif
struct sockaddr sa;
struct sockaddr_in sin;
+#ifndef NO_IPV6
+ struct sockaddr_in6 sin6;
+#endif
struct sockaddr_un su;
};
}
}
+/*
+ * This constructs a SockAddr that points at one specific sub-address
+ * of a parent SockAddr. The returned SockAddr does not own all its
+ * own memory: it points into the old one's data structures, so it
+ * MUST NOT be used after the old one is freed, and it MUST NOT be
+ * passed to sk_addr_free. (The latter is why it's returned by value
+ * rather than dynamically allocated - that should clue in anyone
+ * writing a call to it that something is weird about it.)
+ */
+static struct SockAddr_tag sk_extractaddr_tmp(
+ SockAddr addr, const SockAddrStep *step)
+{
+ struct SockAddr_tag toret;
+ toret = *addr; /* structure copy */
+ toret.refcount = 1;
+
+ if (addr->superfamily == IP) {
+#ifndef NO_IPV6
+ toret.ais = step->ai;
+#else
+ assert(SOCKADDR_FAMILY(addr, *step) == AF_INET);
+ toret.addresses += step->curraddr;
+#endif
+ }
+
+ return toret;
+}
+
int sk_addr_needs_port(SockAddr addr)
{
if (addr->superfamily == UNRESOLVED || addr->superfamily == UNIX) {
if (sock->s >= 0)
close(sock->s);
- plug_log(sock->plug, 0, sock->addr, sock->port, NULL, 0);
+ {
+ struct SockAddr_tag thisaddr = sk_extractaddr_tmp(
+ sock->addr, &sock->step);
+ plug_log(sock->plug, 0, &thisaddr, sock->port, NULL, 0);
+ }
/*
* Open socket.
*/
add234(sktree, sock);
- if (err)
- plug_log(sock->plug, 1, sock->addr, sock->port, strerror(err), err);
+ if (err) {
+ struct SockAddr_tag thisaddr = sk_extractaddr_tmp(
+ sock->addr, &sock->step);
+ plug_log(sock->plug, 1, &thisaddr, sock->port, strerror(err), err);
+ }
return err;
}
}
}
if (ret < 0) {
- /*
- * An error at this point _might_ be an error reported
- * by a non-blocking connect(). So before we return a
- * panic status to the user, let's just see whether
- * that's the case.
- */
- int err = errno;
- if (s->addr) {
- plug_log(s->plug, 1, s->addr, s->port, strerror(err), err);
- while (s->addr && sk_nextaddr(s->addr, &s->step)) {
- err = try_connect(s);
- }
- }
- if (err != 0)
- return plug_closing(s->plug, strerror(err), err, 0);
+ return plug_closing(s->plug, strerror(errno), errno, 0);
} else if (0 == ret) {
s->incomingeof = TRUE; /* stop trying to read now */
uxsel_tell(s);
if (!s->connected) {
/*
* select() reports a socket as _writable_ when an
- * asynchronous connection is completed.
+ * asynchronous connect() attempt either completes or
+ * fails. So first we must find out which.
*/
+ {
+ int err;
+ socklen_t errlen = sizeof(err);
+ char *errmsg = NULL;
+ if (getsockopt(s->s, SOL_SOCKET, SO_ERROR, &err, &errlen)<0) {
+ errmsg = dupprintf("getsockopt(SO_ERROR): %s",
+ strerror(errno));
+ err = errno; /* got to put something in here */
+ } else if (err != 0) {
+ errmsg = dupstr(strerror(err));
+ }
+ if (errmsg) {
+ /*
+ * The asynchronous connection attempt failed.
+ * Report the problem via plug_log, and try again
+ * with the next candidate address, if we have
+ * more than one.
+ */
+ struct SockAddr_tag thisaddr;
+ assert(s->addr);
+
+ thisaddr = sk_extractaddr_tmp(s->addr, &s->step);
+ plug_log(s->plug, 1, &thisaddr, s->port, errmsg, err);
+
+ while (err && s->addr && sk_nextaddr(s->addr, &s->step)) {
+ err = try_connect(s);
+ }
+ if (err)
+ return plug_closing(s->plug, strerror(err), err, 0);
+ if (!s->connected)
+ return 0; /* another async attempt in progress */
+ }
+ }
+
+ /*
+ * If we get here, we've managed to make a connection.
+ */
+ if (s->addr) {
+ sk_addr_free(s->addr);
+ s->addr = NULL;
+ }
s->connected = s->writable = 1;
uxsel_tell(s);
- break;
} else {
int bufsize_before, bufsize_after;
s->writable = 1;
static char *sk_tcp_peer_info(Socket sock)
{
Actual_Socket s = (Actual_Socket) sock;
- struct sockaddr_storage addr;
+ union sockaddr_union addr;
socklen_t addrlen = sizeof(addr);
+#ifndef NO_IPV6
char buf[INET6_ADDRSTRLEN];
+#endif
- if (getpeername(s->s, (struct sockaddr *)&addr, &addrlen) < 0)
+ if (getpeername(s->s, &addr.sa, &addrlen) < 0)
return NULL;
- if (addr.ss_family == AF_INET) {
+ if (addr.storage.ss_family == AF_INET) {
return dupprintf
("%s:%d",
- inet_ntoa(((struct sockaddr_in *)&addr)->sin_addr),
- (int)ntohs(((struct sockaddr_in *)&addr)->sin_port));
+ inet_ntoa(addr.sin.sin_addr),
+ (int)ntohs(addr.sin.sin_port));
#ifndef NO_IPV6
- } else if (addr.ss_family == AF_INET6) {
+ } else if (addr.storage.ss_family == AF_INET6) {
return dupprintf
("[%s]:%d",
- inet_ntop(AF_INET6, &((struct sockaddr_in6 *)&addr)->sin6_addr,
- buf, sizeof(buf)),
- (int)ntohs(((struct sockaddr_in6 *)&addr)->sin6_port));
+ inet_ntop(AF_INET6, &addr.sin6.sin6_addr, buf, sizeof(buf)),
+ (int)ntohs(addr.sin6.sin6_port));
#endif
- } else if (addr.ss_family == AF_UNIX) {
+ } else if (addr.storage.ss_family == AF_UNIX) {
/*
* For Unix sockets, the source address is unlikely to be
* helpful. Instead, we try SO_PEERCRED and try to get the