]> jfr.im git - solanum.git/blobdiff - librb/src/commio.c
librb: Fix nossl
[solanum.git] / librb / src / commio.c
index 9fe8689c3c12005c5fde83bb6160c6121c59526e..c3f4d122162d64e976bb955ed034908340fc32ac 100644 (file)
@@ -68,10 +68,8 @@ static int number_fd = 0;
 int rb_maxconnections = 0;
 
 static PF rb_connect_timeout;
-static PF rb_connect_tryconnect;
-#ifdef RB_IPV6
+static PF rb_connect_outcome;
 static void mangle_mapped_sockaddr(struct sockaddr *in);
-#endif
 
 #ifndef HAVE_SOCKETPAIR
 static int rb_inet_socketpair(int d, int type, int protocol, rb_platform_fd_t sv[2]);
@@ -110,6 +108,16 @@ free_fds(void)
        RB_DLINK_FOREACH_SAFE(ptr, next, closed_list.head)
        {
                F = ptr->data;
+
+               number_fd--;
+
+#ifdef _WIN32
+               if(F->type & (RB_FD_SOCKET | RB_FD_PIPE))
+                       closesocket(F->fd);
+               else
+#endif
+                       close(F->fd);
+
                rb_dlinkDelete(ptr, &closed_list);
                rb_bh_free(fd_heap, F);
        }
@@ -304,7 +312,7 @@ rb_settimeout(rb_fde_t *F, time_t timeout, PF * callback, void *cbdata)
  * this will happen.
  */
 void
-rb_checktimeouts(void *notused)
+rb_checktimeouts(void *notused __attribute__((unused)))
 {
        rb_dlink_node *ptr, *next;
        struct timeout_data *td;
@@ -331,16 +339,192 @@ rb_checktimeouts(void *notused)
        }
 }
 
-static void
-rb_accept_tryaccept(rb_fde_t *F, void *data)
+static int
+rb_setsockopt_reuseaddr(rb_fde_t *F)
+{
+       int opt_one = 1;
+       int ret;
+
+       ret = setsockopt(F->fd, SOL_SOCKET, SO_REUSEADDR, &opt_one, sizeof(opt_one));
+       if (ret) {
+               rb_lib_log("rb_setsockopt_reuseaddr: Cannot set SO_REUSEADDR for FD %d: %s",
+                               F->fd, strerror(rb_get_sockerr(F)));
+               return ret;
+       }
+
+       return 0;
+}
+
+#ifdef HAVE_LIBSCTP
+static int
+rb_setsockopt_sctp(rb_fde_t *F)
+{
+       int opt_zero = 0;
+       int opt_one = 1;
+       /* workaround for https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/net/sctp?id=299ee123e19889d511092347f5fc14db0f10e3a6 */
+       char *env_mapped = getenv("SCTP_I_WANT_MAPPED_V4_ADDR");
+       int opt_mapped = env_mapped != NULL ? atoi(env_mapped) : opt_zero;
+       int ret;
+       struct sctp_initmsg initmsg;
+       struct sctp_rtoinfo rtoinfo;
+       struct sctp_paddrparams paddrparams;
+       struct sctp_assocparams assocparams;
+
+       ret = setsockopt(F->fd, SOL_SCTP, SCTP_NODELAY, &opt_one, sizeof(opt_one));
+       if (ret) {
+               rb_lib_log("rb_setsockopt_sctp: Cannot set SCTP_NODELAY for fd %d: %s",
+                               F->fd, strerror(rb_get_sockerr(F)));
+               return ret;
+       }
+
+       ret = setsockopt(F->fd, SOL_SCTP, SCTP_I_WANT_MAPPED_V4_ADDR, &opt_mapped, sizeof(opt_mapped));
+       if (ret) {
+               rb_lib_log("rb_setsockopt_sctp: Cannot unset SCTP_I_WANT_MAPPED_V4_ADDR for fd %d: %s",
+                               F->fd, strerror(rb_get_sockerr(F)));
+               return ret;
+       }
+
+       /* Configure INIT message to specify that we only want one stream */
+       memset(&initmsg, 0, sizeof(initmsg));
+       initmsg.sinit_num_ostreams = 1;
+       initmsg.sinit_max_instreams = 1;
+
+       ret = setsockopt(F->fd, SOL_SCTP, SCTP_INITMSG, &initmsg, sizeof(initmsg));
+       if (ret) {
+               rb_lib_log("rb_setsockopt_sctp: Cannot set SCTP_INITMSG for fd %d: %s",
+                               F->fd, strerror(rb_get_sockerr(F)));
+               return ret;
+       }
+
+       /* Configure RTO values to reduce the maximum timeout */
+       memset(&rtoinfo, 0, sizeof(rtoinfo));
+       rtoinfo.srto_initial = 3000;
+       rtoinfo.srto_min = 1000;
+       rtoinfo.srto_max = 10000;
+
+       ret = setsockopt(F->fd, SOL_SCTP, SCTP_RTOINFO, &rtoinfo, sizeof(rtoinfo));
+       if (ret) {
+               rb_lib_log("rb_setsockopt_sctp: Cannot set SCTP_RTOINFO for fd %d: %s",
+                               F->fd, strerror(rb_get_sockerr(F)));
+               return ret;
+       }
+
+       /*
+        * Configure peer address parameters to ensure that we monitor the connection
+        * more often than the default and don't timeout retransmit attempts before
+        * the ping timeout does.
+        *
+        * Each peer address will timeout reachability in about 750s.
+        */
+       memset(&paddrparams, 0, sizeof(paddrparams));
+       paddrparams.spp_assoc_id = 0;
+       memcpy(&paddrparams.spp_address, &in6addr_any, sizeof(in6addr_any));
+       paddrparams.spp_pathmaxrxt = 50;
+       paddrparams.spp_hbinterval = 5000;
+       paddrparams.spp_flags |= SPP_HB_ENABLE;
+
+       ret = setsockopt(F->fd, SOL_SCTP, SCTP_PEER_ADDR_PARAMS, &paddrparams, sizeof(paddrparams));
+       if (ret) {
+               rb_lib_log("rb_setsockopt_sctp: Cannot set SCTP_PEER_ADDR_PARAMS for fd %d: %s",
+                               F->fd, strerror(rb_get_sockerr(F)));
+               return ret;
+       }
+
+       /* Configure association parameters for retransmit attempts as above */
+       memset(&assocparams, 0, sizeof(assocparams));
+       assocparams.sasoc_assoc_id = 0;
+       assocparams.sasoc_asocmaxrxt = 50;
+
+       ret = setsockopt(F->fd, SOL_SCTP, SCTP_ASSOCINFO, &assocparams, sizeof(assocparams));
+       if (ret) {
+               rb_lib_log("rb_setsockopt_sctp: Cannot set SCTP_ASSOCINFO for fd %d: %s",
+                               F->fd, strerror(rb_get_sockerr(F)));
+               return ret;
+       }
+
+       return 0;
+}
+#endif
+
+int
+rb_bind(rb_fde_t *F, struct sockaddr *addr)
+{
+       int ret;
+
+       ret = rb_setsockopt_reuseaddr(F);
+       if (ret)
+               return ret;
+
+       ret = bind(F->fd, addr, GET_SS_LEN(addr));
+       if (ret)
+               return ret;
+
+       return 0;
+}
+
+#ifdef HAVE_LIBSCTP
+static int
+rb_sctp_bindx_only(rb_fde_t *F, struct sockaddr_storage *addrs, size_t len)
+{
+       int ret;
+
+       for (size_t i = 0; i < len; i++) {
+               if (GET_SS_FAMILY(&addrs[i]) == AF_UNSPEC)
+                       continue;
+
+               ret = sctp_bindx(F->fd, (struct sockaddr *)&addrs[i], 1, SCTP_BINDX_ADD_ADDR);
+               if (ret)
+                       return ret;
+       }
+
+       return 0;
+}
+#endif
+
+int
+rb_sctp_bindx(rb_fde_t *F, struct sockaddr_storage *addrs, size_t len)
 {
+#ifdef HAVE_LIBSCTP
+       int ret;
+
+       if ((F->type & RB_FD_SCTP) == 0)
+               return -1;
+
+       ret = rb_setsockopt_reuseaddr(F);
+       if (ret)
+               return ret;
+
+       ret = rb_sctp_bindx_only(F, addrs, len);
+       if (ret)
+               return ret;
+
+       return 0;
+#else
+       return -1;
+#endif
+}
+
+int
+rb_inet_get_proto(rb_fde_t *F)
+{
+#ifdef HAVE_LIBSCTP
+       if (F->type & RB_FD_SCTP)
+               return IPPROTO_SCTP;
+#endif
+       return IPPROTO_TCP;
+}
+
+static void rb_accept_tryaccept(rb_fde_t *F, void *data __attribute__((unused))) {
        struct rb_sockaddr_storage st;
        rb_fde_t *new_F;
-       rb_socklen_t addrlen = sizeof(st);
+       rb_socklen_t addrlen;
        int new_fd;
 
        while(1)
        {
+               memset(&st, 0, sizeof(st));
+               addrlen = sizeof(st);
+
                new_fd = accept(F->fd, (struct sockaddr *)&st, &addrlen);
                rb_get_errno();
                if(new_fd < 0)
@@ -351,12 +535,12 @@ rb_accept_tryaccept(rb_fde_t *F, void *data)
 
                rb_fd_hack(&new_fd);
 
-               new_F = rb_open(new_fd, RB_FD_SOCKET, "Incoming Connection");
+               new_F = rb_open(new_fd, RB_FD_SOCKET | (F->type & RB_FD_INHERIT_TYPES), "Incoming Connection");
 
                if(new_F == NULL)
                {
                        rb_lib_log
-                               ("rb_accept: new_F == NULL on incoming connection. Closing new_fd == %d\n",
+                               ("rb_accept: new_F == NULL on incoming connection. Closing new_fd == %d",
                                 new_fd);
                        close(new_fd);
                        continue;
@@ -369,9 +553,7 @@ rb_accept_tryaccept(rb_fde_t *F, void *data)
                        rb_close(new_F);
                }
 
-#ifdef RB_IPV6
                mangle_mapped_sockaddr((struct sockaddr *)&st);
-#endif
 
                if(F->accept->precb != NULL)
                {
@@ -410,10 +592,10 @@ rb_accept_tcp(rb_fde_t *F, ACPRE * precb, ACCB * callback, void *data)
 
 /*
  * void rb_connect_tcp(rb_platform_fd_t fd, struct sockaddr *dest,
- *                       struct sockaddr *clocal, int socklen,
+ *                       struct sockaddr *clocal,
  *                       CNCB *callback, void *data, int timeout)
  * Input: An fd to connect with, a host and port to connect to,
- *        a local sockaddr to connect from + length(or NULL to use the
+ *        a local sockaddr to connect from (or NULL to use the
  *        default), a callback, the data to pass into the callback, the
  *        address family.
  * Output: None.
@@ -423,9 +605,11 @@ rb_accept_tcp(rb_fde_t *F, ACPRE * precb, ACCB * callback, void *data)
  */
 void
 rb_connect_tcp(rb_fde_t *F, struct sockaddr *dest,
-              struct sockaddr *clocal, int socklen, CNCB * callback, void *data, int timeout)
+              struct sockaddr *clocal, CNCB * callback, void *data, int timeout)
 {
-       if(F == NULL)
+       int retval;
+
+       if (F == NULL)
                return;
 
        lrb_assert(callback);
@@ -442,7 +626,7 @@ rb_connect_tcp(rb_fde_t *F, struct sockaddr *dest,
         * virtual host IP, for completeness.
         *   -- adrian
         */
-       if((clocal != NULL) && (bind(F->fd, clocal, socklen) < 0))
+       if((clocal != NULL) && (bind(F->fd, clocal, GET_SS_LEN(clocal)) < 0))
        {
                /* Failure, call the callback with RB_ERR_BIND */
                rb_connect_callback(F, RB_ERR_BIND);
@@ -453,9 +637,107 @@ rb_connect_tcp(rb_fde_t *F, struct sockaddr *dest,
        /* We have a valid IP, so we just call tryconnect */
        /* Make sure we actually set the timeout here .. */
        rb_settimeout(F, timeout, rb_connect_timeout, NULL);
-       rb_connect_tryconnect(F, NULL);
+
+       retval = connect(F->fd,
+                        (struct sockaddr *)&F->connect->hostaddr,
+                        GET_SS_LEN(&F->connect->hostaddr));
+       /* Error? */
+       if (retval < 0) {
+               /*
+                * If we get EISCONN, then we've already connect()ed the socket,
+                * which is a good thing.
+                *   -- adrian
+                */
+               rb_get_errno();
+               if (errno == EISCONN) {
+                       rb_connect_callback(F, RB_OK);
+               } else if (rb_ignore_errno(errno)) {
+                       /* Ignore error? Reschedule */
+                       rb_setselect(F, RB_SELECT_CONNECT, rb_connect_outcome, NULL);
+               } else {
+                       /* Error? Fail with RB_ERR_CONNECT */
+                       rb_connect_callback(F, RB_ERR_CONNECT);
+               }
+               return;
+       }
+       /* If we get here, we've succeeded, so call with RB_OK */
+       rb_connect_callback(F, RB_OK);
 }
 
+void
+rb_connect_sctp(rb_fde_t *F, struct sockaddr_storage *dest, size_t dest_len,
+       struct sockaddr_storage *clocal, size_t clocal_len,
+       CNCB *callback, void *data, int timeout)
+{
+#ifdef HAVE_LIBSCTP
+       uint8_t packed_dest[sizeof(struct sockaddr_storage) * dest_len];
+       uint8_t *p = &packed_dest[0];
+       size_t n = 0;
+       int retval;
+
+       if (F == NULL)
+               return;
+
+       lrb_assert(callback);
+       F->connect = rb_malloc(sizeof(struct conndata));
+       F->connect->callback = callback;
+       F->connect->data = data;
+
+       if ((F->type & RB_FD_SCTP) == 0) {
+               rb_connect_callback(F, RB_ERR_CONNECT);
+               return;
+       }
+
+       for (size_t i = 0; i < dest_len; i++) {
+               if (GET_SS_FAMILY(&dest[i]) == AF_INET6) {
+                       memcpy(p, &dest[i], sizeof(struct sockaddr_in6));
+                       n++;
+                       p += sizeof(struct sockaddr_in6);
+               } else if (GET_SS_FAMILY(&dest[i]) == AF_INET) {
+                       memcpy(p, &dest[i], sizeof(struct sockaddr_in));
+                       n++;
+                       p += sizeof(struct sockaddr_in);
+               }
+       }
+       dest_len = n;
+
+       memcpy(&F->connect->hostaddr, &dest[0], sizeof(F->connect->hostaddr));
+
+       if ((clocal_len > 0) && (rb_sctp_bindx_only(F, clocal, clocal_len) < 0)) {
+               /* Failure, call the callback with RB_ERR_BIND */
+               rb_connect_callback(F, RB_ERR_BIND);
+               /* ... and quit */
+               return;
+       }
+
+       rb_settimeout(F, timeout, rb_connect_timeout, NULL);
+
+       retval = sctp_connectx(F->fd, (struct sockaddr *)packed_dest, dest_len, NULL);
+       /* Error? */
+       if (retval < 0) {
+               /*
+                * If we get EISCONN, then we've already connect()ed the socket,
+                * which is a good thing.
+                *   -- adrian
+                */
+               rb_get_errno();
+               if (errno == EISCONN) {
+                       rb_connect_callback(F, RB_OK);
+               } else if (rb_ignore_errno(errno)) {
+                       /* Ignore error? Reschedule */
+                       rb_setselect(F, RB_SELECT_CONNECT, rb_connect_outcome, NULL);
+               } else {
+                       /* Error? Fail with RB_ERR_CONNECT */
+                       rb_connect_callback(F, RB_ERR_CONNECT);
+               }
+               return;
+       }
+       /* If we get here, we've succeeded, so call with RB_OK */
+       rb_connect_callback(F, RB_OK);
+#else
+       rb_connect_callback(F, RB_ERR_CONNECT);
+#endif
+}
 
 /*
  * rb_connect_callback() - call the callback, and continue with life
@@ -490,51 +772,34 @@ rb_connect_callback(rb_fde_t *F, int status)
  * called ..
  */
 static void
-rb_connect_timeout(rb_fde_t *F, void *notused)
+rb_connect_timeout(rb_fde_t *F, void *notused __attribute__((unused)))
 {
        /* error! */
        rb_connect_callback(F, RB_ERR_TIMEOUT);
 }
 
-/* static void rb_connect_tryconnect(rb_platform_fd_t fd, void *notused)
- * Input: The fd, the handler data(unused).
- * Output: None.
- * Side-effects: Try and connect with pending connect data for the FD. If
- *               we succeed or get a fatal error, call the callback.
- *               Otherwise, it is still blocking or something, so register
- *               to select for a write event on this FD.
- */
 static void
-rb_connect_tryconnect(rb_fde_t *F, void *notused)
+rb_connect_outcome(rb_fde_t *F, void *notused __attribute__((unused)))
 {
        int retval;
+       int err = 0;
+       socklen_t len = sizeof(err);
 
        if(F == NULL || F->connect == NULL || F->connect->callback == NULL)
                return;
-       /* Try the connect() */
-       retval = connect(F->fd,
-                        (struct sockaddr *)&F->connect->hostaddr,
-                        GET_SS_LEN(&F->connect->hostaddr));
-       /* Error? */
-       if(retval < 0)
-       {
-               /*
-                * If we get EISCONN, then we've already connect()ed the socket,
-                * which is a good thing.
-                *   -- adrian
-                */
+       retval = getsockopt(F->fd, SOL_SOCKET, SO_ERROR, &err, &len);
+       if (retval < 0) {
                rb_get_errno();
-               if(errno == EISCONN)
-                       rb_connect_callback(F, RB_OK);
-               else if(rb_ignore_errno(errno))
-                       /* Ignore error? Reschedule */
-                       rb_setselect(F, RB_SELECT_CONNECT, rb_connect_tryconnect, NULL);
-               else
-                       /* Error? Fail with RB_ERR_CONNECT */
-                       rb_connect_callback(F, RB_ERR_CONNECT);
+       } else if (err != 0) {
+               errno = err;
+               retval = -1;
+       }
+       if (retval < 0) {
+               /* Error? Fail with RB_ERR_CONNECT */
+               rb_connect_callback(F, RB_ERR_CONNECT);
                return;
        }
-       /* If we get here, we've suceeded, so call with RB_OK */
+       /* If we get here, we've succeeded, so call with RB_OK */
        rb_connect_callback(F, RB_OK);
 }
 
@@ -695,23 +960,25 @@ rb_socket(int family, int sock_type, int proto, const char *note)
        if(rb_unlikely(fd < 0))
                return NULL;    /* errno will be passed through, yay.. */
 
-#if defined(RB_IPV6) && defined(IPV6_V6ONLY)
        /*
         * Make sure we can take both IPv4 and IPv6 connections
-        * on an AF_INET6 socket
+        * on an AF_INET6 SCTP socket, otherwise keep them separate
         */
        if(family == AF_INET6)
        {
-               int off = 1;
-               if(setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, (void *) &off, sizeof(off)) == -1)
+#ifdef HAVE_LIBSCTP
+               int v6only = (proto == IPPROTO_SCTP) ? 0 : 1;
+#else
+               int v6only = 1;
+#endif
+               if(setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, (void *) &v6only, sizeof(v6only)) == -1)
                {
-                       rb_lib_log("rb_socket: Could not set IPV6_V6ONLY option to 1 on FD %d: %s",
-                                  fd, strerror(errno));
+                       rb_lib_log("rb_socket: Could not set IPV6_V6ONLY option to %d on FD %d: %s",
+                                  v6only, fd, strerror(errno));
                        close(fd);
                        return NULL;
                }
        }
-#endif
 
        F = rb_open(fd, RB_FD_SOCKET, note);
        if(F == NULL)
@@ -721,6 +988,20 @@ rb_socket(int family, int sock_type, int proto, const char *note)
                close(fd);
                return NULL;
        }
+
+#ifdef HAVE_LIBSCTP
+       if (proto == IPPROTO_SCTP) {
+               F->type |= RB_FD_SCTP;
+
+               if (rb_setsockopt_sctp(F)) {
+                       rb_lib_log("rb_socket: Could not set SCTP socket options on FD %d: %s",
+                               fd, strerror(errno));
+                       close(fd);
+                       return NULL;
+               }
+       }
+#endif
+
        /* Set the socket non-blocking, and other wonderful bits */
        if(rb_unlikely(!rb_set_nb(F)))
        {
@@ -736,15 +1017,11 @@ rb_socket(int family, int sock_type, int proto, const char *note)
  * If a sockaddr_storage is AF_INET6 but is a mapped IPv4
  * socket manged the sockaddr.
  */
-#ifdef RB_IPV6
 static void
 mangle_mapped_sockaddr(struct sockaddr *in)
 {
        struct sockaddr_in6 *in6 = (struct sockaddr_in6 *)in;
 
-       if(in->sa_family == AF_INET)
-               return;
-
        if(in->sa_family == AF_INET6 && IN6_IS_ADDR_V4MAPPED(&in6->sin6_addr))
        {
                struct sockaddr_in in4;
@@ -754,9 +1031,7 @@ mangle_mapped_sockaddr(struct sockaddr *in)
                in4.sin_addr.s_addr = ((uint32_t *)&in6->sin6_addr)[3];
                memcpy(in, &in4, sizeof(struct sockaddr_in));
        }
-       return;
 }
-#endif
 
 /*
  * rb_listen() - listen on a port
@@ -766,7 +1041,7 @@ rb_listen(rb_fde_t *F, int backlog, int defer_accept)
 {
        int result;
 
-       F->type = RB_FD_SOCKET | RB_FD_LISTEN;
+       F->type = RB_FD_SOCKET | RB_FD_LISTEN | (F->type & RB_FD_INHERIT_TYPES);
        result = listen(F->fd, backlog);
 
 #ifdef TCP_DEFER_ACCEPT
@@ -869,6 +1144,11 @@ rb_close(rb_fde_t *F)
                lrb_assert(F->read_handler == NULL);
                lrb_assert(F->write_handler == NULL);
        }
+
+       if (type & RB_FD_LISTEN) {
+               listen(F->fd, 0);
+       }
+
        rb_setselect(F, RB_SELECT_WRITE | RB_SELECT_READ, NULL, NULL);
        rb_settimeout(F, 0, NULL, NULL);
        rb_free(F->accept);
@@ -886,17 +1166,8 @@ rb_close(rb_fde_t *F)
                ClearFDOpen(F);
        }
 
-       number_fd--;
-
-#ifdef _WIN32
-       if(type & (RB_FD_SOCKET | RB_FD_PIPE))
-       {
-               closesocket(fd);
-               return;
-       }
-       else
-#endif
-               close(fd);
+       if(type & RB_FD_LISTEN)
+               shutdown(fd, SHUT_RDWR);
 }
 
 
@@ -1180,9 +1451,7 @@ inetntoa(const char *in)
  */
 
 static const char *inet_ntop4(const unsigned char *src, char *dst, unsigned int size);
-#ifdef RB_IPV6
 static const char *inet_ntop6(const unsigned char *src, char *dst, unsigned int size);
-#endif
 
 /* const char *
  * inet_ntop4(src, dst, size)
@@ -1209,7 +1478,6 @@ inet_ntop4(const unsigned char *src, char *dst, unsigned int size)
  * author:
  *     Paul Vixie, 1996.
  */
-#ifdef RB_IPV6
 static const char *
 inet_ntop6(const unsigned char *src, char *dst, unsigned int size)
 {
@@ -1314,27 +1582,25 @@ inet_ntop6(const unsigned char *src, char *dst, unsigned int size)
        }
        return memcpy(dst, tmp, tp - tmp);
 }
-#endif
 
 int
-rb_inet_pton_sock(const char *src, struct sockaddr *dst)
+rb_inet_pton_sock(const char *src, struct sockaddr_storage *dst)
 {
+       memset(dst, 0, sizeof(*dst));
        if(rb_inet_pton(AF_INET, src, &((struct sockaddr_in *)dst)->sin_addr))
        {
-               ((struct sockaddr_in *)dst)->sin_port = 0;
-               ((struct sockaddr_in *)dst)->sin_family = AF_INET;
+               SET_SS_FAMILY(dst, AF_INET);
+               SET_SS_PORT(dst, 0);
                SET_SS_LEN(dst, sizeof(struct sockaddr_in));
                return 1;
        }
-#ifdef RB_IPV6
        else if(rb_inet_pton(AF_INET6, src, &((struct sockaddr_in6 *)dst)->sin6_addr))
        {
-               ((struct sockaddr_in6 *)dst)->sin6_port = 0;
-               ((struct sockaddr_in6 *)dst)->sin6_family = AF_INET6;
+               SET_SS_FAMILY(dst, AF_INET6);
+               SET_SS_PORT(dst, 0);
                SET_SS_LEN(dst, sizeof(struct sockaddr_in6));
                return 1;
        }
-#endif
        return 0;
 }
 
@@ -1345,16 +1611,11 @@ rb_inet_ntop_sock(struct sockaddr *src, char *dst, unsigned int size)
        {
        case AF_INET:
                return (rb_inet_ntop(AF_INET, &((struct sockaddr_in *)src)->sin_addr, dst, size));
-               break;
-#ifdef RB_IPV6
        case AF_INET6:
                return (rb_inet_ntop
                        (AF_INET6, &((struct sockaddr_in6 *)src)->sin6_addr, dst, size));
-               break;
-#endif
        default:
                return NULL;
-               break;
        }
 }
 
@@ -1373,7 +1634,6 @@ rb_inet_ntop(int af, const void *src, char *dst, unsigned int size)
        {
        case AF_INET:
                return (inet_ntop4(src, dst, size));
-#ifdef RB_IPV6
        case AF_INET6:
                if(IN6_IS_ADDR_V4MAPPED((const struct in6_addr *)src) ||
                   IN6_IS_ADDR_V4COMPAT((const struct in6_addr *)src))
@@ -1382,9 +1642,6 @@ rb_inet_ntop(int af, const void *src, char *dst, unsigned int size)
                                 s6_addr[12], dst, size));
                else
                        return (inet_ntop6(src, dst, size));
-
-
-#endif
        default:
                return (NULL);
        }
@@ -1460,7 +1717,6 @@ inet_pton4(const char *src, unsigned char *dst)
        return (1);
 }
 
-#ifdef RB_IPV6
 /* int
  * inet_pton6(src, dst)
  *     convert presentation level address to network order binary form.
@@ -1573,7 +1829,7 @@ inet_pton6(const char *src, unsigned char *dst)
        memcpy(dst, tmp, IN6ADDRSZ);
        return (1);
 }
-#endif
+
 int
 rb_inet_pton(int af, const char *src, void *dst)
 {
@@ -1581,7 +1837,6 @@ rb_inet_pton(int af, const char *src, void *dst)
        {
        case AF_INET:
                return (inet_pton4(src, dst));
-#ifdef RB_IPV6
        case AF_INET6:
                /* Somebody might have passed as an IPv4 address this is sick but it works */
                if(inet_pton4(src, dst))
@@ -1592,7 +1847,6 @@ rb_inet_pton(int af, const char *src, void *dst)
                }
                else
                        return (inet_pton6(src, dst));
-#endif
        default:
                return (-1);
        }
@@ -2096,7 +2350,6 @@ rb_setup_fd(rb_fde_t *F)
 }
 
 
-
 int
 rb_ignore_errno(int error)
 {
@@ -2195,7 +2448,7 @@ rb_recv_fd_buf(rb_fde_t *F, void *data, size_t datasize, rb_fde_t **xF, int nfds
 
 
 int
-rb_send_fd_buf(rb_fde_t *xF, rb_fde_t **F, int count, void *data, size_t datasize, pid_t pid)
+rb_send_fd_buf(rb_fde_t *xF, rb_fde_t **F, int count, void *data, size_t datasize, pid_t pid __attribute__((unused)))
 {
        struct msghdr msg;
        struct cmsghdr *cmsg;
@@ -2243,7 +2496,7 @@ rb_send_fd_buf(rb_fde_t *xF, rb_fde_t **F, int count, void *data, size_t datasiz
        }
        return sendmsg(rb_get_fd(xF), &msg, MSG_NOSIGNAL);
 }
-#else
+#else /* defined(HAVE_SENDMSG) && !defined(WIN32) */
 #ifndef _WIN32
 int
 rb_recv_fd_buf(rb_fde_t *F, void *data, size_t datasize, rb_fde_t **xF, int nfds)
@@ -2258,5 +2511,30 @@ rb_send_fd_buf(rb_fde_t *xF, rb_fde_t **F, int count, void *data, size_t datasiz
        errno = ENOSYS;
        return -1;
 }
-#endif
-#endif
+#endif /* _WIN32 */
+#endif /* defined(HAVE_SENDMSG) && !defined(WIN32) */
+
+int
+rb_ipv4_from_ipv6(const struct sockaddr_in6 *restrict ip6, struct sockaddr_in *restrict ip4)
+{
+       int i;
+
+       if (!memcmp(ip6->sin6_addr.s6_addr, "\x20\x02", 2))
+       {
+               /* 6to4 and similar */
+               memcpy(&ip4->sin_addr, ip6->sin6_addr.s6_addr + 2, 4);
+       }
+       else if (!memcmp(ip6->sin6_addr.s6_addr, "\x20\x01\x00\x00", 4))
+       {
+               /* Teredo */
+               for (i = 0; i < 4; i++)
+                       ((uint8_t *)&ip4->sin_addr)[i] = 0xFF ^
+                               ip6->sin6_addr.s6_addr[12 + i];
+       }
+       else
+               return 0;
+       SET_SS_LEN(ip4, sizeof(struct sockaddr_in));
+       ip4->sin_family = AF_INET;
+       ip4->sin_port = 0;
+       return 1;
+}