From 8a189bbb1223257eef616df61ad87ac585f77127 Mon Sep 17 00:00:00 2001
From: Daniel Stenberg <daniel@haxx.se>
Date: Wed, 21 Dec 2011 10:59:15 +0100
Subject: [PATCH] windows threader resolver: use callback to create dummy
 socket

The threaded resolver fow windows creates a dummy socket internally to
be able to fill in one for select() to remain happy. This was not
created nor closed using the callbacks which could lead to applications
being confused due to the mismatch with what the multi_socket callbacks
or multi_fdset() provided to the application.

This adds a new "purpose" to the CURLOPT_OPENSOCKETFUNCTION callback.
---
 docs/libcurl/curl_easy_setopt.3 |   26 +++++++++++++++++++-------
 include/curl/curl.h             |    2 ++
 lib/asyn-thread.c               |   28 +++++++++++++++++++++-------
 3 files changed, 42 insertions(+), 14 deletions(-)

diff --git a/docs/libcurl/curl_easy_setopt.3 b/docs/libcurl/curl_easy_setopt.3
index cce2e09..dfc9c7a 100644
--- a/docs/libcurl/curl_easy_setopt.3
+++ b/docs/libcurl/curl_easy_setopt.3
@@ -295,13 +295,25 @@ Function pointer that should match the \fIcurl_opensocket_callback\fP
 prototype found in \fI<curl/curl.h>\fP. This function gets called by libcurl
 instead of the \fIsocket(2)\fP call. The callback's \fIpurpose\fP argument
 identifies the exact purpose for this particular socket:
-\fICURLSOCKTYPE_IPCXN\fP is for IP based connections. Future versions of
-libcurl may support more purposes. It passes the resolved peer address as a
-\fIaddress\fP argument so the callback can modify the address or refuse to
-connect at all. The callback function should return the socket or
-\fICURL_SOCKET_BAD\fP in case no connection should be established or any error
-detected. Any additional \fIsetsockopt(2)\fP calls can be done on the socket
-at the user's discretion.  \fICURL_SOCKET_BAD\fP return value from the
+
+\fICURLSOCKTYPE_IPCXN\fP for regular IP based connections. It can be either
+TCP or UDP depending on the protocol. Also, there are protocols that create
+more than one such connection for a single request (such as FTP).
+
+\fICURLSOCKTYPE_DUMMY\fP for when libcurl needs to create and use a socket
+internally for technical reasons. It won't involve an actual IP based
+connection.
+
+(Future versions of libcurl may support more purposes.)
+
+For \fICURLSOCKTYPE_IPCXN\fP sockets libcurl passes the resolved peer address
+as a \fIaddress\fP argument so the callback can modify the address or refuse
+to connect at all.
+
+The callback function must return the new socket descriptor or
+\fICURL_SOCKET_BAD\fP in case no connection should be established or an error
+was detected. Any additional \fIsetsockopt(2)\fP calls can be done on the
+socket at the user's discretion. \fICURL_SOCKET_BAD\fP return value from the
 callback function will signal an unrecoverable error to the library and it
 will return \fICURLE_COULDNT_CONNECT\fP.  This return code can be used for IP
 address blacklisting.  The default behavior is:
diff --git a/include/curl/curl.h b/include/curl/curl.h
index f53d6d4..d254d9b 100644
--- a/include/curl/curl.h
+++ b/include/curl/curl.h
@@ -310,6 +310,8 @@ typedef size_t (*curl_read_callback)(char *buffer,
 
 typedef enum  {
   CURLSOCKTYPE_IPCXN, /* socket created for a specific IP connection */
+  CURLSOCKTYPE_DUMMY, /* created only for internal technical reasons, not used
+                         for actual traffic */
   CURLSOCKTYPE_LAST   /* never use */
 } curlsocktype;
 
diff --git a/lib/asyn-thread.c b/lib/asyn-thread.c
index cd035dc..8068a15 100644
--- a/lib/asyn-thread.c
+++ b/lib/asyn-thread.c
@@ -142,7 +142,7 @@ int Curl_resolver_duphandle(void **to, void *from)
   return CURLE_OK;
 }
 
-static void destroy_async_data(struct Curl_async *);
+static void destroy_async_data(struct connectdata *conn);
 
 /*
  * Cancel all possibly still on-going resolves for this connection.
@@ -321,8 +321,10 @@ static unsigned int CURL_STDCALL gethostbyname_thread (void *arg)
 /*
  * destroy_async_data() cleans up async resolver data and thread handle.
  */
-static void destroy_async_data (struct Curl_async *async)
+static void destroy_async_data(struct connectdata *conn)
 {
+  struct Curl_async *async = &conn->async;
+
   if(async->hostname)
     free(async->hostname);
 
@@ -330,7 +332,7 @@ static void destroy_async_data (struct Curl_async *async)
     struct thread_data *td = (struct thread_data*) async->os_specific;
 
     if(td->dummy_sock != CURL_SOCKET_BAD)
-      sclose(td->dummy_sock);
+      Curl_closesocket(conn, td->dummy_sock);
 
     if(td->thread_hnd != curl_thread_t_null)
       Curl_thread_join(&td->thread_hnd);
@@ -380,7 +382,19 @@ static bool init_resolve_thread (struct connectdata *conn,
    * should never become signalled for read since it's unbound but
    * Windows needs at least 1 socket in select().
    */
-  td->dummy_sock = socket(AF_INET, SOCK_DGRAM, 0);
+  if(conn->data->set.fopensocket) {
+    struct curl_sockaddr empty_addr;
+    memset(&empty_addr, 0, sizeof(empty_addr));
+    /* we pass in a blanked struct as a safe-guard in case older applications
+       don't properly verify the socket's purpose before reading the address
+       struct */
+    td->dummy_sock = conn->data->set.fopensocket(data->set.opensocket_client,
+                                                 CURLSOCKTYPE_DUMMY,
+                                                 &empty_addr);
+  }
+  else
+    td->dummy_sock = socket(AF_INET, SOCK_DGRAM, 0);
+
   if(td->dummy_sock == CURL_SOCKET_BAD)
     goto err_exit;
 #endif
@@ -401,7 +415,7 @@ static bool init_resolve_thread (struct connectdata *conn,
   return TRUE;
 
  err_exit:
-  destroy_async_data(&conn->async);
+  destroy_async_data(conn);
 
   SET_ERRNO(err);
 
@@ -506,7 +520,7 @@ CURLcode Curl_resolver_wait_resolv(struct connectdata *conn,
     }
   }
 
-  destroy_async_data(&conn->async);
+  destroy_async_data(conn);
 
   if(!conn->async.dns)
     conn->bits.close = TRUE;
@@ -539,7 +553,7 @@ CURLcode Curl_resolver_is_resolved(struct connectdata *conn,
 
   if(done) {
     getaddrinfo_complete(conn);
-    destroy_async_data(&conn->async);
+    destroy_async_data(conn);
 
     if(!conn->async.dns) {
       resolver_error(conn, "host");
-- 
1.7.7.3

