sws: add UNIX domain socket support

This extends sws with a --unix-socket option which causes the port to
be ignored (as the server now listens on the path specified by
--unix-socket). This feature will be available in the following patch
that enables checking for UNIX domain socket support.

Proxy support (CONNECT) is not considered nor tested. It does not make
sense anyway, first connecting through a TCP proxy, then let that TCP
proxy connect to a UNIX socket.

Signed-off-by: Peter Wu <peter@lekensteyn.nl>
diff --git a/tests/server/server_sockaddr.h b/tests/server/server_sockaddr.h
index 6a17fe0..3f4cd67 100644
--- a/tests/server/server_sockaddr.h
+++ b/tests/server/server_sockaddr.h
@@ -7,7 +7,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2012, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2014, Daniel Stenberg, <daniel@haxx.se>, et al.
  *
  * This software is licensed as described in the file COPYING, which
  * you should have received as part of this distribution. The terms
@@ -23,12 +23,19 @@
  ***************************************************************************/
 #include "server_setup.h"
 
+#ifdef HAVE_SYS_UN_H
+#include <sys/un.h> /* for sockaddr_un */
+#endif
+
 typedef union {
   struct sockaddr      sa;
   struct sockaddr_in   sa4;
 #ifdef ENABLE_IPV6
   struct sockaddr_in6  sa6;
 #endif
+#ifdef USE_UNIX_SOCKETS
+  struct sockaddr_un   sau;
+#endif
 } srvr_sockaddr_union_t;
 
 #endif /* HEADER_CURL_SERVER_SOCKADDR_H */
diff --git a/tests/server/sws.c b/tests/server/sws.c
index ebc70d6..766c46a 100644
--- a/tests/server/sws.c
+++ b/tests/server/sws.c
@@ -70,6 +70,9 @@
 #ifdef ENABLE_IPV6
   , socket_domain_inet6 = AF_INET6
 #endif
+#ifdef USE_UNIX_SOCKETS
+  socket_domain_unix = AF_UNIX,
+#endif
 } socket_domain = AF_INET;
 static bool use_gopher = FALSE;
 static int serverlogslocked = 0;
@@ -1363,6 +1366,11 @@
     rc = connect(serverfd, &serveraddr.sa, sizeof(serveraddr.sa6));
     break;
 #endif /* ENABLE_IPV6 */
+#ifdef USE_UNIX_SOCKETS
+  case AF_UNIX:
+    logmsg("Proxying through UNIX socket is not (yet?) supported.");
+    return CURL_SOCKET_BAD;
+#endif /* USE_UNIX_SOCKETS */
   }
 
   if(got_exit_signal) {
@@ -1946,6 +1954,10 @@
   int wrotepidfile = 0;
   int flag;
   unsigned short port = DEFAULT_PORT;
+#ifdef USE_UNIX_SOCKETS
+  const char *unix_socket = NULL;
+  bool unlink_socket = false;
+#endif
   char *pidname= (char *)".http.pid";
   struct httprequest req;
   int rc;
@@ -1968,6 +1980,9 @@
 #ifdef ENABLE_IPV6
              "/IPv6"
 #endif
+#ifdef USE_UNIX_SOCKETS
+             "/unix"
+#endif
           );
       return 0;
     }
@@ -2000,6 +2015,23 @@
 #endif
       arg++;
     }
+    else if(!strcmp("--unix-socket", argv[arg])) {
+      arg++;
+      if(argc>arg) {
+#ifdef USE_UNIX_SOCKETS
+        unix_socket = argv[arg];
+        if(strlen(unix_socket) >= sizeof(me.sau.sun_path)) {
+          fprintf(stderr, "sws: socket path must be shorter than %zu chars\n",
+                  sizeof(me.sau.sun_path));
+          return 0;
+        }
+        socket_type = "unix";
+        socket_domain = AF_UNIX;
+        location_str = unix_socket;
+#endif
+        arg++;
+      }
+    }
     else if(!strcmp("--port", argv[arg])) {
       arg++;
       if(argc>arg) {
@@ -2041,6 +2073,7 @@
            " --pidfile [file]\n"
            " --ipv4\n"
            " --ipv6\n"
+           " --unix-socket [file]\n"
            " --port [port]\n"
            " --srcdir [path]\n"
            " --connect [ip4-addr]\n"
@@ -2104,6 +2137,14 @@
     rc = bind(sock, &me.sa, sizeof(me.sa6));
     break;
 #endif /* ENABLE_IPV6 */
+#ifdef USE_UNIX_SOCKETS
+  case AF_UNIX:
+    memset(&me.sau, 0, sizeof(me.sau));
+    me.sau.sun_family = AF_UNIX;
+    strncpy(me.sau.sun_path, unix_socket, sizeof(me.sau.sun_path));
+    rc = bind(sock, &me.sa, sizeof(me.sau));
+    break;
+#endif /* USE_UNIX_SOCKETS */
   }
   if(0 != rc) {
     error = SOCKERRNO;
@@ -2124,6 +2165,11 @@
     goto sws_cleanup;
   }
 
+#ifdef USE_UNIX_SOCKETS
+  /* listen succeeds, so let's assume a valid listening UNIX socket */
+  unlink_socket = true;
+#endif
+
   /*
   ** As soon as this server writes its pid file the test harness will
   ** attempt to connect to this server and initiate its verification.
@@ -2264,6 +2310,13 @@
   if(sock != CURL_SOCKET_BAD)
     sclose(sock);
 
+#ifdef USE_UNIX_SOCKETS
+  if(unlink_socket && socket_domain == AF_UNIX) {
+    rc = unlink(unix_socket);
+    logmsg("unlink(%s) = %d (%s)", unix_socket, rc, strerror(rc));
+  }
+#endif
+
   if(got_exit_signal)
     logmsg("signalled to die");