aboutsummaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorPeter Wu <peter@lekensteyn.nl>2014-11-27 23:59:25 +0100
committerDaniel Stenberg <daniel@haxx.se>2014-12-04 02:52:19 +0100
commit970c22f970f0172e6a4c98ccc3176c740ddaa1c6 (patch)
tree54ecfe7c1eacb1864357725cf4440aff4e0973f3 /lib
parent479abdd32eee15dab78d0cd6b1786d569680f0ac (diff)
libcurl: add UNIX domain sockets support
The ability to do HTTP requests over a UNIX domain socket has been requested before, in Apr 2008 [0][1] and Sep 2010 [2]. While a discussion happened, no patch seems to get through. I decided to give it a go since I need to test a nginx HTTP server which listens on a UNIX domain socket. One patch [3] seems to make it possible to use the CURLOPT_OPENSOCKETFUNCTION function to gain a UNIX domain socket. Another person wrote a Go program which can do HTTP over a UNIX socket for Docker[4] which uses a special URL scheme (though the name contains cURL, it has no relation to the cURL library). This patch considers support for UNIX domain sockets at the same level as HTTP proxies / IPv6, it acts as an intermediate socket provider and not as a separate protocol. Since this feature affects network operations, a new feature flag was added ("unix-sockets") with a corresponding CURL_VERSION_UNIX_SOCKETS macro. A new CURLOPT_UNIX_SOCKET_PATH option is added and documented. This option enables UNIX domain sockets support for all requests on the handle (replacing IP sockets and skipping proxies). A new configure option (--enable-unix-sockets) and CMake option (ENABLE_UNIX_SOCKETS) can disable this optional feature. Note that I deliberately did not mark this feature as advanced, this is a feature/component that should easily be available. [0]: http://curl.haxx.se/mail/lib-2008-04/0279.html [1]: http://daniel.haxx.se/blog/2008/04/14/http-over-unix-domain-sockets/ [2]: http://sourceforge.net/p/curl/feature-requests/53/ [3]: http://curl.haxx.se/mail/lib-2008-04/0361.html [4]: https://github.com/Soulou/curl-unix-socket Signed-off-by: Peter Wu <peter@lekensteyn.nl>
Diffstat (limited to 'lib')
-rw-r--r--lib/curl_addrinfo.c39
-rw-r--r--lib/curl_addrinfo.h4
-rw-r--r--lib/curl_config.h.cmake3
-rw-r--r--lib/url.c44
-rw-r--r--lib/urldata.h4
-rw-r--r--lib/version.c3
6 files changed, 96 insertions, 1 deletions
diff --git a/lib/curl_addrinfo.c b/lib/curl_addrinfo.c
index 94ed63d96..b8032f4df 100644
--- a/lib/curl_addrinfo.c
+++ b/lib/curl_addrinfo.c
@@ -33,6 +33,9 @@
#ifdef HAVE_ARPA_INET_H
# include <arpa/inet.h>
#endif
+#ifdef HAVE_SYS_UN_H
+# include <sys/un.h>
+#endif
#ifdef __VMS
# include <in.h>
@@ -477,6 +480,42 @@ Curl_addrinfo *Curl_str2addr(char *address, int port)
return NULL; /* bad input format */
}
+#ifdef USE_UNIX_SOCKETS
+/**
+ * Given a path to a UNIX domain socket, return a newly allocated Curl_addrinfo
+ * struct initialized with this path.
+ */
+Curl_addrinfo *Curl_unix2addr(const char *path)
+{
+ Curl_addrinfo *ai;
+ struct sockaddr_un *sun;
+ size_t path_len;
+
+ ai = calloc(1, sizeof(Curl_addrinfo));
+ if(!ai)
+ return NULL;
+ if((ai->ai_addr = calloc(1, sizeof(struct sockaddr_un))) == NULL) {
+ free(ai);
+ return NULL;
+ }
+ /* sun_path must be able to store the NUL-terminated path */
+ path_len = strlen(path);
+ if(path_len >= sizeof(sun->sun_path)) {
+ free(ai->ai_addr);
+ free(ai);
+ return NULL;
+ }
+
+ ai->ai_family = AF_UNIX;
+ ai->ai_socktype = SOCK_STREAM; /* assume reliable transport for HTTP */
+ ai->ai_addrlen = (curl_socklen_t) sizeof(struct sockaddr_un);
+ sun = (void *) ai->ai_addr;
+ sun->sun_family = AF_UNIX;
+ memcpy(sun->sun_path, path, path_len + 1); /* copy NUL byte */
+ return ai;
+}
+#endif
+
#if defined(CURLDEBUG) && defined(HAVE_FREEADDRINFO)
/*
* curl_dofreeaddrinfo()
diff --git a/lib/curl_addrinfo.h b/lib/curl_addrinfo.h
index 6d2b753eb..4ef882703 100644
--- a/lib/curl_addrinfo.h
+++ b/lib/curl_addrinfo.h
@@ -79,6 +79,10 @@ Curl_ip2addr(int af, const void *inaddr, const char *hostname, int port);
Curl_addrinfo *Curl_str2addr(char *dotted, int port);
+#ifdef USE_UNIX_SOCKETS
+Curl_addrinfo *Curl_unix2addr(const char *path);
+#endif
+
#if defined(CURLDEBUG) && defined(HAVE_FREEADDRINFO)
void
curl_dofreeaddrinfo(struct addrinfo *freethis,
diff --git a/lib/curl_config.h.cmake b/lib/curl_config.h.cmake
index 44014f5dd..bd367c28d 100644
--- a/lib/curl_config.h.cmake
+++ b/lib/curl_config.h.cmake
@@ -912,6 +912,9 @@
/* if SSL is enabled */
#cmakedefine USE_SSLEAY 1
+/* if UNIX domain sockets are enabled */
+#cmakedefine USE_UNIX_SOCKETS
+
/* Define to 1 if you are building a Windows target without large file
support. */
#cmakedefine USE_WIN32_LARGE_FILES 1
diff --git a/lib/url.c b/lib/url.c
index d8703921f..bb9a3bfbc 100644
--- a/lib/url.c
+++ b/lib/url.c
@@ -47,6 +47,10 @@
#include <inet.h>
#endif
+#ifdef HAVE_SYS_UN_H
+#include <sys/un.h>
+#endif
+
#ifndef HAVE_SOCKET
#error "We can't compile without socket() support!"
#endif
@@ -2574,6 +2578,13 @@ CURLcode Curl_setopt(struct SessionHandle *data, CURLoption option,
data->set.ssl_enable_alpn = (0 != va_arg(param, long))?TRUE:FALSE;
break;
+#ifdef USE_UNIX_SOCKETS
+ case CURLOPT_UNIX_SOCKET_PATH:
+ result = setstropt(&data->set.str[STRING_UNIX_SOCKET_PATH],
+ va_arg(param, char *));
+ break;
+#endif
+
default:
/* unknown tag and its companion, just ignore: */
result = CURLE_UNKNOWN_OPTION;
@@ -5064,6 +5075,32 @@ static CURLcode resolve_server(struct SessionHandle *data,
/* set a pointer to the hostname we display */
fix_hostname(data, conn, &conn->host);
+#ifdef USE_UNIX_SOCKETS
+ if(data->set.str[STRING_UNIX_SOCKET_PATH]) {
+ /* UNIX domain sockets are local. The host gets ignored, just use the
+ * specified domain socket address. Do not cache "DNS entries". There is
+ * no DNS involved and we already have the filesystem path available */
+ const char *path = data->set.str[STRING_UNIX_SOCKET_PATH];
+
+ hostaddr = calloc(1, sizeof(struct Curl_dns_entry));
+ if(!hostaddr)
+ result = CURLE_OUT_OF_MEMORY;
+ else if((hostaddr->addr = Curl_unix2addr(path)) != NULL)
+ hostaddr->inuse++;
+ else {
+ /* Long paths are not supported for now */
+ if(strlen(path) >= sizeof(((struct sockaddr_un *)0)->sun_path)) {
+ failf(data, "UNIX socket path too long: '%s'", path);
+ result = CURLE_COULDNT_RESOLVE_HOST;
+ }
+ else
+ result = CURLE_OUT_OF_MEMORY;
+ free(hostaddr);
+ hostaddr = NULL;
+ }
+ }
+ else
+#endif
if(!conn->proxy.name || !*conn->proxy.name) {
/* If not connecting via a proxy, extract the port from the URL, if it is
* there, thus overriding any defaults that might have been set above. */
@@ -5379,6 +5416,13 @@ static CURLcode create_conn(struct SessionHandle *data,
else if(!proxy)
proxy = detect_proxy(conn);
+#ifdef USE_UNIX_SOCKETS
+ if(proxy && data->set.str[STRING_UNIX_SOCKET_PATH]) {
+ free(proxy); /* UNIX domain sockets cannot be proxied, so disable it */
+ proxy = NULL;
+ }
+#endif
+
if(proxy && (!*proxy || (conn->handler->flags & PROTOPT_NONETWORK))) {
free(proxy); /* Don't bother with an empty proxy string or if the
protocol doesn't work with network */
diff --git a/lib/urldata.h b/lib/urldata.h
index 075dca9b2..d61e8cefc 100644
--- a/lib/urldata.h
+++ b/lib/urldata.h
@@ -1426,8 +1426,10 @@ enum dupstring {
STRING_TLSAUTH_USERNAME, /* TLS auth <username> */
STRING_TLSAUTH_PASSWORD, /* TLS auth <password> */
#endif
-
STRING_BEARER, /* <bearer>, if used */
+#ifdef USE_UNIX_SOCKETS
+ STRING_UNIX_SOCKET_PATH, /* path to UNIX socket, if used */
+#endif
/* -- end of zero-terminated strings -- */
diff --git a/lib/version.c b/lib/version.c
index 93317e19b..511562fe1 100644
--- a/lib/version.c
+++ b/lib/version.c
@@ -295,6 +295,9 @@ static curl_version_info_data version_info = {
#if defined(USE_NGHTTP2)
| CURL_VERSION_HTTP2
#endif
+#if defined(USE_UNIX_SOCKETS)
+ | CURL_VERSION_UNIX_SOCKETS
+#endif
,
NULL, /* ssl_version */
0, /* ssl_version_num, this is kept at zero */