+
+/* mostly based on perl's emulation of socketpair udp */
+static int
+rb_inet_socketpair_udp(rb_fde_t **newF1, rb_fde_t **newF2)
+{
+ struct sockaddr_in addr[2];
+ rb_socklen_t size = sizeof(struct sockaddr_in);
+ rb_fde_t *F[2];
+ unsigned int fd[2];
+ int i, got;
+ unsigned short port;
+
+ memset(&addr, 0, sizeof(addr));
+
+ for(i = 0; i < 2; i++)
+ {
+ F[i] = rb_socket(AF_INET, SOCK_DGRAM, 0, "udp socketpair");
+ if(F[i] == NULL)
+ goto failed;
+ addr[i].sin_family = AF_INET;
+ addr[i].sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+ addr[i].sin_port = 0;
+ if(bind(rb_get_fd(F[i]), (struct sockaddr *)&addr[i], sizeof(struct sockaddr_in)))
+ goto failed;
+ fd[i] = rb_get_fd(F[i]);
+ }
+
+ for(i = 0; i < 2; i++)
+ {
+ if(getsockname(fd[i], (struct sockaddr *)&addr[i], &size))
+ goto failed;
+ if(size != sizeof(struct sockaddr_in))
+ goto failed;
+ if(connect(fd[!i], (struct sockaddr *)&addr[i], sizeof(struct sockaddr_in)) == -1)
+ goto failed;
+ }
+
+ for(i = 0; i < 2; i++)
+ {
+ port = addr[i].sin_port;
+ got = rb_write(F[i], &port, sizeof(port));
+ if(got != sizeof(port))
+ {
+ if(got == -1)
+ goto failed;
+ goto abort_failed;
+ }
+ }
+
+
+ struct timeval wait = { 0, 100000 };
+
+ int max = fd[1] > fd[0] ? fd[1] : fd[0];
+ fd_set rset;
+ FD_ZERO(&rset);
+ FD_SET(fd[0], &rset);
+ FD_SET(fd[1], &rset);
+ got = select(max + 1, &rset, NULL, NULL, &wait);
+ if(got != 2 || !FD_ISSET(fd[0], &rset) || !FD_ISSET(fd[1], &rset))
+ {
+ if(got == -1)
+ goto failed;
+ goto abort_failed;
+ }
+
+ struct sockaddr_in readfrom;
+ unsigned short buf[2];
+ for(i = 0; i < 2; i++)
+ {
+#ifdef MSG_DONTWAIT
+ int flag = MSG_DONTWAIT
+#else
+ int flag = 0;
+#endif
+ got = recvfrom(rb_get_fd(F[i]), (char *)&buf, sizeof(buf), flag,
+ (struct sockaddr *)&readfrom, &size);
+ if(got == -1)
+ goto failed;
+ if(got != sizeof(port)
+ || size != sizeof(struct sockaddr_in)
+ || buf[0] != (unsigned short)addr[!i].sin_port
+ || readfrom.sin_family != addr[!i].sin_family
+ || readfrom.sin_addr.s_addr != addr[!i].sin_addr.s_addr
+ || readfrom.sin_port != addr[!i].sin_port)
+ goto abort_failed;
+ }
+
+ *newF1 = F[0];
+ *newF2 = F[1];
+ return 0;
+
+#ifdef _WIN32
+#define ECONNABORTED WSAECONNABORTED
+#endif
+
+ abort_failed:
+ rb_get_errno();
+ errno = ECONNABORTED;
+ failed:
+ if(errno != ECONNABORTED)
+ rb_get_errno();
+ int o_errno = errno;
+ if(F[0] != NULL)
+ rb_close(F[0]);
+ if(F[1] != NULL)
+ rb_close(F[1]);
+ errno = o_errno;
+ return -1;
+}
+
+