summaryrefslogtreecommitdiffstats
path: root/lib/cf-socket.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/cf-socket.c')
-rw-r--r--lib/cf-socket.c201
1 files changed, 126 insertions, 75 deletions
diff --git a/lib/cf-socket.c b/lib/cf-socket.c
index 5729fe0..effe6e6 100644
--- a/lib/cf-socket.c
+++ b/lib/cf-socket.c
@@ -71,6 +71,7 @@
#include "warnless.h"
#include "conncache.h"
#include "multihandle.h"
+#include "rand.h"
#include "share.h"
#include "version_win32.h"
@@ -390,6 +391,7 @@ void Curl_sndbufset(curl_socket_t sockfd)
}
#endif
+#ifndef CURL_DISABLE_BINDLOCAL
static CURLcode bindlocal(struct Curl_easy *data, struct connectdata *conn,
curl_socket_t sockfd, int af, unsigned int scope)
{
@@ -444,29 +446,24 @@ static CURLcode bindlocal(struct Curl_easy *data, struct connectdata *conn,
/* interface */
if(!is_host) {
#ifdef SO_BINDTODEVICE
- /* I am not sure any other OSs than Linux that provide this feature,
- * and at the least I cannot test. --Ben
- *
- * This feature allows one to tightly bind 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.
+ /*
+ * 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.
*
- * 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.
+ * 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 typically "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.
+ /* 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);
return CURLE_OK;
}
#endif
@@ -632,8 +629,8 @@ static CURLcode bindlocal(struct Curl_easy *data, struct connectdata *conn,
port++; /* try next port */
if(port == 0)
break;
- infof(data, "Bind to local port %hu failed, trying next", port - 1);
- /* We re-use/clobber the port variable here below */
+ infof(data, "Bind to local port %d failed, trying next", port - 1);
+ /* We reuse/clobber the port variable here below */
if(sock->sa_family == AF_INET)
si4->sin_port = ntohs(port);
#ifdef ENABLE_IPV6
@@ -653,6 +650,7 @@ static CURLcode bindlocal(struct Curl_easy *data, struct connectdata *conn,
return CURLE_INTERFACE_FAILED;
}
+#endif
/*
* verifyconnect() returns TRUE if the connect really has happened.
@@ -727,8 +725,6 @@ static bool verifyconnect(curl_socket_t sockfd, int *error)
static CURLcode socket_connect_result(struct Curl_easy *data,
const char *ipaddress, int error)
{
- char buffer[STRERROR_LEN];
-
switch(error) {
case EINPROGRESS:
case EWOULDBLOCK:
@@ -745,8 +741,15 @@ static CURLcode socket_connect_result(struct Curl_easy *data,
default:
/* unknown error, fallthrough and try another address! */
- infof(data, "Immediate connect fail for %s: %s",
- ipaddress, Curl_strerror(error, buffer, sizeof(buffer)));
+#ifdef CURL_DISABLE_VERBOSE_STRINGS
+ (void)ipaddress;
+#else
+ {
+ char buffer[STRERROR_LEN];
+ infof(data, "Immediate connect fail for %s: %s",
+ ipaddress, Curl_strerror(error, buffer, sizeof(buffer)));
+ }
+#endif
data->state.os_errno = error;
/* connect failed */
return CURLE_COULDNT_CONNECT;
@@ -775,6 +778,10 @@ struct cf_socket_ctx {
struct curltime connected_at; /* when socket connected/got first byte */
struct curltime first_byte_at; /* when first byte was recvd */
int error; /* errno of last failure or 0 */
+#ifdef DEBUGBUILD
+ int wblock_percent; /* percent of writes doing EAGAIN */
+ int wpartial_percent; /* percent of bytes written in send */
+#endif
BIT(got_first_byte); /* if first byte was received */
BIT(accepted); /* socket was accepted, not connected */
BIT(active);
@@ -790,6 +797,22 @@ static void cf_socket_ctx_init(struct cf_socket_ctx *ctx,
ctx->transport = transport;
Curl_sock_assign_addr(&ctx->addr, ai, transport);
Curl_bufq_init(&ctx->recvbuf, NW_RECV_CHUNK_SIZE, NW_RECV_CHUNKS);
+#ifdef DEBUGBUILD
+ {
+ char *p = getenv("CURL_DBG_SOCK_WBLOCK");
+ if(p) {
+ long l = strtol(p, NULL, 10);
+ if(l >= 0 && l <= 100)
+ ctx->wblock_percent = (int)l;
+ }
+ p = getenv("CURL_DBG_SOCK_WPARTIAL");
+ if(p) {
+ long l = strtol(p, NULL, 10);
+ if(l >= 0 && l <= 100)
+ ctx->wpartial_percent = (int)l;
+ }
+ }
+#endif
}
struct reader_ctx {
@@ -836,8 +859,8 @@ static ssize_t nw_in_read(void *reader_ctx,
nread = -1;
}
}
- DEBUGF(LOG_CF(rctx->data, rctx->cf, "nw_in_read(len=%zu) -> %d, err=%d",
- len, (int)nread, *err));
+ CURL_TRC_CF(rctx->data, rctx->cf, "nw_in_read(len=%zu) -> %d, err=%d",
+ len, (int)nread, *err);
return nread;
}
@@ -852,14 +875,14 @@ static void cf_socket_close(struct Curl_cfilter *cf, struct Curl_easy *data)
* closed it) and we just forget about it.
*/
if(ctx->sock == cf->conn->sock[cf->sockindex]) {
- DEBUGF(LOG_CF(data, cf, "cf_socket_close(%" CURL_FORMAT_SOCKET_T
- ", active)", ctx->sock));
+ CURL_TRC_CF(data, cf, "cf_socket_close(%" CURL_FORMAT_SOCKET_T
+ ", active)", ctx->sock);
socket_close(data, cf->conn, !ctx->accepted, ctx->sock);
cf->conn->sock[cf->sockindex] = CURL_SOCKET_BAD;
}
else {
- DEBUGF(LOG_CF(data, cf, "cf_socket_close(%" CURL_FORMAT_SOCKET_T
- ") no longer at conn->sock[], discarding", ctx->sock));
+ CURL_TRC_CF(data, cf, "cf_socket_close(%" CURL_FORMAT_SOCKET_T
+ ") no longer at conn->sock[], discarding", ctx->sock);
/* TODO: we do not want this to happen. Need to check which
* code is messing with conn->sock[cf->sockindex] */
}
@@ -869,8 +892,8 @@ static void cf_socket_close(struct Curl_cfilter *cf, struct Curl_easy *data)
}
else {
/* this is our local socket, we did never publish it */
- DEBUGF(LOG_CF(data, cf, "cf_socket_close(%" CURL_FORMAT_SOCKET_T
- ", not active)", ctx->sock));
+ CURL_TRC_CF(data, cf, "cf_socket_close(%" CURL_FORMAT_SOCKET_T
+ ", not active)", ctx->sock);
socket_close(data, cf->conn, !ctx->accepted, ctx->sock);
ctx->sock = CURL_SOCKET_BAD;
}
@@ -889,7 +912,7 @@ static void cf_socket_destroy(struct Curl_cfilter *cf, struct Curl_easy *data)
struct cf_socket_ctx *ctx = cf->ctx;
cf_socket_close(cf, data);
- DEBUGF(LOG_CF(data, cf, "destroy"));
+ CURL_TRC_CF(data, cf, "destroy");
Curl_bufq_free(&ctx->recvbuf);
free(ctx);
cf->ctx = NULL;
@@ -957,7 +980,6 @@ static CURLcode cf_socket_open(struct Curl_cfilter *cf,
bool isconnected = FALSE;
CURLcode result = CURLE_COULDNT_CONNECT;
bool is_tcp;
- const char *ipmsg;
(void)data;
DEBUGASSERT(ctx->sock == CURL_SOCKET_BAD);
@@ -970,15 +992,20 @@ static CURLcode cf_socket_open(struct Curl_cfilter *cf,
if(result)
goto out;
+#ifndef CURL_DISABLE_VERBOSE_STRINGS
+ {
+ const char *ipmsg;
#ifdef ENABLE_IPV6
- if(ctx->addr.family == AF_INET6) {
- set_ipv6_v6only(ctx->sock, 0);
- ipmsg = " Trying [%s]:%d...";
+ if(ctx->addr.family == AF_INET6) {
+ set_ipv6_v6only(ctx->sock, 0);
+ ipmsg = " Trying [%s]:%d...";
+ }
+ else
+#endif
+ ipmsg = " Trying %s:%d...";
+ infof(data, ipmsg, ctx->r_ip, ctx->r_port);
}
- else
#endif
- ipmsg = " Trying %s:%d...";
- infof(data, ipmsg, ctx->r_ip, ctx->r_port);
#ifdef ENABLE_IPV6
is_tcp = (ctx->addr.family == AF_INET
@@ -1014,6 +1041,7 @@ static CURLcode cf_socket_open(struct Curl_cfilter *cf,
}
}
+#ifndef CURL_DISABLE_BINDLOCAL
/* possibly bind the local end to an IP, interface or port */
if(ctx->addr.family == AF_INET
#ifdef ENABLE_IPV6
@@ -1031,6 +1059,7 @@ static CURLcode cf_socket_open(struct Curl_cfilter *cf,
goto out;
}
}
+#endif
/* set socket non-blocking */
(void)curlx_nonblock(ctx->sock, TRUE);
@@ -1047,8 +1076,8 @@ out:
ctx->connected_at = Curl_now();
cf->connected = TRUE;
}
- DEBUGF(LOG_CF(data, cf, "cf_socket_open() -> %d, fd=%" CURL_FORMAT_SOCKET_T,
- result, ctx->sock));
+ CURL_TRC_CF(data, cf, "cf_socket_open() -> %d, fd=%" CURL_FORMAT_SOCKET_T,
+ result, ctx->sock);
return result;
}
@@ -1155,7 +1184,7 @@ static CURLcode cf_tcp_connect(struct Curl_cfilter *cf,
rc = SOCKET_WRITABLE(ctx->sock, 0);
if(rc == 0) { /* no connection yet */
- DEBUGF(LOG_CF(data, cf, "not connected yet"));
+ CURL_TRC_CF(data, cf, "not connected yet");
return CURLE_OK;
}
else if(rc == CURL_CSELECT_OUT || cf->conn->bits.tcp_fastopen) {
@@ -1165,7 +1194,7 @@ static CURLcode cf_tcp_connect(struct Curl_cfilter *cf,
set_local_ip(cf, data);
*done = TRUE;
cf->connected = TRUE;
- DEBUGF(LOG_CF(data, cf, "connected"));
+ CURL_TRC_CF(data, cf, "connected");
return CURLE_OK;
}
}
@@ -1245,11 +1274,34 @@ static ssize_t cf_socket_send(struct Curl_cfilter *cf, struct Curl_easy *data,
struct cf_socket_ctx *ctx = cf->ctx;
curl_socket_t fdsave;
ssize_t nwritten;
+ size_t orig_len = len;
*err = CURLE_OK;
fdsave = cf->conn->sock[cf->sockindex];
cf->conn->sock[cf->sockindex] = ctx->sock;
+#ifdef DEBUGBUILD
+ /* simulate network blocking/partial writes */
+ if(ctx->wblock_percent > 0) {
+ unsigned char c;
+ Curl_rand(data, &c, 1);
+ if(c >= ((100-ctx->wblock_percent)*256/100)) {
+ CURL_TRC_CF(data, cf, "send(len=%zu) SIMULATE EWOULDBLOCK", orig_len);
+ *err = CURLE_AGAIN;
+ nwritten = -1;
+ cf->conn->sock[cf->sockindex] = fdsave;
+ return nwritten;
+ }
+ }
+ if(cf->cft != &Curl_cft_udp && ctx->wpartial_percent > 0 && len > 8) {
+ len = len * ctx->wpartial_percent / 100;
+ if(!len)
+ len = 1;
+ CURL_TRC_CF(data, cf, "send(len=%zu) SIMULATE partial write of %zu bytes",
+ orig_len, len);
+ }
+#endif
+
#if defined(MSG_FASTOPEN) && !defined(TCP_FASTOPEN_CONNECT) /* Linux */
if(cf->conn->bits.tcp_fastopen) {
nwritten = sendto(ctx->sock, buf, len, MSG_FASTOPEN,
@@ -1288,8 +1340,8 @@ static ssize_t cf_socket_send(struct Curl_cfilter *cf, struct Curl_easy *data,
}
}
- DEBUGF(LOG_CF(data, cf, "send(len=%zu) -> %d, err=%d",
- len, (int)nwritten, *err));
+ CURL_TRC_CF(data, cf, "send(len=%zu) -> %d, err=%d",
+ orig_len, (int)nwritten, *err);
cf->conn->sock[cf->sockindex] = fdsave;
return nwritten;
}
@@ -1307,7 +1359,7 @@ static ssize_t cf_socket_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
cf->conn->sock[cf->sockindex] = ctx->sock;
if(ctx->buffer_recv && !Curl_bufq_is_empty(&ctx->recvbuf)) {
- DEBUGF(LOG_CF(data, cf, "recv from buffer"));
+ CURL_TRC_CF(data, cf, "recv from buffer");
nread = Curl_bufq_read(&ctx->recvbuf, (unsigned char *)buf, len, err);
}
else {
@@ -1324,7 +1376,7 @@ static ssize_t cf_socket_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
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. */
- DEBUGF(LOG_CF(data, cf, "partial read: empty buffer first"));
+ 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) {
@@ -1337,7 +1389,7 @@ static ssize_t cf_socket_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
nread = 0;
}
else {
- DEBUGF(LOG_CF(data, cf, "buffered %zd additional bytes", nwritten));
+ CURL_TRC_CF(data, cf, "buffered %zd additional bytes", nwritten);
nread = Curl_bufq_read(&ctx->recvbuf, (unsigned char *)buf, len, err);
}
}
@@ -1347,8 +1399,8 @@ static ssize_t cf_socket_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
}
out:
- DEBUGF(LOG_CF(data, cf, "recv(len=%zu) -> %d, err=%d", len, (int)nread,
- *err));
+ CURL_TRC_CF(data, cf, "recv(len=%zu) -> %d, err=%d", len, (int)nread,
+ *err);
if(nread > 0 && !ctx->got_first_byte) {
ctx->first_byte_at = Curl_now();
ctx->got_first_byte = TRUE;
@@ -1455,19 +1507,19 @@ static bool cf_socket_conn_is_alive(struct Curl_cfilter *cf,
r = Curl_poll(pfd, 1, 0);
if(r < 0) {
- DEBUGF(LOG_CF(data, cf, "is_alive: poll error, assume dead"));
+ CURL_TRC_CF(data, cf, "is_alive: poll error, assume dead");
return FALSE;
}
else if(r == 0) {
- DEBUGF(LOG_CF(data, cf, "is_alive: poll timeout, assume alive"));
+ CURL_TRC_CF(data, cf, "is_alive: poll timeout, assume alive");
return TRUE;
}
else if(pfd[0].revents & (POLLERR|POLLHUP|POLLPRI|POLLNVAL)) {
- DEBUGF(LOG_CF(data, cf, "is_alive: err/hup/etc events, assume dead"));
+ CURL_TRC_CF(data, cf, "is_alive: err/hup/etc events, assume dead");
return FALSE;
}
- DEBUGF(LOG_CF(data, cf, "is_alive: valid events, looks alive"));
+ CURL_TRC_CF(data, cf, "is_alive: valid events, looks alive");
*input_pending = TRUE;
return TRUE;
}
@@ -1520,7 +1572,7 @@ static CURLcode cf_socket_query(struct Curl_cfilter *cf,
struct Curl_cftype Curl_cft_tcp = {
"TCP",
CF_TYPE_IP_CONNECT,
- CURL_LOG_DEFAULT,
+ CURL_LOG_LVL_NONE,
cf_socket_destroy,
cf_tcp_connect,
cf_socket_close,
@@ -1581,10 +1633,10 @@ static CURLcode cf_udp_setup_quic(struct Curl_cfilter *cf,
return socket_connect_result(data, ctx->r_ip, SOCKERRNO);
}
set_local_ip(cf, data);
- DEBUGF(LOG_CF(data, cf, "%s socket %" CURL_FORMAT_SOCKET_T
- " connected: [%s:%d] -> [%s:%d]",
- (ctx->transport == TRNSPRT_QUIC)? "QUIC" : "UDP",
- ctx->sock, ctx->l_ip, ctx->l_port, ctx->r_ip, ctx->r_port));
+ CURL_TRC_CF(data, cf, "%s socket %" CURL_FORMAT_SOCKET_T
+ " connected: [%s:%d] -> [%s:%d]",
+ (ctx->transport == TRNSPRT_QUIC)? "QUIC" : "UDP",
+ ctx->sock, ctx->l_ip, ctx->l_port, ctx->r_ip, ctx->r_port);
(void)curlx_nonblock(ctx->sock, TRUE);
switch(ctx->addr.family) {
@@ -1624,7 +1676,7 @@ static CURLcode cf_udp_connect(struct Curl_cfilter *cf,
if(ctx->sock == CURL_SOCKET_BAD) {
result = cf_socket_open(cf, data);
if(result) {
- DEBUGF(LOG_CF(data, cf, "cf_udp_connect(), open failed -> %d", result));
+ CURL_TRC_CF(data, cf, "cf_udp_connect(), open failed -> %d", result);
goto out;
}
@@ -1632,13 +1684,13 @@ static CURLcode cf_udp_connect(struct Curl_cfilter *cf,
result = cf_udp_setup_quic(cf, data);
if(result)
goto out;
- DEBUGF(LOG_CF(data, cf, "cf_udp_connect(), opened socket=%"
- CURL_FORMAT_SOCKET_T " (%s:%d)",
- ctx->sock, ctx->l_ip, ctx->l_port));
+ CURL_TRC_CF(data, cf, "cf_udp_connect(), opened socket=%"
+ CURL_FORMAT_SOCKET_T " (%s:%d)",
+ ctx->sock, ctx->l_ip, ctx->l_port);
}
else {
- DEBUGF(LOG_CF(data, cf, "cf_udp_connect(), opened socket=%"
- CURL_FORMAT_SOCKET_T " (unconnected)", ctx->sock));
+ CURL_TRC_CF(data, cf, "cf_udp_connect(), opened socket=%"
+ CURL_FORMAT_SOCKET_T " (unconnected)", ctx->sock);
}
*done = TRUE;
cf->connected = TRUE;
@@ -1650,7 +1702,7 @@ out:
struct Curl_cftype Curl_cft_udp = {
"UDP",
CF_TYPE_IP_CONNECT,
- CURL_LOG_DEFAULT,
+ CURL_LOG_LVL_NONE,
cf_socket_destroy,
cf_udp_connect,
cf_socket_close,
@@ -1701,7 +1753,7 @@ out:
struct Curl_cftype Curl_cft_unix = {
"UNIX",
CF_TYPE_IP_CONNECT,
- CURL_LOG_DEFAULT,
+ CURL_LOG_LVL_NONE,
cf_socket_destroy,
cf_tcp_connect,
cf_socket_close,
@@ -1765,7 +1817,7 @@ static CURLcode cf_tcp_accept_connect(struct Curl_cfilter *cf,
struct Curl_cftype Curl_cft_tcp_accept = {
"TCP-ACCEPT",
CF_TYPE_IP_CONNECT,
- CURL_LOG_DEFAULT,
+ CURL_LOG_LVL_NONE,
cf_socket_destroy,
cf_tcp_accept_connect,
cf_socket_close,
@@ -1810,8 +1862,8 @@ CURLcode Curl_conn_tcp_listen_set(struct Curl_easy *data,
ctx->active = TRUE;
ctx->connected_at = Curl_now();
cf->connected = TRUE;
- DEBUGF(LOG_CF(data, cf, "Curl_conn_tcp_listen_set(%"
- CURL_FORMAT_SOCKET_T ")", ctx->sock));
+ CURL_TRC_CF(data, cf, "Curl_conn_tcp_listen_set(%"
+ CURL_FORMAT_SOCKET_T ")", ctx->sock);
out:
if(result) {
@@ -1875,9 +1927,9 @@ CURLcode Curl_conn_tcp_accepted_set(struct Curl_easy *data,
ctx->accepted = TRUE;
ctx->connected_at = Curl_now();
cf->connected = TRUE;
- DEBUGF(LOG_CF(data, cf, "accepted_set(sock=%" CURL_FORMAT_SOCKET_T
- ", remote=%s port=%d)",
- ctx->sock, ctx->r_ip, ctx->r_port));
+ CURL_TRC_CF(data, cf, "accepted_set(sock=%" CURL_FORMAT_SOCKET_T
+ ", remote=%s port=%d)",
+ ctx->sock, ctx->r_ip, ctx->r_port);
return CURLE_OK;
}
@@ -1922,4 +1974,3 @@ CURLcode Curl_cf_socket_peek(struct Curl_cfilter *cf,
}
return CURLE_FAILED_INIT;
}
-