|
@@ -35,6 +35,9 @@
|
|
|
#elif defined(HAVE_NETINET_TCP_H)
|
|
#elif defined(HAVE_NETINET_TCP_H)
|
|
|
#include <netinet/tcp.h>
|
|
#include <netinet/tcp.h>
|
|
|
#endif
|
|
#endif
|
|
|
|
|
+#ifdef HAVE_NETINET_UDP_H
|
|
|
|
|
+#include <netinet/udp.h>
|
|
|
|
|
+#endif
|
|
|
#ifdef HAVE_SYS_IOCTL_H
|
|
#ifdef HAVE_SYS_IOCTL_H
|
|
|
#include <sys/ioctl.h>
|
|
#include <sys/ioctl.h>
|
|
|
#endif
|
|
#endif
|
|
@@ -53,6 +56,11 @@
|
|
|
#include <inet.h>
|
|
#include <inet.h>
|
|
|
#endif
|
|
#endif
|
|
|
|
|
|
|
|
|
|
+#ifdef __DragonFly__
|
|
|
|
|
+/* Required for __DragonFly_version */
|
|
|
|
|
+#include <sys/param.h>
|
|
|
|
|
+#endif
|
|
|
|
|
+
|
|
|
#include "urldata.h"
|
|
#include "urldata.h"
|
|
|
#include "bufq.h"
|
|
#include "bufq.h"
|
|
|
#include "sendf.h"
|
|
#include "sendf.h"
|
|
@@ -73,6 +81,7 @@
|
|
|
#include "multihandle.h"
|
|
#include "multihandle.h"
|
|
|
#include "rand.h"
|
|
#include "rand.h"
|
|
|
#include "share.h"
|
|
#include "share.h"
|
|
|
|
|
+#include "strdup.h"
|
|
|
#include "version_win32.h"
|
|
#include "version_win32.h"
|
|
|
|
|
|
|
|
/* The last 3 #include files should be in this order */
|
|
/* The last 3 #include files should be in this order */
|
|
@@ -137,8 +146,12 @@ static void nosigpipe(struct Curl_easy *data,
|
|
|
#define nosigpipe(x,y) Curl_nop_stmt
|
|
#define nosigpipe(x,y) Curl_nop_stmt
|
|
|
#endif
|
|
#endif
|
|
|
|
|
|
|
|
-#if defined(__DragonFly__) || defined(USE_WINSOCK)
|
|
|
|
|
-/* DragonFlyBSD and Windows use millisecond units */
|
|
|
|
|
|
|
+#if defined(USE_WINSOCK) || \
|
|
|
|
|
+ (defined(__sun) && !defined(TCP_KEEPIDLE)) || \
|
|
|
|
|
+ (defined(__DragonFly__) && __DragonFly_version < 500702) || \
|
|
|
|
|
+ (defined(_WIN32) && !defined(TCP_KEEPIDLE))
|
|
|
|
|
+/* Solaris < 11.4, DragonFlyBSD < 500702 and Windows < 10.0.16299
|
|
|
|
|
+ * use millisecond units. */
|
|
|
#define KEEPALIVE_FACTOR(x) (x *= 1000)
|
|
#define KEEPALIVE_FACTOR(x) (x *= 1000)
|
|
|
#else
|
|
#else
|
|
|
#define KEEPALIVE_FACTOR(x)
|
|
#define KEEPALIVE_FACTOR(x)
|
|
@@ -168,23 +181,50 @@ tcpkeepalive(struct Curl_easy *data,
|
|
|
sockfd, SOCKERRNO);
|
|
sockfd, SOCKERRNO);
|
|
|
}
|
|
}
|
|
|
else {
|
|
else {
|
|
|
-#if defined(SIO_KEEPALIVE_VALS)
|
|
|
|
|
|
|
+#if defined(SIO_KEEPALIVE_VALS) /* Windows */
|
|
|
|
|
+/* Windows 10, version 1709 (10.0.16299) and later versions */
|
|
|
|
|
+#if defined(TCP_KEEPIDLE) && defined(TCP_KEEPINTVL) && defined(TCP_KEEPCNT)
|
|
|
|
|
+ optval = curlx_sltosi(data->set.tcp_keepidle);
|
|
|
|
|
+ KEEPALIVE_FACTOR(optval);
|
|
|
|
|
+ if(setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPIDLE,
|
|
|
|
|
+ (const char *)&optval, sizeof(optval)) < 0) {
|
|
|
|
|
+ infof(data, "Failed to set TCP_KEEPIDLE on fd "
|
|
|
|
|
+ "%" CURL_FORMAT_SOCKET_T ": errno %d",
|
|
|
|
|
+ sockfd, SOCKERRNO);
|
|
|
|
|
+ }
|
|
|
|
|
+ optval = curlx_sltosi(data->set.tcp_keepintvl);
|
|
|
|
|
+ KEEPALIVE_FACTOR(optval);
|
|
|
|
|
+ if(setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPINTVL,
|
|
|
|
|
+ (const char *)&optval, sizeof(optval)) < 0) {
|
|
|
|
|
+ infof(data, "Failed to set TCP_KEEPINTVL on fd "
|
|
|
|
|
+ "%" CURL_FORMAT_SOCKET_T ": errno %d",
|
|
|
|
|
+ sockfd, SOCKERRNO);
|
|
|
|
|
+ }
|
|
|
|
|
+ optval = curlx_sltosi(data->set.tcp_keepcnt);
|
|
|
|
|
+ if(setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPCNT,
|
|
|
|
|
+ (const char *)&optval, sizeof(optval)) < 0) {
|
|
|
|
|
+ infof(data, "Failed to set TCP_KEEPCNT on fd "
|
|
|
|
|
+ "%" CURL_FORMAT_SOCKET_T ": errno %d",
|
|
|
|
|
+ sockfd, SOCKERRNO);
|
|
|
|
|
+ }
|
|
|
|
|
+#else /* Windows < 10.0.16299 */
|
|
|
struct tcp_keepalive vals;
|
|
struct tcp_keepalive vals;
|
|
|
DWORD dummy;
|
|
DWORD dummy;
|
|
|
vals.onoff = 1;
|
|
vals.onoff = 1;
|
|
|
optval = curlx_sltosi(data->set.tcp_keepidle);
|
|
optval = curlx_sltosi(data->set.tcp_keepidle);
|
|
|
KEEPALIVE_FACTOR(optval);
|
|
KEEPALIVE_FACTOR(optval);
|
|
|
- vals.keepalivetime = optval;
|
|
|
|
|
|
|
+ vals.keepalivetime = (u_long)optval;
|
|
|
optval = curlx_sltosi(data->set.tcp_keepintvl);
|
|
optval = curlx_sltosi(data->set.tcp_keepintvl);
|
|
|
KEEPALIVE_FACTOR(optval);
|
|
KEEPALIVE_FACTOR(optval);
|
|
|
- vals.keepaliveinterval = optval;
|
|
|
|
|
|
|
+ vals.keepaliveinterval = (u_long)optval;
|
|
|
if(WSAIoctl(sockfd, SIO_KEEPALIVE_VALS, (LPVOID) &vals, sizeof(vals),
|
|
if(WSAIoctl(sockfd, SIO_KEEPALIVE_VALS, (LPVOID) &vals, sizeof(vals),
|
|
|
NULL, 0, &dummy, NULL, NULL) != 0) {
|
|
NULL, 0, &dummy, NULL, NULL) != 0) {
|
|
|
infof(data, "Failed to set SIO_KEEPALIVE_VALS on fd "
|
|
infof(data, "Failed to set SIO_KEEPALIVE_VALS on fd "
|
|
|
"%" CURL_FORMAT_SOCKET_T ": errno %d",
|
|
"%" CURL_FORMAT_SOCKET_T ": errno %d",
|
|
|
sockfd, SOCKERRNO);
|
|
sockfd, SOCKERRNO);
|
|
|
}
|
|
}
|
|
|
-#else
|
|
|
|
|
|
|
+#endif
|
|
|
|
|
+#else /* !Windows */
|
|
|
#ifdef TCP_KEEPIDLE
|
|
#ifdef TCP_KEEPIDLE
|
|
|
optval = curlx_sltosi(data->set.tcp_keepidle);
|
|
optval = curlx_sltosi(data->set.tcp_keepidle);
|
|
|
KEEPALIVE_FACTOR(optval);
|
|
KEEPALIVE_FACTOR(optval);
|
|
@@ -204,6 +244,16 @@ tcpkeepalive(struct Curl_easy *data,
|
|
|
"%" CURL_FORMAT_SOCKET_T ": errno %d",
|
|
"%" CURL_FORMAT_SOCKET_T ": errno %d",
|
|
|
sockfd, SOCKERRNO);
|
|
sockfd, SOCKERRNO);
|
|
|
}
|
|
}
|
|
|
|
|
+#elif defined(TCP_KEEPALIVE_THRESHOLD)
|
|
|
|
|
+ /* Solaris <11.4 style */
|
|
|
|
|
+ optval = curlx_sltosi(data->set.tcp_keepidle);
|
|
|
|
|
+ KEEPALIVE_FACTOR(optval);
|
|
|
|
|
+ if(setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPALIVE_THRESHOLD,
|
|
|
|
|
+ (void *)&optval, sizeof(optval)) < 0) {
|
|
|
|
|
+ infof(data, "Failed to set TCP_KEEPALIVE_THRESHOLD on fd "
|
|
|
|
|
+ "%" CURL_FORMAT_SOCKET_T ": errno %d",
|
|
|
|
|
+ sockfd, SOCKERRNO);
|
|
|
|
|
+ }
|
|
|
#endif
|
|
#endif
|
|
|
#ifdef TCP_KEEPINTVL
|
|
#ifdef TCP_KEEPINTVL
|
|
|
optval = curlx_sltosi(data->set.tcp_keepintvl);
|
|
optval = curlx_sltosi(data->set.tcp_keepintvl);
|
|
@@ -214,6 +264,36 @@ tcpkeepalive(struct Curl_easy *data,
|
|
|
"%" CURL_FORMAT_SOCKET_T ": errno %d",
|
|
"%" CURL_FORMAT_SOCKET_T ": errno %d",
|
|
|
sockfd, SOCKERRNO);
|
|
sockfd, SOCKERRNO);
|
|
|
}
|
|
}
|
|
|
|
|
+#elif defined(TCP_KEEPALIVE_ABORT_THRESHOLD)
|
|
|
|
|
+ /* Solaris <11.4 style */
|
|
|
|
|
+ /* TCP_KEEPALIVE_ABORT_THRESHOLD should equal to
|
|
|
|
|
+ * TCP_KEEPCNT * TCP_KEEPINTVL on other platforms.
|
|
|
|
|
+ * The default value of TCP_KEEPCNT is 9 on Linux,
|
|
|
|
|
+ * 8 on *BSD/macOS, 5 or 10 on Windows. We use the
|
|
|
|
|
+ * default config for Solaris <11.4 because there is
|
|
|
|
|
+ * no default value for TCP_KEEPCNT on Solaris 11.4.
|
|
|
|
|
+ *
|
|
|
|
|
+ * Note that the consequent probes will not be sent
|
|
|
|
|
+ * at equal intervals on Solaris, but will be sent
|
|
|
|
|
+ * using the exponential backoff algorithm. */
|
|
|
|
|
+ optval = curlx_sltosi(data->set.tcp_keepcnt) *
|
|
|
|
|
+ curlx_sltosi(data->set.tcp_keepintvl);
|
|
|
|
|
+ KEEPALIVE_FACTOR(optval);
|
|
|
|
|
+ if(setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPALIVE_ABORT_THRESHOLD,
|
|
|
|
|
+ (void *)&optval, sizeof(optval)) < 0) {
|
|
|
|
|
+ infof(data, "Failed to set TCP_KEEPALIVE_ABORT_THRESHOLD on fd "
|
|
|
|
|
+ "%" CURL_FORMAT_SOCKET_T ": errno %d",
|
|
|
|
|
+ sockfd, SOCKERRNO);
|
|
|
|
|
+ }
|
|
|
|
|
+#endif
|
|
|
|
|
+#ifdef TCP_KEEPCNT
|
|
|
|
|
+ optval = curlx_sltosi(data->set.tcp_keepcnt);
|
|
|
|
|
+ if(setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPCNT,
|
|
|
|
|
+ (void *)&optval, sizeof(optval)) < 0) {
|
|
|
|
|
+ infof(data, "Failed to set TCP_KEEPCNT on fd "
|
|
|
|
|
+ "%" CURL_FORMAT_SOCKET_T ": errno %d",
|
|
|
|
|
+ sockfd, SOCKERRNO);
|
|
|
|
|
+ }
|
|
|
#endif
|
|
#endif
|
|
|
#endif
|
|
#endif
|
|
|
}
|
|
}
|
|
@@ -249,7 +329,7 @@ void Curl_sock_assign_addr(struct Curl_sockaddr_ex *dest,
|
|
|
dest->protocol = IPPROTO_UDP;
|
|
dest->protocol = IPPROTO_UDP;
|
|
|
break;
|
|
break;
|
|
|
}
|
|
}
|
|
|
- dest->addrlen = ai->ai_addrlen;
|
|
|
|
|
|
|
+ dest->addrlen = (unsigned int)ai->ai_addrlen;
|
|
|
|
|
|
|
|
if(dest->addrlen > sizeof(struct Curl_sockaddr_storage))
|
|
if(dest->addrlen > sizeof(struct Curl_sockaddr_storage))
|
|
|
dest->addrlen = sizeof(struct Curl_sockaddr_storage);
|
|
dest->addrlen = sizeof(struct Curl_sockaddr_storage);
|
|
@@ -314,7 +394,7 @@ CURLcode Curl_socket_open(struct Curl_easy *data,
|
|
|
struct Curl_sockaddr_ex dummy;
|
|
struct Curl_sockaddr_ex dummy;
|
|
|
|
|
|
|
|
if(!addr)
|
|
if(!addr)
|
|
|
- /* if the caller doesn't want info back, use a local temp copy */
|
|
|
|
|
|
|
+ /* if the caller does not want info back, use a local temp copy */
|
|
|
addr = &dummy;
|
|
addr = &dummy;
|
|
|
|
|
|
|
|
Curl_sock_assign_addr(addr, ai, transport);
|
|
Curl_sock_assign_addr(addr, ai, transport);
|
|
@@ -363,14 +443,14 @@ int Curl_socket_close(struct Curl_easy *data, struct connectdata *conn,
|
|
|
Buffer Size
|
|
Buffer Size
|
|
|
|
|
|
|
|
The problem described in this knowledge-base is applied only to pre-Vista
|
|
The problem described in this knowledge-base is applied only to pre-Vista
|
|
|
- Windows. Following function trying to detect OS version and skips
|
|
|
|
|
|
|
+ Windows. Following function trying to detect OS version and skips
|
|
|
SO_SNDBUF adjustment for Windows Vista and above.
|
|
SO_SNDBUF adjustment for Windows Vista and above.
|
|
|
*/
|
|
*/
|
|
|
#define DETECT_OS_NONE 0
|
|
#define DETECT_OS_NONE 0
|
|
|
#define DETECT_OS_PREVISTA 1
|
|
#define DETECT_OS_PREVISTA 1
|
|
|
#define DETECT_OS_VISTA_OR_LATER 2
|
|
#define DETECT_OS_VISTA_OR_LATER 2
|
|
|
|
|
|
|
|
-void Curl_sndbufset(curl_socket_t sockfd)
|
|
|
|
|
|
|
+void Curl_sndbuf_init(curl_socket_t sockfd)
|
|
|
{
|
|
{
|
|
|
int val = CURL_MAX_WRITE_SIZE + 32;
|
|
int val = CURL_MAX_WRITE_SIZE + 32;
|
|
|
int curval = 0;
|
|
int curval = 0;
|
|
@@ -395,7 +475,83 @@ void Curl_sndbufset(curl_socket_t sockfd)
|
|
|
|
|
|
|
|
setsockopt(sockfd, SOL_SOCKET, SO_SNDBUF, (const char *)&val, sizeof(val));
|
|
setsockopt(sockfd, SOL_SOCKET, SO_SNDBUF, (const char *)&val, sizeof(val));
|
|
|
}
|
|
}
|
|
|
-#endif
|
|
|
|
|
|
|
+#endif /* USE_WINSOCK */
|
|
|
|
|
+
|
|
|
|
|
+/*
|
|
|
|
|
+ * Curl_parse_interface()
|
|
|
|
|
+ *
|
|
|
|
|
+ * This is used to parse interface argument in the following formats.
|
|
|
|
|
+ * In all the examples, `host` can be an IP address or a hostname.
|
|
|
|
|
+ *
|
|
|
|
|
+ * <iface_or_host> - can be either an interface name or a host.
|
|
|
|
|
+ * if!<iface> - interface name.
|
|
|
|
|
+ * host!<host> - hostname.
|
|
|
|
|
+ * ifhost!<iface>!<host> - interface name and hostname.
|
|
|
|
|
+ *
|
|
|
|
|
+ * Parameters:
|
|
|
|
|
+ *
|
|
|
|
|
+ * input [in] - input string.
|
|
|
|
|
+ * len [in] - length of the input string.
|
|
|
|
|
+ * dev [in/out] - address where a pointer to newly allocated memory
|
|
|
|
|
+ * holding the interface-or-host will be stored upon
|
|
|
|
|
+ * completion.
|
|
|
|
|
+ * iface [in/out] - address where a pointer to newly allocated memory
|
|
|
|
|
+ * holding the interface will be stored upon completion.
|
|
|
|
|
+ * host [in/out] - address where a pointer to newly allocated memory
|
|
|
|
|
+ * holding the host will be stored upon completion.
|
|
|
|
|
+ *
|
|
|
|
|
+ * Returns CURLE_OK on success.
|
|
|
|
|
+ */
|
|
|
|
|
+CURLcode Curl_parse_interface(const char *input, size_t len,
|
|
|
|
|
+ char **dev, char **iface, char **host)
|
|
|
|
|
+{
|
|
|
|
|
+ static const char if_prefix[] = "if!";
|
|
|
|
|
+ static const char host_prefix[] = "host!";
|
|
|
|
|
+ static const char if_host_prefix[] = "ifhost!";
|
|
|
|
|
+
|
|
|
|
|
+ DEBUGASSERT(dev);
|
|
|
|
|
+ DEBUGASSERT(iface);
|
|
|
|
|
+ DEBUGASSERT(host);
|
|
|
|
|
+
|
|
|
|
|
+ if(strncmp(if_prefix, input, strlen(if_prefix)) == 0) {
|
|
|
|
|
+ input += strlen(if_prefix);
|
|
|
|
|
+ if(!*input)
|
|
|
|
|
+ return CURLE_BAD_FUNCTION_ARGUMENT;
|
|
|
|
|
+ *iface = Curl_memdup0(input, len - strlen(if_prefix));
|
|
|
|
|
+ return *iface ? CURLE_OK : CURLE_OUT_OF_MEMORY;
|
|
|
|
|
+ }
|
|
|
|
|
+ if(strncmp(host_prefix, input, strlen(host_prefix)) == 0) {
|
|
|
|
|
+ input += strlen(host_prefix);
|
|
|
|
|
+ if(!*input)
|
|
|
|
|
+ return CURLE_BAD_FUNCTION_ARGUMENT;
|
|
|
|
|
+ *host = Curl_memdup0(input, len - strlen(host_prefix));
|
|
|
|
|
+ return *host ? CURLE_OK : CURLE_OUT_OF_MEMORY;
|
|
|
|
|
+ }
|
|
|
|
|
+ if(strncmp(if_host_prefix, input, strlen(if_host_prefix)) == 0) {
|
|
|
|
|
+ const char *host_part;
|
|
|
|
|
+ input += strlen(if_host_prefix);
|
|
|
|
|
+ len -= strlen(if_host_prefix);
|
|
|
|
|
+ host_part = memchr(input, '!', len);
|
|
|
|
|
+ if(!host_part || !*(host_part + 1))
|
|
|
|
|
+ return CURLE_BAD_FUNCTION_ARGUMENT;
|
|
|
|
|
+ *iface = Curl_memdup0(input, host_part - input);
|
|
|
|
|
+ if(!*iface)
|
|
|
|
|
+ return CURLE_OUT_OF_MEMORY;
|
|
|
|
|
+ ++host_part;
|
|
|
|
|
+ *host = Curl_memdup0(host_part, len - (host_part - input));
|
|
|
|
|
+ if(!*host) {
|
|
|
|
|
+ free(*iface);
|
|
|
|
|
+ *iface = NULL;
|
|
|
|
|
+ return CURLE_OUT_OF_MEMORY;
|
|
|
|
|
+ }
|
|
|
|
|
+ return CURLE_OK;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if(!*input)
|
|
|
|
|
+ return CURLE_BAD_FUNCTION_ARGUMENT;
|
|
|
|
|
+ *dev = Curl_memdup0(input, len);
|
|
|
|
|
+ return *dev ? CURLE_OK : CURLE_OUT_OF_MEMORY;
|
|
|
|
|
+}
|
|
|
|
|
|
|
|
#ifndef CURL_DISABLE_BINDLOCAL
|
|
#ifndef CURL_DISABLE_BINDLOCAL
|
|
|
static CURLcode bindlocal(struct Curl_easy *data, struct connectdata *conn,
|
|
static CURLcode bindlocal(struct Curl_easy *data, struct connectdata *conn,
|
|
@@ -415,6 +571,10 @@ static CURLcode bindlocal(struct Curl_easy *data, struct connectdata *conn,
|
|
|
/* how many port numbers to try to bind to, increasing one at a time */
|
|
/* how many port numbers to try to bind to, increasing one at a time */
|
|
|
int portnum = data->set.localportrange;
|
|
int portnum = data->set.localportrange;
|
|
|
const char *dev = data->set.str[STRING_DEVICE];
|
|
const char *dev = data->set.str[STRING_DEVICE];
|
|
|
|
|
+ const char *iface_input = data->set.str[STRING_INTERFACE];
|
|
|
|
|
+ const char *host_input = data->set.str[STRING_BINDHOST];
|
|
|
|
|
+ const char *iface = iface_input ? iface_input : dev;
|
|
|
|
|
+ const char *host = host_input ? host_input : dev;
|
|
|
int error;
|
|
int error;
|
|
|
#ifdef IP_BIND_ADDRESS_NO_PORT
|
|
#ifdef IP_BIND_ADDRESS_NO_PORT
|
|
|
int on = 1;
|
|
int on = 1;
|
|
@@ -426,83 +586,77 @@ static CURLcode bindlocal(struct Curl_easy *data, struct connectdata *conn,
|
|
|
/*************************************************************
|
|
/*************************************************************
|
|
|
* Select device to bind socket to
|
|
* Select device to bind socket to
|
|
|
*************************************************************/
|
|
*************************************************************/
|
|
|
- if(!dev && !port)
|
|
|
|
|
|
|
+ if(!iface && !host && !port)
|
|
|
/* no local kind of binding was requested */
|
|
/* no local kind of binding was requested */
|
|
|
return CURLE_OK;
|
|
return CURLE_OK;
|
|
|
|
|
|
|
|
memset(&sa, 0, sizeof(struct Curl_sockaddr_storage));
|
|
memset(&sa, 0, sizeof(struct Curl_sockaddr_storage));
|
|
|
|
|
|
|
|
- if(dev && (strlen(dev)<255) ) {
|
|
|
|
|
|
|
+ if(iface && (strlen(iface)<255) ) {
|
|
|
char myhost[256] = "";
|
|
char myhost[256] = "";
|
|
|
int done = 0; /* -1 for error, 1 for address found */
|
|
int done = 0; /* -1 for error, 1 for address found */
|
|
|
- bool is_interface = FALSE;
|
|
|
|
|
- bool is_host = FALSE;
|
|
|
|
|
- static const char *if_prefix = "if!";
|
|
|
|
|
- static const char *host_prefix = "host!";
|
|
|
|
|
-
|
|
|
|
|
- if(strncmp(if_prefix, dev, strlen(if_prefix)) == 0) {
|
|
|
|
|
- dev += strlen(if_prefix);
|
|
|
|
|
- is_interface = TRUE;
|
|
|
|
|
- }
|
|
|
|
|
- else if(strncmp(host_prefix, dev, strlen(host_prefix)) == 0) {
|
|
|
|
|
- dev += strlen(host_prefix);
|
|
|
|
|
- is_host = TRUE;
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ if2ip_result_t if2ip_result = IF2IP_NOT_FOUND;
|
|
|
|
|
|
|
|
/* interface */
|
|
/* interface */
|
|
|
- if(!is_host) {
|
|
|
|
|
#ifdef SO_BINDTODEVICE
|
|
#ifdef SO_BINDTODEVICE
|
|
|
- /*
|
|
|
|
|
- * This binds the local socket to a particular interface. This will
|
|
|
|
|
- * force even requests to other local interfaces to go out the external
|
|
|
|
|
- * interface. Only bind to the interface when specified as interface,
|
|
|
|
|
- * not just as a hostname or ip address.
|
|
|
|
|
- *
|
|
|
|
|
- * The interface might be a VRF, eg: vrf-blue, which means it cannot be
|
|
|
|
|
- * converted to an IP address and would fail Curl_if2ip. Simply try to
|
|
|
|
|
- * use it straight away.
|
|
|
|
|
- */
|
|
|
|
|
- if(setsockopt(sockfd, SOL_SOCKET, SO_BINDTODEVICE,
|
|
|
|
|
- dev, (curl_socklen_t)strlen(dev) + 1) == 0) {
|
|
|
|
|
- /* This is often "errno 1, error: Operation not permitted" if you're
|
|
|
|
|
- * not running as root or another suitable privileged user. If it
|
|
|
|
|
- * succeeds it means the parameter was a valid interface and not an IP
|
|
|
|
|
- * address. Return immediately.
|
|
|
|
|
- */
|
|
|
|
|
- infof(data, "socket successfully bound to interface '%s'", dev);
|
|
|
|
|
|
|
+ /*
|
|
|
|
|
+ * This binds the local socket to a particular interface. This will
|
|
|
|
|
+ * force even requests to other local interfaces to go out the external
|
|
|
|
|
+ * interface. Only bind to the interface when specified as interface,
|
|
|
|
|
+ * not just as a hostname or ip address.
|
|
|
|
|
+ *
|
|
|
|
|
+ * The interface might be a VRF, eg: vrf-blue, which means it cannot be
|
|
|
|
|
+ * converted to an IP address and would fail Curl_if2ip. Simply try to
|
|
|
|
|
+ * use it straight away.
|
|
|
|
|
+ */
|
|
|
|
|
+ if(setsockopt(sockfd, SOL_SOCKET, SO_BINDTODEVICE,
|
|
|
|
|
+ iface, (curl_socklen_t)strlen(iface) + 1) == 0) {
|
|
|
|
|
+ /* This is often "errno 1, error: Operation not permitted" if you are
|
|
|
|
|
+ * not running as root or another suitable privileged user. If it
|
|
|
|
|
+ * succeeds it means the parameter was a valid interface and not an IP
|
|
|
|
|
+ * address. Return immediately.
|
|
|
|
|
+ */
|
|
|
|
|
+ if(!host_input) {
|
|
|
|
|
+ infof(data, "socket successfully bound to interface '%s'", iface);
|
|
|
return CURLE_OK;
|
|
return CURLE_OK;
|
|
|
}
|
|
}
|
|
|
|
|
+ }
|
|
|
#endif
|
|
#endif
|
|
|
-
|
|
|
|
|
- switch(Curl_if2ip(af,
|
|
|
|
|
|
|
+ if(!host_input) {
|
|
|
|
|
+ /* Discover IP from input device, then bind to it */
|
|
|
|
|
+ if2ip_result = Curl_if2ip(af,
|
|
|
#ifdef USE_IPV6
|
|
#ifdef USE_IPV6
|
|
|
- scope, conn->scope_id,
|
|
|
|
|
-#endif
|
|
|
|
|
- dev, myhost, sizeof(myhost))) {
|
|
|
|
|
- case IF2IP_NOT_FOUND:
|
|
|
|
|
- if(is_interface) {
|
|
|
|
|
- /* Do not fall back to treating it as a host name */
|
|
|
|
|
- failf(data, "Couldn't bind to interface '%s'", dev);
|
|
|
|
|
- return CURLE_INTERFACE_FAILED;
|
|
|
|
|
- }
|
|
|
|
|
- break;
|
|
|
|
|
- case IF2IP_AF_NOT_SUPPORTED:
|
|
|
|
|
- /* Signal the caller to try another address family if available */
|
|
|
|
|
- return CURLE_UNSUPPORTED_PROTOCOL;
|
|
|
|
|
- case IF2IP_FOUND:
|
|
|
|
|
- is_interface = TRUE;
|
|
|
|
|
- /*
|
|
|
|
|
- * We now have the numerical IP address in the 'myhost' buffer
|
|
|
|
|
- */
|
|
|
|
|
- infof(data, "Local Interface %s is ip %s using address family %i",
|
|
|
|
|
- dev, myhost, af);
|
|
|
|
|
- done = 1;
|
|
|
|
|
- break;
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ scope, conn->scope_id,
|
|
|
|
|
+#endif
|
|
|
|
|
+ iface, myhost, sizeof(myhost));
|
|
|
|
|
+ }
|
|
|
|
|
+ switch(if2ip_result) {
|
|
|
|
|
+ case IF2IP_NOT_FOUND:
|
|
|
|
|
+ if(iface_input && !host_input) {
|
|
|
|
|
+ /* Do not fall back to treating it as a hostname */
|
|
|
|
|
+ char buffer[STRERROR_LEN];
|
|
|
|
|
+ data->state.os_errno = error = SOCKERRNO;
|
|
|
|
|
+ failf(data, "Couldn't bind to interface '%s' with errno %d: %s",
|
|
|
|
|
+ iface, error, Curl_strerror(error, buffer, sizeof(buffer)));
|
|
|
|
|
+ return CURLE_INTERFACE_FAILED;
|
|
|
|
|
+ }
|
|
|
|
|
+ break;
|
|
|
|
|
+ case IF2IP_AF_NOT_SUPPORTED:
|
|
|
|
|
+ /* Signal the caller to try another address family if available */
|
|
|
|
|
+ return CURLE_UNSUPPORTED_PROTOCOL;
|
|
|
|
|
+ case IF2IP_FOUND:
|
|
|
|
|
+ /*
|
|
|
|
|
+ * We now have the numerical IP address in the 'myhost' buffer
|
|
|
|
|
+ */
|
|
|
|
|
+ host = myhost;
|
|
|
|
|
+ infof(data, "Local Interface %s is ip %s using address family %i",
|
|
|
|
|
+ iface, host, af);
|
|
|
|
|
+ done = 1;
|
|
|
|
|
+ break;
|
|
|
}
|
|
}
|
|
|
- if(!is_interface) {
|
|
|
|
|
|
|
+ if(!iface_input || host_input) {
|
|
|
/*
|
|
/*
|
|
|
- * This was not an interface, resolve the name as a host name
|
|
|
|
|
|
|
+ * This was not an interface, resolve the name as a hostname
|
|
|
* or IP number
|
|
* or IP number
|
|
|
*
|
|
*
|
|
|
* Temporarily force name resolution to use only the address type
|
|
* Temporarily force name resolution to use only the address type
|
|
@@ -519,7 +673,7 @@ static CURLcode bindlocal(struct Curl_easy *data, struct connectdata *conn,
|
|
|
conn->ip_version = CURL_IPRESOLVE_V6;
|
|
conn->ip_version = CURL_IPRESOLVE_V6;
|
|
|
#endif
|
|
#endif
|
|
|
|
|
|
|
|
- rc = Curl_resolv(data, dev, 80, FALSE, &h);
|
|
|
|
|
|
|
+ rc = Curl_resolv(data, host, 80, FALSE, &h);
|
|
|
if(rc == CURLRESOLV_PENDING)
|
|
if(rc == CURLRESOLV_PENDING)
|
|
|
(void)Curl_resolver_wait_resolv(data, &h);
|
|
(void)Curl_resolver_wait_resolv(data, &h);
|
|
|
conn->ip_version = ipver;
|
|
conn->ip_version = ipver;
|
|
@@ -528,7 +682,7 @@ static CURLcode bindlocal(struct Curl_easy *data, struct connectdata *conn,
|
|
|
/* convert the resolved address, sizeof myhost >= INET_ADDRSTRLEN */
|
|
/* convert the resolved address, sizeof myhost >= INET_ADDRSTRLEN */
|
|
|
Curl_printable_address(h->addr, myhost, sizeof(myhost));
|
|
Curl_printable_address(h->addr, myhost, sizeof(myhost));
|
|
|
infof(data, "Name '%s' family %i resolved to '%s' family %i",
|
|
infof(data, "Name '%s' family %i resolved to '%s' family %i",
|
|
|
- dev, af, myhost, h->addr->ai_family);
|
|
|
|
|
|
|
+ host, af, myhost, h->addr->ai_family);
|
|
|
Curl_resolv_unlock(data, h);
|
|
Curl_resolv_unlock(data, h);
|
|
|
if(af != h->addr->ai_family) {
|
|
if(af != h->addr->ai_family) {
|
|
|
/* bad IP version combo, signal the caller to try another address
|
|
/* bad IP version combo, signal the caller to try another address
|
|
@@ -562,7 +716,7 @@ static CURLcode bindlocal(struct Curl_easy *data, struct connectdata *conn,
|
|
|
if(scope_ptr) {
|
|
if(scope_ptr) {
|
|
|
/* The "myhost" string either comes from Curl_if2ip or from
|
|
/* The "myhost" string either comes from Curl_if2ip or from
|
|
|
Curl_printable_address. The latter returns only numeric scope
|
|
Curl_printable_address. The latter returns only numeric scope
|
|
|
- IDs and the former returns none at all. So the scope ID, if
|
|
|
|
|
|
|
+ IDs and the former returns none at all. So the scope ID, if
|
|
|
present, is known to be numeric */
|
|
present, is known to be numeric */
|
|
|
unsigned long scope_id = strtoul(scope_ptr, NULL, 10);
|
|
unsigned long scope_id = strtoul(scope_ptr, NULL, 10);
|
|
|
if(scope_id > UINT_MAX)
|
|
if(scope_id > UINT_MAX)
|
|
@@ -589,8 +743,11 @@ static CURLcode bindlocal(struct Curl_easy *data, struct connectdata *conn,
|
|
|
/* errorbuf is set false so failf will overwrite any message already in
|
|
/* errorbuf is set false so failf will overwrite any message already in
|
|
|
the error buffer, so the user receives this error message instead of a
|
|
the error buffer, so the user receives this error message instead of a
|
|
|
generic resolve error. */
|
|
generic resolve error. */
|
|
|
|
|
+ char buffer[STRERROR_LEN];
|
|
|
data->state.errorbuf = FALSE;
|
|
data->state.errorbuf = FALSE;
|
|
|
- failf(data, "Couldn't bind to '%s'", dev);
|
|
|
|
|
|
|
+ data->state.os_errno = error = SOCKERRNO;
|
|
|
|
|
+ failf(data, "Couldn't bind to '%s' with errno %d: %s",
|
|
|
|
|
+ host, error, Curl_strerror(error, buffer, sizeof(buffer)));
|
|
|
return CURLE_INTERFACE_FAILED;
|
|
return CURLE_INTERFACE_FAILED;
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
@@ -667,8 +824,8 @@ static bool verifyconnect(curl_socket_t sockfd, int *error)
|
|
|
* Gisle Vanem could reproduce the former problems with this function, but
|
|
* Gisle Vanem could reproduce the former problems with this function, but
|
|
|
* could avoid them by adding this SleepEx() call below:
|
|
* could avoid them by adding this SleepEx() call below:
|
|
|
*
|
|
*
|
|
|
- * "I don't have Rational Quantify, but the hint from his post was
|
|
|
|
|
- * ntdll::NtRemoveIoCompletion(). So I'd assume the SleepEx (or maybe
|
|
|
|
|
|
|
+ * "I do not have Rational Quantify, but the hint from his post was
|
|
|
|
|
+ * ntdll::NtRemoveIoCompletion(). I would assume the SleepEx (or maybe
|
|
|
* just Sleep(0) would be enough?) would release whatever
|
|
* just Sleep(0) would be enough?) would release whatever
|
|
|
* mutex/critical-section the ntdll call is waiting on.
|
|
* mutex/critical-section the ntdll call is waiting on.
|
|
|
*
|
|
*
|
|
@@ -686,14 +843,14 @@ static bool verifyconnect(curl_socket_t sockfd, int *error)
|
|
|
if(0 != getsockopt(sockfd, SOL_SOCKET, SO_ERROR, (void *)&err, &errSize))
|
|
if(0 != getsockopt(sockfd, SOL_SOCKET, SO_ERROR, (void *)&err, &errSize))
|
|
|
err = SOCKERRNO;
|
|
err = SOCKERRNO;
|
|
|
#ifdef _WIN32_WCE
|
|
#ifdef _WIN32_WCE
|
|
|
- /* Old WinCE versions don't support SO_ERROR */
|
|
|
|
|
|
|
+ /* Old WinCE versions do not support SO_ERROR */
|
|
|
if(WSAENOPROTOOPT == err) {
|
|
if(WSAENOPROTOOPT == err) {
|
|
|
SET_SOCKERRNO(0);
|
|
SET_SOCKERRNO(0);
|
|
|
err = 0;
|
|
err = 0;
|
|
|
}
|
|
}
|
|
|
#endif
|
|
#endif
|
|
|
#if defined(EBADIOCTL) && defined(__minix)
|
|
#if defined(EBADIOCTL) && defined(__minix)
|
|
|
- /* Minix 3.1.x doesn't support getsockopt on UDP sockets */
|
|
|
|
|
|
|
+ /* Minix 3.1.x does not support getsockopt on UDP sockets */
|
|
|
if(EBADIOCTL == err) {
|
|
if(EBADIOCTL == err) {
|
|
|
SET_SOCKERRNO(0);
|
|
SET_SOCKERRNO(0);
|
|
|
err = 0;
|
|
err = 0;
|
|
@@ -703,7 +860,7 @@ static bool verifyconnect(curl_socket_t sockfd, int *error)
|
|
|
/* we are connected, awesome! */
|
|
/* we are connected, awesome! */
|
|
|
rc = TRUE;
|
|
rc = TRUE;
|
|
|
else
|
|
else
|
|
|
- /* This wasn't a successful connect */
|
|
|
|
|
|
|
+ /* This was not a successful connect */
|
|
|
rc = FALSE;
|
|
rc = FALSE;
|
|
|
if(error)
|
|
if(error)
|
|
|
*error = err;
|
|
*error = err;
|
|
@@ -765,11 +922,14 @@ struct cf_socket_ctx {
|
|
|
int transport;
|
|
int transport;
|
|
|
struct Curl_sockaddr_ex addr; /* address to connect to */
|
|
struct Curl_sockaddr_ex addr; /* address to connect to */
|
|
|
curl_socket_t sock; /* current attempt socket */
|
|
curl_socket_t sock; /* current attempt socket */
|
|
|
- struct bufq recvbuf; /* used when `buffer_recv` is set */
|
|
|
|
|
struct ip_quadruple ip; /* The IP quadruple 2x(addr+port) */
|
|
struct ip_quadruple ip; /* The IP quadruple 2x(addr+port) */
|
|
|
struct curltime started_at; /* when socket was created */
|
|
struct curltime started_at; /* when socket was created */
|
|
|
struct curltime connected_at; /* when socket connected/got first byte */
|
|
struct curltime connected_at; /* when socket connected/got first byte */
|
|
|
struct curltime first_byte_at; /* when first byte was recvd */
|
|
struct curltime first_byte_at; /* when first byte was recvd */
|
|
|
|
|
+#ifdef USE_WINSOCK
|
|
|
|
|
+ struct curltime last_sndbuf_query_at; /* when SO_SNDBUF last queried */
|
|
|
|
|
+ ULONG sndbuf_size; /* the last set SO_SNDBUF size */
|
|
|
|
|
+#endif
|
|
|
int error; /* errno of last failure or 0 */
|
|
int error; /* errno of last failure or 0 */
|
|
|
#ifdef DEBUGBUILD
|
|
#ifdef DEBUGBUILD
|
|
|
int wblock_percent; /* percent of writes doing EAGAIN */
|
|
int wblock_percent; /* percent of writes doing EAGAIN */
|
|
@@ -781,7 +941,6 @@ struct cf_socket_ctx {
|
|
|
BIT(accepted); /* socket was accepted, not connected */
|
|
BIT(accepted); /* socket was accepted, not connected */
|
|
|
BIT(sock_connected); /* socket is "connected", e.g. in UDP */
|
|
BIT(sock_connected); /* socket is "connected", e.g. in UDP */
|
|
|
BIT(active);
|
|
BIT(active);
|
|
|
- BIT(buffer_recv);
|
|
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
static void cf_socket_ctx_init(struct cf_socket_ctx *ctx,
|
|
static void cf_socket_ctx_init(struct cf_socket_ctx *ctx,
|
|
@@ -792,7 +951,6 @@ static void cf_socket_ctx_init(struct cf_socket_ctx *ctx,
|
|
|
ctx->sock = CURL_SOCKET_BAD;
|
|
ctx->sock = CURL_SOCKET_BAD;
|
|
|
ctx->transport = transport;
|
|
ctx->transport = transport;
|
|
|
Curl_sock_assign_addr(&ctx->addr, ai, transport);
|
|
Curl_sock_assign_addr(&ctx->addr, ai, transport);
|
|
|
- Curl_bufq_init(&ctx->recvbuf, NW_RECV_CHUNK_SIZE, NW_RECV_CHUNKS);
|
|
|
|
|
#ifdef DEBUGBUILD
|
|
#ifdef DEBUGBUILD
|
|
|
{
|
|
{
|
|
|
char *p = getenv("CURL_DBG_SOCK_WBLOCK");
|
|
char *p = getenv("CURL_DBG_SOCK_WBLOCK");
|
|
@@ -823,56 +981,6 @@ static void cf_socket_ctx_init(struct cf_socket_ctx *ctx,
|
|
|
#endif
|
|
#endif
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-struct reader_ctx {
|
|
|
|
|
- struct Curl_cfilter *cf;
|
|
|
|
|
- struct Curl_easy *data;
|
|
|
|
|
-};
|
|
|
|
|
-
|
|
|
|
|
-static ssize_t nw_in_read(void *reader_ctx,
|
|
|
|
|
- unsigned char *buf, size_t len,
|
|
|
|
|
- CURLcode *err)
|
|
|
|
|
-{
|
|
|
|
|
- struct reader_ctx *rctx = reader_ctx;
|
|
|
|
|
- struct cf_socket_ctx *ctx = rctx->cf->ctx;
|
|
|
|
|
- ssize_t nread;
|
|
|
|
|
-
|
|
|
|
|
- *err = CURLE_OK;
|
|
|
|
|
- nread = sread(ctx->sock, buf, len);
|
|
|
|
|
-
|
|
|
|
|
- if(-1 == nread) {
|
|
|
|
|
- int sockerr = SOCKERRNO;
|
|
|
|
|
-
|
|
|
|
|
- if(
|
|
|
|
|
-#ifdef WSAEWOULDBLOCK
|
|
|
|
|
- /* This is how Windows does it */
|
|
|
|
|
- (WSAEWOULDBLOCK == sockerr)
|
|
|
|
|
-#else
|
|
|
|
|
- /* errno may be EWOULDBLOCK or on some systems EAGAIN when it returned
|
|
|
|
|
- due to its inability to send off data without blocking. We therefore
|
|
|
|
|
- treat both error codes the same here */
|
|
|
|
|
- (EWOULDBLOCK == sockerr) || (EAGAIN == sockerr) || (EINTR == sockerr)
|
|
|
|
|
-#endif
|
|
|
|
|
- ) {
|
|
|
|
|
- /* this is just a case of EWOULDBLOCK */
|
|
|
|
|
- *err = CURLE_AGAIN;
|
|
|
|
|
- nread = -1;
|
|
|
|
|
- }
|
|
|
|
|
- else {
|
|
|
|
|
- char buffer[STRERROR_LEN];
|
|
|
|
|
-
|
|
|
|
|
- failf(rctx->data, "Recv failure: %s",
|
|
|
|
|
- Curl_strerror(sockerr, buffer, sizeof(buffer)));
|
|
|
|
|
- rctx->data->state.os_errno = sockerr;
|
|
|
|
|
- *err = CURLE_RECV_ERROR;
|
|
|
|
|
- nread = -1;
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- CURL_TRC_CF(rctx->data, rctx->cf, "nw_in_read(len=%zu, fd=%"
|
|
|
|
|
- CURL_FORMAT_SOCKET_T ") -> %d, err=%d",
|
|
|
|
|
- len, ctx->sock, (int)nread, *err);
|
|
|
|
|
- return nread;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
static void cf_socket_close(struct Curl_cfilter *cf, struct Curl_easy *data)
|
|
static void cf_socket_close(struct Curl_cfilter *cf, struct Curl_easy *data)
|
|
|
{
|
|
{
|
|
|
struct cf_socket_ctx *ctx = cf->ctx;
|
|
struct cf_socket_ctx *ctx = cf->ctx;
|
|
@@ -886,9 +994,7 @@ static void cf_socket_close(struct Curl_cfilter *cf, struct Curl_easy *data)
|
|
|
ctx->sock = CURL_SOCKET_BAD;
|
|
ctx->sock = CURL_SOCKET_BAD;
|
|
|
if(ctx->active && cf->sockindex == FIRSTSOCKET)
|
|
if(ctx->active && cf->sockindex == FIRSTSOCKET)
|
|
|
cf->conn->remote_addr = NULL;
|
|
cf->conn->remote_addr = NULL;
|
|
|
- Curl_bufq_reset(&ctx->recvbuf);
|
|
|
|
|
ctx->active = FALSE;
|
|
ctx->active = FALSE;
|
|
|
- ctx->buffer_recv = FALSE;
|
|
|
|
|
memset(&ctx->started_at, 0, sizeof(ctx->started_at));
|
|
memset(&ctx->started_at, 0, sizeof(ctx->started_at));
|
|
|
memset(&ctx->connected_at, 0, sizeof(ctx->connected_at));
|
|
memset(&ctx->connected_at, 0, sizeof(ctx->connected_at));
|
|
|
}
|
|
}
|
|
@@ -896,13 +1002,35 @@ static void cf_socket_close(struct Curl_cfilter *cf, struct Curl_easy *data)
|
|
|
cf->connected = FALSE;
|
|
cf->connected = FALSE;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+static CURLcode cf_socket_shutdown(struct Curl_cfilter *cf,
|
|
|
|
|
+ struct Curl_easy *data,
|
|
|
|
|
+ bool *done)
|
|
|
|
|
+{
|
|
|
|
|
+ if(cf->connected) {
|
|
|
|
|
+ struct cf_socket_ctx *ctx = cf->ctx;
|
|
|
|
|
+
|
|
|
|
|
+ CURL_TRC_CF(data, cf, "cf_socket_shutdown(%" CURL_FORMAT_SOCKET_T
|
|
|
|
|
+ ")", ctx->sock);
|
|
|
|
|
+ /* On TCP, and when the socket looks well and non-blocking mode
|
|
|
|
|
+ * can be enabled, receive dangling bytes before close to avoid
|
|
|
|
|
+ * entering RST states unnecessarily. */
|
|
|
|
|
+ if(ctx->sock != CURL_SOCKET_BAD &&
|
|
|
|
|
+ ctx->transport == TRNSPRT_TCP &&
|
|
|
|
|
+ (curlx_nonblock(ctx->sock, TRUE) >= 0)) {
|
|
|
|
|
+ unsigned char buf[1024];
|
|
|
|
|
+ (void)sread(ctx->sock, buf, sizeof(buf));
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ *done = TRUE;
|
|
|
|
|
+ return CURLE_OK;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
static void cf_socket_destroy(struct Curl_cfilter *cf, struct Curl_easy *data)
|
|
static void cf_socket_destroy(struct Curl_cfilter *cf, struct Curl_easy *data)
|
|
|
{
|
|
{
|
|
|
struct cf_socket_ctx *ctx = cf->ctx;
|
|
struct cf_socket_ctx *ctx = cf->ctx;
|
|
|
|
|
|
|
|
cf_socket_close(cf, data);
|
|
cf_socket_close(cf, data);
|
|
|
CURL_TRC_CF(data, cf, "destroy");
|
|
CURL_TRC_CF(data, cf, "destroy");
|
|
|
- Curl_bufq_free(&ctx->recvbuf);
|
|
|
|
|
free(ctx);
|
|
free(ctx);
|
|
|
cf->ctx = NULL;
|
|
cf->ctx = NULL;
|
|
|
}
|
|
}
|
|
@@ -949,7 +1077,7 @@ static CURLcode set_remote_ip(struct Curl_cfilter *cf,
|
|
|
struct cf_socket_ctx *ctx = cf->ctx;
|
|
struct cf_socket_ctx *ctx = cf->ctx;
|
|
|
|
|
|
|
|
/* store remote address and port used in this connection attempt */
|
|
/* store remote address and port used in this connection attempt */
|
|
|
- if(!Curl_addr2string(&ctx->addr.sa_addr, ctx->addr.addrlen,
|
|
|
|
|
|
|
+ if(!Curl_addr2string(&ctx->addr.sa_addr, (curl_socklen_t)ctx->addr.addrlen,
|
|
|
ctx->ip.remote_ip, &ctx->ip.remote_port)) {
|
|
ctx->ip.remote_ip, &ctx->ip.remote_port)) {
|
|
|
char buffer[STRERROR_LEN];
|
|
char buffer[STRERROR_LEN];
|
|
|
|
|
|
|
@@ -974,7 +1102,20 @@ static CURLcode cf_socket_open(struct Curl_cfilter *cf,
|
|
|
(void)data;
|
|
(void)data;
|
|
|
DEBUGASSERT(ctx->sock == CURL_SOCKET_BAD);
|
|
DEBUGASSERT(ctx->sock == CURL_SOCKET_BAD);
|
|
|
ctx->started_at = Curl_now();
|
|
ctx->started_at = Curl_now();
|
|
|
|
|
+#ifdef SOCK_NONBLOCK
|
|
|
|
|
+ /* Do not tuck SOCK_NONBLOCK into socktype when opensocket callback is set
|
|
|
|
|
+ * because we would not know how socketype is about to be used in the
|
|
|
|
|
+ * callback, SOCK_NONBLOCK might get factored out before calling socket().
|
|
|
|
|
+ */
|
|
|
|
|
+ if(!data->set.fopensocket)
|
|
|
|
|
+ ctx->addr.socktype |= SOCK_NONBLOCK;
|
|
|
|
|
+#endif
|
|
|
result = socket_open(data, &ctx->addr, &ctx->sock);
|
|
result = socket_open(data, &ctx->addr, &ctx->sock);
|
|
|
|
|
+#ifdef SOCK_NONBLOCK
|
|
|
|
|
+ /* Restore the socktype after the socket is created. */
|
|
|
|
|
+ if(!data->set.fopensocket)
|
|
|
|
|
+ ctx->addr.socktype &= ~SOCK_NONBLOCK;
|
|
|
|
|
+#endif
|
|
|
if(result)
|
|
if(result)
|
|
|
goto out;
|
|
goto out;
|
|
|
|
|
|
|
@@ -1004,7 +1145,7 @@ static CURLcode cf_socket_open(struct Curl_cfilter *cf,
|
|
|
|
|
|
|
|
nosigpipe(data, ctx->sock);
|
|
nosigpipe(data, ctx->sock);
|
|
|
|
|
|
|
|
- Curl_sndbufset(ctx->sock);
|
|
|
|
|
|
|
+ Curl_sndbuf_init(ctx->sock);
|
|
|
|
|
|
|
|
if(is_tcp && data->set.tcp_keepalive)
|
|
if(is_tcp && data->set.tcp_keepalive)
|
|
|
tcpkeepalive(data, ctx->sock);
|
|
tcpkeepalive(data, ctx->sock);
|
|
@@ -1045,8 +1186,27 @@ static CURLcode cf_socket_open(struct Curl_cfilter *cf,
|
|
|
}
|
|
}
|
|
|
#endif
|
|
#endif
|
|
|
|
|
|
|
|
- /* set socket non-blocking */
|
|
|
|
|
- (void)curlx_nonblock(ctx->sock, TRUE);
|
|
|
|
|
|
|
+#ifndef SOCK_NONBLOCK
|
|
|
|
|
+ /* Set socket non-blocking, must be a non-blocking socket for
|
|
|
|
|
+ * a non-blocking connect. */
|
|
|
|
|
+ error = curlx_nonblock(ctx->sock, TRUE);
|
|
|
|
|
+ if(error < 0) {
|
|
|
|
|
+ result = CURLE_UNSUPPORTED_PROTOCOL;
|
|
|
|
|
+ ctx->error = SOCKERRNO;
|
|
|
|
|
+ goto out;
|
|
|
|
|
+ }
|
|
|
|
|
+#else
|
|
|
|
|
+ if(data->set.fopensocket) {
|
|
|
|
|
+ /* Set socket non-blocking, must be a non-blocking socket for
|
|
|
|
|
+ * a non-blocking connect. */
|
|
|
|
|
+ error = curlx_nonblock(ctx->sock, TRUE);
|
|
|
|
|
+ if(error < 0) {
|
|
|
|
|
+ result = CURLE_UNSUPPORTED_PROTOCOL;
|
|
|
|
|
+ ctx->error = SOCKERRNO;
|
|
|
|
|
+ goto out;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+#endif
|
|
|
ctx->sock_connected = (ctx->addr.socktype != SOCK_DGRAM);
|
|
ctx->sock_connected = (ctx->addr.socktype != SOCK_DGRAM);
|
|
|
out:
|
|
out:
|
|
|
if(result) {
|
|
if(result) {
|
|
@@ -1114,7 +1274,8 @@ static int do_connect(struct Curl_cfilter *cf, struct Curl_easy *data,
|
|
|
#endif
|
|
#endif
|
|
|
}
|
|
}
|
|
|
else {
|
|
else {
|
|
|
- rc = connect(ctx->sock, &ctx->addr.sa_addr, ctx->addr.addrlen);
|
|
|
|
|
|
|
+ rc = connect(ctx->sock, &ctx->addr.sa_addr,
|
|
|
|
|
+ (curl_socklen_t)ctx->addr.addrlen);
|
|
|
}
|
|
}
|
|
|
return rc;
|
|
return rc;
|
|
|
}
|
|
}
|
|
@@ -1257,13 +1418,36 @@ static bool cf_socket_data_pending(struct Curl_cfilter *cf,
|
|
|
int readable;
|
|
int readable;
|
|
|
|
|
|
|
|
(void)data;
|
|
(void)data;
|
|
|
- if(!Curl_bufq_is_empty(&ctx->recvbuf))
|
|
|
|
|
- return TRUE;
|
|
|
|
|
-
|
|
|
|
|
readable = SOCKET_READABLE(ctx->sock, 0);
|
|
readable = SOCKET_READABLE(ctx->sock, 0);
|
|
|
return (readable > 0 && (readable & CURL_CSELECT_IN));
|
|
return (readable > 0 && (readable & CURL_CSELECT_IN));
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+#ifdef USE_WINSOCK
|
|
|
|
|
+
|
|
|
|
|
+#ifndef SIO_IDEAL_SEND_BACKLOG_QUERY
|
|
|
|
|
+#define SIO_IDEAL_SEND_BACKLOG_QUERY 0x4004747B
|
|
|
|
|
+#endif
|
|
|
|
|
+
|
|
|
|
|
+static void win_update_sndbuf_size(struct cf_socket_ctx *ctx)
|
|
|
|
|
+{
|
|
|
|
|
+ ULONG ideal;
|
|
|
|
|
+ DWORD ideallen;
|
|
|
|
|
+ struct curltime n = Curl_now();
|
|
|
|
|
+
|
|
|
|
|
+ if(Curl_timediff(n, ctx->last_sndbuf_query_at) > 1000) {
|
|
|
|
|
+ if(!WSAIoctl(ctx->sock, SIO_IDEAL_SEND_BACKLOG_QUERY, 0, 0,
|
|
|
|
|
+ &ideal, sizeof(ideal), &ideallen, 0, 0) &&
|
|
|
|
|
+ ideal != ctx->sndbuf_size &&
|
|
|
|
|
+ !setsockopt(ctx->sock, SOL_SOCKET, SO_SNDBUF,
|
|
|
|
|
+ (const char *)&ideal, sizeof(ideal))) {
|
|
|
|
|
+ ctx->sndbuf_size = ideal;
|
|
|
|
|
+ }
|
|
|
|
|
+ ctx->last_sndbuf_query_at = n;
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+#endif /* USE_WINSOCK */
|
|
|
|
|
+
|
|
|
static ssize_t cf_socket_send(struct Curl_cfilter *cf, struct Curl_easy *data,
|
|
static ssize_t cf_socket_send(struct Curl_cfilter *cf, struct Curl_easy *data,
|
|
|
const void *buf, size_t len, CURLcode *err)
|
|
const void *buf, size_t len, CURLcode *err)
|
|
|
{
|
|
{
|
|
@@ -1336,6 +1520,11 @@ static ssize_t cf_socket_send(struct Curl_cfilter *cf, struct Curl_easy *data,
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+#if defined(USE_WINSOCK)
|
|
|
|
|
+ if(!*err)
|
|
|
|
|
+ win_update_sndbuf_size(ctx);
|
|
|
|
|
+#endif
|
|
|
|
|
+
|
|
|
CURL_TRC_CF(data, cf, "send(len=%zu) -> %d, err=%d",
|
|
CURL_TRC_CF(data, cf, "send(len=%zu) -> %d, err=%d",
|
|
|
orig_len, (int)nwritten, *err);
|
|
orig_len, (int)nwritten, *err);
|
|
|
cf->conn->sock[cf->sockindex] = fdsave;
|
|
cf->conn->sock[cf->sockindex] = fdsave;
|
|
@@ -1346,14 +1535,10 @@ static ssize_t cf_socket_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
|
|
|
char *buf, size_t len, CURLcode *err)
|
|
char *buf, size_t len, CURLcode *err)
|
|
|
{
|
|
{
|
|
|
struct cf_socket_ctx *ctx = cf->ctx;
|
|
struct cf_socket_ctx *ctx = cf->ctx;
|
|
|
- curl_socket_t fdsave;
|
|
|
|
|
ssize_t nread;
|
|
ssize_t nread;
|
|
|
|
|
|
|
|
*err = CURLE_OK;
|
|
*err = CURLE_OK;
|
|
|
|
|
|
|
|
- fdsave = cf->conn->sock[cf->sockindex];
|
|
|
|
|
- cf->conn->sock[cf->sockindex] = ctx->sock;
|
|
|
|
|
-
|
|
|
|
|
#ifdef DEBUGBUILD
|
|
#ifdef DEBUGBUILD
|
|
|
/* simulate network blocking/partial reads */
|
|
/* simulate network blocking/partial reads */
|
|
|
if(cf->cft != &Curl_cft_udp && ctx->rblock_percent > 0) {
|
|
if(cf->cft != &Curl_cft_udp && ctx->rblock_percent > 0) {
|
|
@@ -1362,9 +1547,7 @@ static ssize_t cf_socket_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
|
|
|
if(c >= ((100-ctx->rblock_percent)*256/100)) {
|
|
if(c >= ((100-ctx->rblock_percent)*256/100)) {
|
|
|
CURL_TRC_CF(data, cf, "recv(len=%zu) SIMULATE EWOULDBLOCK", len);
|
|
CURL_TRC_CF(data, cf, "recv(len=%zu) SIMULATE EWOULDBLOCK", len);
|
|
|
*err = CURLE_AGAIN;
|
|
*err = CURLE_AGAIN;
|
|
|
- nread = -1;
|
|
|
|
|
- cf->conn->sock[cf->sockindex] = fdsave;
|
|
|
|
|
- return nread;
|
|
|
|
|
|
|
+ return -1;
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
if(cf->cft != &Curl_cft_udp && ctx->recv_max && ctx->recv_max < len) {
|
|
if(cf->cft != &Curl_cft_udp && ctx->recv_max && ctx->recv_max < len) {
|
|
@@ -1375,54 +1558,42 @@ static ssize_t cf_socket_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
|
|
|
}
|
|
}
|
|
|
#endif
|
|
#endif
|
|
|
|
|
|
|
|
- if(ctx->buffer_recv && !Curl_bufq_is_empty(&ctx->recvbuf)) {
|
|
|
|
|
- CURL_TRC_CF(data, cf, "recv from buffer");
|
|
|
|
|
- nread = Curl_bufq_read(&ctx->recvbuf, (unsigned char *)buf, len, err);
|
|
|
|
|
- }
|
|
|
|
|
- else {
|
|
|
|
|
- struct reader_ctx rctx;
|
|
|
|
|
-
|
|
|
|
|
- rctx.cf = cf;
|
|
|
|
|
- rctx.data = data;
|
|
|
|
|
-
|
|
|
|
|
- /* "small" reads may trigger filling our buffer, "large" reads
|
|
|
|
|
- * are probably not worth the additional copy */
|
|
|
|
|
- if(ctx->buffer_recv && len < NW_SMALL_READS) {
|
|
|
|
|
- ssize_t nwritten;
|
|
|
|
|
- nwritten = Curl_bufq_slurp(&ctx->recvbuf, nw_in_read, &rctx, err);
|
|
|
|
|
- if(nwritten < 0 && !Curl_bufq_is_empty(&ctx->recvbuf)) {
|
|
|
|
|
- /* we have a partial read with an error. need to deliver
|
|
|
|
|
- * what we got, return the error later. */
|
|
|
|
|
- CURL_TRC_CF(data, cf, "partial read: empty buffer first");
|
|
|
|
|
- nread = Curl_bufq_read(&ctx->recvbuf, (unsigned char *)buf, len, err);
|
|
|
|
|
- }
|
|
|
|
|
- else if(nwritten < 0) {
|
|
|
|
|
- nread = -1;
|
|
|
|
|
- goto out;
|
|
|
|
|
- }
|
|
|
|
|
- else if(nwritten == 0) {
|
|
|
|
|
- /* eof */
|
|
|
|
|
- *err = CURLE_OK;
|
|
|
|
|
- nread = 0;
|
|
|
|
|
- }
|
|
|
|
|
- else {
|
|
|
|
|
- CURL_TRC_CF(data, cf, "buffered %zd additional bytes", nwritten);
|
|
|
|
|
- nread = Curl_bufq_read(&ctx->recvbuf, (unsigned char *)buf, len, err);
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ *err = CURLE_OK;
|
|
|
|
|
+ nread = sread(ctx->sock, buf, len);
|
|
|
|
|
+
|
|
|
|
|
+ if(-1 == nread) {
|
|
|
|
|
+ int sockerr = SOCKERRNO;
|
|
|
|
|
+
|
|
|
|
|
+ if(
|
|
|
|
|
+#ifdef WSAEWOULDBLOCK
|
|
|
|
|
+ /* This is how Windows does it */
|
|
|
|
|
+ (WSAEWOULDBLOCK == sockerr)
|
|
|
|
|
+#else
|
|
|
|
|
+ /* errno may be EWOULDBLOCK or on some systems EAGAIN when it returned
|
|
|
|
|
+ due to its inability to send off data without blocking. We therefore
|
|
|
|
|
+ treat both error codes the same here */
|
|
|
|
|
+ (EWOULDBLOCK == sockerr) || (EAGAIN == sockerr) || (EINTR == sockerr)
|
|
|
|
|
+#endif
|
|
|
|
|
+ ) {
|
|
|
|
|
+ /* this is just a case of EWOULDBLOCK */
|
|
|
|
|
+ *err = CURLE_AGAIN;
|
|
|
}
|
|
}
|
|
|
else {
|
|
else {
|
|
|
- nread = nw_in_read(&rctx, (unsigned char *)buf, len, err);
|
|
|
|
|
|
|
+ char buffer[STRERROR_LEN];
|
|
|
|
|
+
|
|
|
|
|
+ failf(data, "Recv failure: %s",
|
|
|
|
|
+ Curl_strerror(sockerr, buffer, sizeof(buffer)));
|
|
|
|
|
+ data->state.os_errno = sockerr;
|
|
|
|
|
+ *err = CURLE_RECV_ERROR;
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-out:
|
|
|
|
|
CURL_TRC_CF(data, cf, "recv(len=%zu) -> %d, err=%d", len, (int)nread,
|
|
CURL_TRC_CF(data, cf, "recv(len=%zu) -> %d, err=%d", len, (int)nread,
|
|
|
*err);
|
|
*err);
|
|
|
if(nread > 0 && !ctx->got_first_byte) {
|
|
if(nread > 0 && !ctx->got_first_byte) {
|
|
|
ctx->first_byte_at = Curl_now();
|
|
ctx->first_byte_at = Curl_now();
|
|
|
ctx->got_first_byte = TRUE;
|
|
ctx->got_first_byte = TRUE;
|
|
|
}
|
|
}
|
|
|
- cf->conn->sock[cf->sockindex] = fdsave;
|
|
|
|
|
return nread;
|
|
return nread;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -1444,11 +1615,6 @@ static void cf_socket_active(struct Curl_cfilter *cf, struct Curl_easy *data)
|
|
|
cf->conn->bits.ipv6 = (ctx->addr.family == AF_INET6)? TRUE : FALSE;
|
|
cf->conn->bits.ipv6 = (ctx->addr.family == AF_INET6)? TRUE : FALSE;
|
|
|
#endif
|
|
#endif
|
|
|
Curl_persistconninfo(data, cf->conn, &ctx->ip);
|
|
Curl_persistconninfo(data, cf->conn, &ctx->ip);
|
|
|
- /* buffering is currently disabled by default because we have stalls
|
|
|
|
|
- * in parallel transfers where not all buffered data is consumed and no
|
|
|
|
|
- * socket events happen.
|
|
|
|
|
- */
|
|
|
|
|
- ctx->buffer_recv = FALSE;
|
|
|
|
|
}
|
|
}
|
|
|
ctx->active = TRUE;
|
|
ctx->active = TRUE;
|
|
|
}
|
|
}
|
|
@@ -1564,6 +1730,7 @@ struct Curl_cftype Curl_cft_tcp = {
|
|
|
cf_socket_destroy,
|
|
cf_socket_destroy,
|
|
|
cf_tcp_connect,
|
|
cf_tcp_connect,
|
|
|
cf_socket_close,
|
|
cf_socket_close,
|
|
|
|
|
+ cf_socket_shutdown,
|
|
|
cf_socket_get_host,
|
|
cf_socket_get_host,
|
|
|
cf_socket_adjust_pollset,
|
|
cf_socket_adjust_pollset,
|
|
|
cf_socket_data_pending,
|
|
cf_socket_data_pending,
|
|
@@ -1608,25 +1775,23 @@ out:
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
static CURLcode cf_udp_setup_quic(struct Curl_cfilter *cf,
|
|
static CURLcode cf_udp_setup_quic(struct Curl_cfilter *cf,
|
|
|
- struct Curl_easy *data)
|
|
|
|
|
|
|
+ struct Curl_easy *data)
|
|
|
{
|
|
{
|
|
|
struct cf_socket_ctx *ctx = cf->ctx;
|
|
struct cf_socket_ctx *ctx = cf->ctx;
|
|
|
int rc;
|
|
int rc;
|
|
|
|
|
+ int one = 1;
|
|
|
|
|
+
|
|
|
|
|
+ (void)one;
|
|
|
|
|
|
|
|
/* QUIC needs a connected socket, nonblocking */
|
|
/* QUIC needs a connected socket, nonblocking */
|
|
|
DEBUGASSERT(ctx->sock != CURL_SOCKET_BAD);
|
|
DEBUGASSERT(ctx->sock != CURL_SOCKET_BAD);
|
|
|
|
|
|
|
|
-#if defined(__APPLE__) && defined(USE_OPENSSL_QUIC)
|
|
|
|
|
- (void)rc;
|
|
|
|
|
- /* On macOS OpenSSL QUIC fails on connected sockets.
|
|
|
|
|
- * see: <https://github.com/openssl/openssl/issues/23251> */
|
|
|
|
|
-#else
|
|
|
|
|
- rc = connect(ctx->sock, &ctx->addr.sa_addr, ctx->addr.addrlen);
|
|
|
|
|
|
|
+ rc = connect(ctx->sock, &ctx->addr.sa_addr,
|
|
|
|
|
+ (curl_socklen_t)ctx->addr.addrlen);
|
|
|
if(-1 == rc) {
|
|
if(-1 == rc) {
|
|
|
return socket_connect_result(data, ctx->ip.remote_ip, SOCKERRNO);
|
|
return socket_connect_result(data, ctx->ip.remote_ip, SOCKERRNO);
|
|
|
}
|
|
}
|
|
|
ctx->sock_connected = TRUE;
|
|
ctx->sock_connected = TRUE;
|
|
|
-#endif
|
|
|
|
|
set_local_ip(cf, data);
|
|
set_local_ip(cf, data);
|
|
|
CURL_TRC_CF(data, cf, "%s socket %" CURL_FORMAT_SOCKET_T
|
|
CURL_TRC_CF(data, cf, "%s socket %" CURL_FORMAT_SOCKET_T
|
|
|
" connected: [%s:%d] -> [%s:%d]",
|
|
" connected: [%s:%d] -> [%s:%d]",
|
|
@@ -1634,7 +1799,11 @@ static CURLcode cf_udp_setup_quic(struct Curl_cfilter *cf,
|
|
|
ctx->sock, ctx->ip.local_ip, ctx->ip.local_port,
|
|
ctx->sock, ctx->ip.local_ip, ctx->ip.local_port,
|
|
|
ctx->ip.remote_ip, ctx->ip.remote_port);
|
|
ctx->ip.remote_ip, ctx->ip.remote_port);
|
|
|
|
|
|
|
|
- (void)curlx_nonblock(ctx->sock, TRUE);
|
|
|
|
|
|
|
+ /* Currently, cf->ctx->sock is always non-blocking because the only
|
|
|
|
|
+ * caller to cf_udp_setup_quic() is cf_udp_connect() that passes the
|
|
|
|
|
+ * non-blocking socket created by cf_socket_open() to it. Thus, we
|
|
|
|
|
+ * do not need to call curlx_nonblock() in cf_udp_setup_quic() anymore.
|
|
|
|
|
+ */
|
|
|
switch(ctx->addr.family) {
|
|
switch(ctx->addr.family) {
|
|
|
#if defined(__linux__) && defined(IP_MTU_DISCOVER)
|
|
#if defined(__linux__) && defined(IP_MTU_DISCOVER)
|
|
|
case AF_INET: {
|
|
case AF_INET: {
|
|
@@ -1653,6 +1822,14 @@ static CURLcode cf_udp_setup_quic(struct Curl_cfilter *cf,
|
|
|
}
|
|
}
|
|
|
#endif
|
|
#endif
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+#if defined(__linux__) && defined(UDP_GRO) && \
|
|
|
|
|
+ (defined(HAVE_SENDMMSG) || defined(HAVE_SENDMSG)) && \
|
|
|
|
|
+ ((defined(USE_NGTCP2) && defined(USE_NGHTTP3)) || defined(USE_QUICHE))
|
|
|
|
|
+ (void)setsockopt(ctx->sock, IPPROTO_UDP, UDP_GRO, &one,
|
|
|
|
|
+ (socklen_t)sizeof(one));
|
|
|
|
|
+#endif
|
|
|
|
|
+
|
|
|
return CURLE_OK;
|
|
return CURLE_OK;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -1702,6 +1879,7 @@ struct Curl_cftype Curl_cft_udp = {
|
|
|
cf_socket_destroy,
|
|
cf_socket_destroy,
|
|
|
cf_udp_connect,
|
|
cf_udp_connect,
|
|
|
cf_socket_close,
|
|
cf_socket_close,
|
|
|
|
|
+ cf_socket_shutdown,
|
|
|
cf_socket_get_host,
|
|
cf_socket_get_host,
|
|
|
cf_socket_adjust_pollset,
|
|
cf_socket_adjust_pollset,
|
|
|
cf_socket_data_pending,
|
|
cf_socket_data_pending,
|
|
@@ -1753,6 +1931,7 @@ struct Curl_cftype Curl_cft_unix = {
|
|
|
cf_socket_destroy,
|
|
cf_socket_destroy,
|
|
|
cf_tcp_connect,
|
|
cf_tcp_connect,
|
|
|
cf_socket_close,
|
|
cf_socket_close,
|
|
|
|
|
+ cf_socket_shutdown,
|
|
|
cf_socket_get_host,
|
|
cf_socket_get_host,
|
|
|
cf_socket_adjust_pollset,
|
|
cf_socket_adjust_pollset,
|
|
|
cf_socket_data_pending,
|
|
cf_socket_data_pending,
|
|
@@ -1817,6 +1996,7 @@ struct Curl_cftype Curl_cft_tcp_accept = {
|
|
|
cf_socket_destroy,
|
|
cf_socket_destroy,
|
|
|
cf_tcp_accept_connect,
|
|
cf_tcp_accept_connect,
|
|
|
cf_socket_close,
|
|
cf_socket_close,
|
|
|
|
|
+ cf_socket_shutdown,
|
|
|
cf_socket_get_host, /* TODO: not accurate */
|
|
cf_socket_get_host, /* TODO: not accurate */
|
|
|
cf_socket_adjust_pollset,
|
|
cf_socket_adjust_pollset,
|
|
|
cf_socket_data_pending,
|
|
cf_socket_data_pending,
|