diff options
Diffstat (limited to 'lib/cf-socket.c')
-rw-r--r-- | lib/cf-socket.c | 201 |
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; } - |