#! /bin/sh /usr/share/dpatch/dpatch-run
## 04_getpeername.dpatch by Nico Golde <nion@debian.org>
##
## All lines beginning with `## DP:' are a description of the patch.
## DP: No description.

--- a/acconfig.h
+++ b/acconfig.h
@@ -43,6 +43,9 @@ allows socksified DNS */
 /* Prototype and function header for close function */
 #undef CLOSE_SIGNATURE
 
+/* Prototype and function header for getpeername function */
+#undef GETPEERNAME_SIGNATURE
+
 /* Work out which function we have for conversion from string IPs to 
 numerical ones */
 #undef HAVE_INET_ADDR
--- a/config.h.in
+++ b/config.h.in
@@ -46,6 +46,9 @@ allows socksified DNS */
 /* Prototype and function header for close function */
 #undef CLOSE_SIGNATURE
 
+/* Prototype and function header for close function */
+#undef GETPEERNAME_SIGNATURE
+
 /* Work out which function we have for conversion from string IPs to 
 numerical ones */
 #undef HAVE_INET_ADDR
--- a/configure
+++ b/configure
@@ -2225,14 +2225,60 @@ cat >> confdefs.h <<EOF
 EOF
 
 
+
+echo $ac_n "checking for correct getpeername prototype""... $ac_c" 1>&6
+echo "configure:2231: checking for correct getpeername prototype" >&5
+PROTO=
+PROTO1='int __fd, const struct sockaddr * __name, int *__namelen'
+PROTO2='int __fd, const struct sockaddr_in * __name, socklen_t *__namelen'
+PROTO3='int __fd, struct sockaddr * __name, socklen_t *__namelen'
+PROTO4='int __fd, const struct sockaddr * __name, socklen_t *__namelen'
+for testproto in "${PROTO1}" \
+                 "${PROTO2}" \
+                 "${PROTO3}" \
+                 "${PROTO4}" 
+do
+  if test "${PROTO}" = ""; then
+    cat > conftest.$ac_ext <<EOF
+#line 2244 "configure"
+#include "confdefs.h"
+
+      #include <sys/socket.h>
+      int getpeername($testproto);
+    
+int main() {
+
+; return 0; }
+EOF
+if { (eval echo configure:2254: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
+  rm -rf conftest*
+  PROTO="$testproto";
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+fi
+rm -f conftest*
+  fi
+done
+if test "${PROTO}" = ""; then
+  { echo "configure: error: "no match found!"" 1>&2; exit 1; }
+fi
+echo "$ac_t""getpeername(${PROTO})" 1>&6
+cat >> confdefs.h <<EOF
+#define GETPEERNAME_SIGNATURE ${PROTO}
+EOF
+
+
+
+
 echo $ac_n "checking for correct poll prototype""... $ac_c" 1>&6
-echo "configure:2230: checking for correct poll prototype" >&5
+echo "configure:2276: checking for correct poll prototype" >&5
 PROTO=
 for testproto in 'struct pollfd *ufds, unsigned long nfds, int timeout' 
 do
   if test "${PROTO}" = ""; then
     cat > conftest.$ac_ext <<EOF
-#line 2236 "configure"
+#line 2282 "configure"
 #include "confdefs.h"
 
       #include <sys/poll.h>
@@ -2242,7 +2288,7 @@ int main() {
 
 ; return 0; }
 EOF
-if { (eval echo configure:2246: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
+if { (eval echo configure:2292: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
   rm -rf conftest*
   PROTO="$testproto";
 else
--- a/configure.in
+++ b/configure.in
@@ -309,6 +309,34 @@ fi
 AC_MSG_RESULT([close(${PROTO})])
 AC_DEFINE_UNQUOTED(CLOSE_SIGNATURE, [${PROTO}])
 
+
+dnl Find the correct getpeername prototype on this machine 
+AC_MSG_CHECKING(for correct getpeername prototype)
+PROTO=
+PROTO1='int __fd, const struct sockaddr * __name, int *__namelen'
+PROTO2='int __fd, const struct sockaddr_in * __name, socklen_t *__namelen'
+PROTO3='int __fd, struct sockaddr * __name, socklen_t *__namelen'
+PROTO4='int __fd, const struct sockaddr * __name, socklen_t *__namelen'
+for testproto in "${PROTO1}" \
+                 "${PROTO2}" \
+                 "${PROTO3}" \
+                 "${PROTO4}" 
+do
+  if test "${PROTO}" = ""; then
+    AC_TRY_COMPILE([
+      #include <sys/socket.h>
+      int getpeername($testproto);
+    ],,[PROTO="$testproto";],)
+  fi
+done
+if test "${PROTO}" = ""; then
+  AC_MSG_ERROR("no match found!")
+fi
+AC_MSG_RESULT([getpeername(${PROTO})])
+AC_DEFINE_UNQUOTED(GETPEERNAME_SIGNATURE, [${PROTO}])
+
+
+
 dnl Find the correct poll prototype on this machine 
 AC_MSG_CHECKING(for correct poll prototype)
 PROTO=
--- a/tsocks.c
+++ b/tsocks.c
@@ -62,6 +62,7 @@ static int (*realconnect)(CONNECT_SIGNAT
 static int (*realselect)(SELECT_SIGNATURE);
 static int (*realpoll)(POLL_SIGNATURE);
 static int (*realclose)(CLOSE_SIGNATURE);
+static int (*realgetpeername)(GETPEERNAME_SIGNATURE);
 static struct parsedfile *config;
 static struct connreq *requests = NULL;
 static int suid = 0;
@@ -73,6 +74,7 @@ int connect(CONNECT_SIGNATURE);
 int select(SELECT_SIGNATURE);
 int poll(POLL_SIGNATURE);
 int close(CLOSE_SIGNATURE);
+int getpeername(GETPEERNAME_SIGNATURE);
 #ifdef USE_SOCKS_DNS
 int res_init(void);
 #endif
@@ -109,14 +111,15 @@ void _init(void) {
 	/* most programs that are run won't use our services, so     */
 	/* we do our general initialization on first call            */
 
-   /* Determine the logging level */
-   suid = (getuid() != geteuid());
+	/* Determine the logging level */
+	suid = (getuid() != geteuid());
 
 #ifndef USE_OLD_DLSYM
 	realconnect = dlsym(RTLD_NEXT, "connect");
 	realselect = dlsym(RTLD_NEXT, "select");
 	realpoll = dlsym(RTLD_NEXT, "poll");
 	realclose = dlsym(RTLD_NEXT, "close");
+	realgetpeername = dlsym(RTLD_NEXT, "getpeername");
 	#ifdef USE_SOCKS_DNS
 	realresinit = dlsym(RTLD_NEXT, "res_init");
 	#endif
@@ -125,14 +128,15 @@ void _init(void) {
 	realconnect = dlsym(lib, "connect");
 	realselect = dlsym(lib, "select");
 	realpoll = dlsym(lib, "poll");
+	realgetpeername = dlsym(lib, "getpeername");
 	#ifdef USE_SOCKS_DNS
 	realresinit = dlsym(lib, "res_init");
 	#endif
-	dlclose(lib);	
+	dlclose(lib);
 
 	lib = dlopen(LIBC, RTLD_LAZY);
-   realclose = dlsym(lib, "close");
-	dlclose(lib);	
+	realclose = dlsym(lib, "close");
+	dlclose(lib);
 #endif
 }
 
@@ -350,8 +354,10 @@ int select(SELECT_SIGNATURE) {
 
    /* If we're not currently managing any requests we can just 
     * leave here */
-   if (!requests)
+   if (!requests) {
+      show_msg(MSGDEBUG, "No requests waiting, calling real select\n");
       return(realselect(n, readfds, writefds, exceptfds, timeout));
+   }
 
    get_environment();
 
@@ -705,6 +711,50 @@ int close(CLOSE_SIGNATURE) {
    return(rc);
 }
 
+/* If we are not done setting up the connection yet, return
+ * -1 and ENOTCONN, otherwise call getpeername
+ *
+ * This is necessary since some applications, when using non-blocking connect,
+ * (like ircII) use getpeername() to find out if they are connected already.
+ *
+ * This results in races sometimes, where the client sends data to the socket
+ * before we are done with the socks connection setup.  Another solution would
+ * be to intercept send().
+ * 
+ * This could be extended to actually set the peername to the peer the
+ * client application has requested, but not for now.
+ *
+ * PP, Sat, 27 Mar 2004 11:30:23 +0100
+ */
+int getpeername(GETPEERNAME_SIGNATURE) {
+   struct connreq *conn;
+   int rc;
+
+    if (realgetpeername == NULL) {
+        show_msg(MSGERR, "Unresolved symbol: getpeername\n");
+        return(-1);
+    }
+
+   show_msg(MSGDEBUG, "Call to getpeername for fd %d\n", __fd);
+
+
+   rc = realgetpeername(__fd, __name, __namelen);
+   if (rc == -1)
+       return rc;
+
+   /* Are we handling this connect? */
+   if ((conn = find_socks_request(__fd, 1))) {
+       /* While we are at it, we might was well try to do something useful */
+       handle_request(conn);
+
+       if (conn->state != DONE) {
+           errno = ENOTCONN;
+           return(-1);
+       }
+   }
+   return rc;
+}
+
 static struct connreq *new_socks_request(int sockid, struct sockaddr_in *connaddr, 
                                          struct sockaddr_in *serveraddr, 
                                          struct serverent *path) {
@@ -854,7 +904,7 @@ static int connect_server(struct connreq
                     sizeof(conn->serveraddr));
 
    show_msg(MSGDEBUG, "Connect returned %d, errno is %d\n", rc, errno); 
-	if (rc) {
+   if (rc) {
       if (errno != EINPROGRESS) {
          show_msg(MSGERR, "Error %d attempting to connect to SOCKS "
                   "server (%s)\n", errno, strerror(errno));
