From 2cb4f7a473a35792f4d7a951ac43bfab79abebec Mon Sep 17 00:00:00 2001
From: Nick Mathewson <nickm@torproject.org>
Date: Tue, 12 Aug 2003 07:43:15 +0000
Subject: [PATCH] Adopt socketpair implementation from perl. For now, always
 use it.

svn:r389
---
 configure.in      |   4 +-
 src/common/util.c | 104 ++++++++++++++++++++++++++++++++++++++++++++++
 src/common/util.h |   2 +
 src/or/dns.c      |   2 +-
 4 files changed, 109 insertions(+), 3 deletions(-)

diff --git a/configure.in b/configure.in
index 83baf2982a..8adc9415d1 100644
--- a/configure.in
+++ b/configure.in
@@ -134,9 +134,9 @@ AC_CHECK_HEADERS(unistd.h string.h signal.h netdb.h ctype.h poll.h sys/poll.h sy
 
 dnl These headers are not essential
 
-AC_CHECK_HEADERS(stdint.h sys/types.h inttypes.h sys/wait.h)
+AC_CHECK_HEADERS(stdint.h sys/types.h inttypes.h sys/wait.h netinet/in.h arpa/inet.h)
 
-AC_CHECK_FUNCS(gettimeofday ftime)
+AC_CHECK_FUNCS(gettimeofday ftime socketpair)
 
 dnl In case we aren't given a working stdint.h, we'll need to grow our own.
 dnl Watch out.
diff --git a/src/common/util.c b/src/common/util.c
index 84f623301f..56d3851296 100644
--- a/src/common/util.c
+++ b/src/common/util.c
@@ -18,6 +18,15 @@
 #ifdef HAVE_FCNTL_H
 #include <fcntl.h>
 #endif
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+#ifdef HAVE_ARPA_INET_H
+#include <arpa/inet.h>
+#endif
 #ifdef HAVE_SYS_TYPES_H
 #include <sys/types.h>
 #endif
@@ -25,6 +34,8 @@
 #include <unistd.h>
 #endif
 #include <assert.h>
+#include <errno.h>
+#include <string.h>
 
 #include "util.h"
 #include "log.h"
@@ -147,3 +158,96 @@ void spawn_exit()
   exit(0);
 #endif
 }
+
+
+/* Fake socket pair over TCP.  Code adapted from perl 5.8.0's util.c */
+int
+tor_socketpair(int family, int type, int protocol, int fd[2])
+{
+#ifdef HAVE_SOCKETPAIR_XXX
+    /* For testing purposes, we never fall back to real socketpairs. */
+    return socketpair(family, type, protocol, fd);
+#else
+    int listener = -1;
+    int connector = -1;
+    int acceptor = -1;
+    struct sockaddr_in listen_addr;
+    struct sockaddr_in connect_addr;
+    size_t size;
+    
+    if (protocol
+#ifdef AF_UNIX
+        || family != AF_UNIX
+#endif
+        ) {
+        errno = EAFNOSUPPORT;
+        return -1;
+    }
+    if (!fd) {
+        errno = EINVAL;
+        return -1;
+    }
+
+    listener = socket(AF_INET, type, 0);
+    if (listener == -1)
+      return -1;
+    memset (&listen_addr, 0, sizeof (listen_addr));
+    listen_addr.sin_family = AF_INET;
+    listen_addr.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
+    listen_addr.sin_port = 0;   /* kernel choses port.  */
+    if (bind(listener, (struct sockaddr *) &listen_addr, sizeof (listen_addr))
+        == -1)
+        goto tidy_up_and_fail;
+    if (listen(listener, 1) == -1)
+        goto tidy_up_and_fail;
+
+    connector = socket(AF_INET, type, 0);
+    if (connector == -1)
+        goto tidy_up_and_fail;
+    /* We want to find out the port number to connect to.  */
+    size = sizeof (connect_addr);
+    if (getsockname(listener, (struct sockaddr *) &connect_addr, &size) == -1)
+        goto tidy_up_and_fail;
+    if (size != sizeof (connect_addr))
+        goto abort_tidy_up_and_fail;
+    if (connect(connector, (struct sockaddr *) &connect_addr,
+                sizeof (connect_addr)) == -1)
+        goto tidy_up_and_fail;
+
+    size = sizeof (listen_addr);
+    acceptor = accept(listener, (struct sockaddr *) &listen_addr, &size);
+    if (acceptor == -1)
+        goto tidy_up_and_fail;
+    if (size != sizeof(listen_addr))
+        goto abort_tidy_up_and_fail;
+    close(listener);
+    /* Now check we are talking to ourself by matching port and host on the
+       two sockets.  */
+    if (getsockname(connector, (struct sockaddr *) &connect_addr, &size) == -1)
+        goto tidy_up_and_fail;
+    if (size != sizeof (connect_addr)
+        || listen_addr.sin_family != connect_addr.sin_family
+        || listen_addr.sin_addr.s_addr != connect_addr.sin_addr.s_addr
+        || listen_addr.sin_port != connect_addr.sin_port) {
+        goto abort_tidy_up_and_fail;
+    }
+    fd[0] = connector;
+    fd[1] = acceptor;
+    return 0;
+
+  abort_tidy_up_and_fail:
+  errno = ECONNABORTED; /* I hope this is portable and appropriate.  */
+  tidy_up_and_fail:
+    {
+        int save_errno = errno;
+        if (listener != -1)
+            close(listener);
+        if (connector != -1)
+	    close(connector);
+        if (acceptor != -1)
+            close(acceptor);
+        errno = save_errno;
+        return -1;
+    }
+#endif
+}
diff --git a/src/common/util.h b/src/common/util.h
index ee3c6eba48..8600387327 100644
--- a/src/common/util.h
+++ b/src/common/util.h
@@ -52,4 +52,6 @@ void set_socket_nonblocking(int socket);
 int spawn_func(int (*func)(void *), void *data);
 void spawn_exit();
 
+int tor_socketpair(int family, int type, int protocol, int fd[2]);
+
 #endif
diff --git a/src/or/dns.c b/src/or/dns.c
index 8e735aa1ae..643c61cc6d 100644
--- a/src/or/dns.c
+++ b/src/or/dns.c
@@ -361,7 +361,7 @@ static int dns_spawn_worker(void) {
   int fd[2];
   connection_t *conn;
 
-  if(socketpair(AF_UNIX, SOCK_STREAM, 0, fd) < 0) {
+  if(tor_socketpair(AF_UNIX, SOCK_STREAM, 0, fd) < 0) {
     perror("socketpair");
     exit(1);
   }
-- 
GitLab