diff options
author | Stefan Eissing <stefan@eissing.org> | 2023-01-30 16:03:00 +0100 |
---|---|---|
committer | Daniel Stenberg <daniel@haxx.se> | 2023-01-31 11:23:59 +0100 |
commit | 5651a36d1ae46db61a31771a8d4d6dcf2a510856 (patch) | |
tree | e4a92cb9ca01ee60bce0d5af481c37a918d92378 /lib/socks.c | |
parent | a3bcfab4b577dde2ddac0f25bbe872037dd81d23 (diff) | |
download | curl-5651a36d1ae46db61a31771a8d4d6dcf2a510856.tar.gz |
cf-socket: improvements in socket I/O handling
- Curl_write_plain/Curl_read_plain have been eliminated. Last code use
now uses Curl_conn_send/recv so that requests use conn->send/revc
callbacks which defaults to cfilters use.
- Curl_recv_plain/Curl_send_plain have been internalized in cf-socket.c.
- USE_RECV_BEFORE_SEND_WORKAROUND (active on Windows) has been moved
into cf-socket.c. The pre_recv buffer is held at the socket filter
context. `postponed_data` structures have been removed from
`connectdata`.
- the hanger in HTTP/2 request handling was a result of read buffering
on all sends and the multi handling is not prepared for this. The
following happens:
- multi preforms on a HTTP/2 easy handle
- h2 reads and processes data
- this leads to a send of h2 data
- which receives and buffers before the send
- h2 returns
- multi selects on the socket, but no data arrives (its in the buffer already)
the workaround now receives data in a loop as long as there is something in
the buffer. The real fix would be for multi to change, so that `data_pending`
is evaluated before deciding to wait on the socket.
io_buffer, optional, in cf-socket.c, http/2 sets state.drain if lower
filter have pending data.
This io_buffer is only available/used when the
-DUSE_RECV_BEFORE_SEND_WORKAROUND is active, e.g. on Windows
configurations. It also maintains the original checks on protocol
handler being HTTP and conn->send/recv not being replaced.
The HTTP/2 (nghttp2) cfilter now sets data->state.drain when it finds
out that the "lower" filter chain has still pending data at the end of
its IO operation. This prevents the processing from becoming stalled.
Closes #10280
Diffstat (limited to 'lib/socks.c')
-rw-r--r-- | lib/socks.c | 281 |
1 files changed, 140 insertions, 141 deletions
diff --git a/lib/socks.c b/lib/socks.c index b8b4f8cb8..ea1e2c17a 100644 --- a/lib/socks.c +++ b/lib/socks.c @@ -89,8 +89,8 @@ struct socks_state { * * This is STUPID BLOCKING behavior. Only used by the SOCKS GSSAPI functions. */ -int Curl_blockread_all(struct Curl_easy *data, /* transfer */ - curl_socket_t sockfd, /* read from this socket */ +int Curl_blockread_all(struct Curl_cfilter *cf, + struct Curl_easy *data, /* transfer */ char *buf, /* store read data here */ ssize_t buffersize, /* max amount to read */ ssize_t *n) /* amount bytes read */ @@ -98,6 +98,8 @@ int Curl_blockread_all(struct Curl_easy *data, /* transfer */ ssize_t nread = 0; ssize_t allread = 0; int result; + CURLcode err = CURLE_OK; + *n = 0; for(;;) { timediff_t timeout_ms = Curl_timeleft(data, NULL, TRUE); @@ -108,15 +110,19 @@ int Curl_blockread_all(struct Curl_easy *data, /* transfer */ } if(!timeout_ms) timeout_ms = TIMEDIFF_T_MAX; - if(SOCKET_READABLE(sockfd, timeout_ms) <= 0) { + if(SOCKET_READABLE(cf->conn->sock[cf->sockindex], timeout_ms) <= 0) { result = ~CURLE_OK; break; } - result = Curl_read_plain(data, sockfd, buf, buffersize, &nread); - if(CURLE_AGAIN == result) - continue; - if(result) - break; + nread = Curl_conn_cf_recv(cf->next, data, buf, buffersize, &err); + if(nread <= 0) { + result = err; + if(CURLE_AGAIN == err) + continue; + if(err) { + break; + } + } if(buffersize == nread) { allread += nread; @@ -192,6 +198,68 @@ static void socksstate(struct socks_state *sx, struct Curl_easy *data, #endif } +static CURLproxycode socks_state_send(struct Curl_cfilter *cf, + struct socks_state *sx, + struct Curl_easy *data, + CURLproxycode failcode, + const char *description) +{ + ssize_t nwritten; + CURLcode result; + + nwritten = Curl_conn_cf_send(cf->next, data, (char *)sx->outp, + sx->outstanding, &result); + if(nwritten <= 0) { + if(CURLE_AGAIN == result) { + return CURLPX_OK; + } + else if(CURLE_OK == result) { + /* connection closed */ + failf(data, "connection to proxy closed"); + return CURLPX_CLOSED; + } + failf(data, "Failed to send %s: %s", description, + curl_easy_strerror(result)); + return failcode; + } + DEBUGASSERT(sx->outstanding >= nwritten); + /* not done, remain in state */ + sx->outstanding -= nwritten; + sx->outp += nwritten; + return CURLPX_OK; +} + +static CURLproxycode socks_state_recv(struct Curl_cfilter *cf, + struct socks_state *sx, + struct Curl_easy *data, + CURLproxycode failcode, + const char *description) +{ + ssize_t nread; + CURLcode result; + + nread = Curl_conn_cf_recv(cf->next, data, (char *)sx->outp, + sx->outstanding, &result); + if(nread <= 0) { + if(CURLE_AGAIN == result) { + return CURLPX_OK; + } + else if(CURLE_OK == result) { + /* connection closed */ + failf(data, "connection to proxy closed"); + return CURLPX_CLOSED; + } + failf(data, "SOCKS4: Failed receiving %s: %s", description, + curl_easy_strerror(result)); + return failcode; + } + /* remain in reading state */ + DEBUGASSERT(sx->outstanding >= nread); + sx->outstanding -= nread; + sx->outp += nread; + return CURLPX_OK; +} + /* * This function logs in to a SOCKS4 proxy and sends the specifics to the final * destination server. @@ -212,10 +280,8 @@ static CURLproxycode do_SOCKS4(struct Curl_cfilter *cf, (conn->socks_proxy.proxytype == CURLPROXY_SOCKS4A) ? TRUE : FALSE; unsigned char *socksreq = (unsigned char *)data->state.buffer; CURLcode result; - curl_socket_t sockfd = conn->sock[cf->sockindex]; + CURLproxycode presult; struct Curl_dns_entry *dns = NULL; - ssize_t actualread; - ssize_t written; /* make sure that the buffer is at least 600 bytes */ DEBUGASSERT(READBUFFER_MIN >= 600); @@ -375,19 +441,14 @@ static CURLproxycode do_SOCKS4(struct Curl_cfilter *cf, /* FALLTHROUGH */ case CONNECT_REQ_SENDING: /* Send request */ - result = Curl_write_plain(data, sockfd, (char *)sx->outp, - sx->outstanding, &written); - if(result && (CURLE_AGAIN != result)) { - failf(data, "Failed to send SOCKS4 connect request."); - return CURLPX_SEND_CONNECT; - } - if(written != sx->outstanding) { - /* not done, remain in state */ - sx->outstanding -= written; - sx->outp += written; + presult = socks_state_send(cf, sx, data, CURLPX_SEND_CONNECT, + "SOCKS4 connect request"); + if(CURLPX_OK != presult) + return presult; + else if(sx->outstanding) { + /* remain in sending state */ return CURLPX_OK; } - /* done sending! */ sx->outstanding = 8; /* receive data size */ sx->outp = socksreq; @@ -396,22 +457,12 @@ static CURLproxycode do_SOCKS4(struct Curl_cfilter *cf, /* FALLTHROUGH */ case CONNECT_SOCKS_READ: /* Receive response */ - result = Curl_read_plain(data, sockfd, (char *)sx->outp, - sx->outstanding, &actualread); - if(result && (CURLE_AGAIN != result)) { - failf(data, "SOCKS4: Failed receiving connect request ack: %s", - curl_easy_strerror(result)); - return CURLPX_RECV_CONNECT; - } - else if(!result && !actualread) { - /* connection closed */ - failf(data, "connection to proxy closed"); - return CURLPX_CLOSED; - } - else if(actualread != sx->outstanding) { + presult = socks_state_recv(cf, sx, data, CURLPX_RECV_CONNECT, + "connect request ack"); + if(CURLPX_OK != presult) + return presult; + else if(sx->outstanding) { /* remain in reading state */ - sx->outstanding -= actualread; - sx->outp += actualread; return CURLPX_OK; } sxstate(sx, data, CONNECT_DONE); @@ -518,10 +569,8 @@ static CURLproxycode do_SOCKS5(struct Curl_cfilter *cf, unsigned char *socksreq = (unsigned char *)data->state.buffer; char dest[256] = "unknown"; /* printable hostname:port */ int idx; - ssize_t actualread; - ssize_t written; CURLcode result; - curl_socket_t sockfd = conn->sock[cf->sockindex]; + CURLproxycode presult; bool socks5_resolve_local = (conn->socks_proxy.proxytype == CURLPROXY_SOCKS5) ? TRUE : FALSE; const size_t hostname_len = strlen(sx->hostname); @@ -567,30 +616,25 @@ static CURLproxycode do_SOCKS5(struct Curl_cfilter *cf, /* write the number of authentication methods */ socksreq[1] = (unsigned char) (idx - 2); - result = Curl_write_plain(data, sockfd, socksreq, idx, &written); - if(result && (CURLE_AGAIN != result)) { - failf(data, "Unable to send initial SOCKS5 request."); - return CURLPX_SEND_CONNECT; - } - if(written != idx) { - sxstate(sx, data, CONNECT_SOCKS_SEND); - sx->outstanding = idx - written; - sx->outp = &socksreq[written]; + sx->outp = socksreq; + sx->outstanding = idx; + presult = socks_state_send(cf, sx, data, CURLPX_SEND_CONNECT, + "initial SOCKS5 request"); + if(CURLPX_OK != presult) + return presult; + else if(sx->outstanding) { + /* remain in sending state */ return CURLPX_OK; } sxstate(sx, data, CONNECT_SOCKS_READ); goto CONNECT_SOCKS_READ_INIT; case CONNECT_SOCKS_SEND: - result = Curl_write_plain(data, sockfd, (char *)sx->outp, - sx->outstanding, &written); - if(result && (CURLE_AGAIN != result)) { - failf(data, "Unable to send initial SOCKS5 request."); - return CURLPX_SEND_CONNECT; - } - if(written != sx->outstanding) { - /* not done, remain in state */ - sx->outstanding -= written; - sx->outp += written; + presult = socks_state_send(cf, sx, data, CURLPX_SEND_CONNECT, + "initial SOCKS5 request"); + if(CURLPX_OK != presult) + return presult; + else if(sx->outstanding) { + /* remain in sending state */ return CURLPX_OK; } /* FALLTHROUGH */ @@ -600,21 +644,12 @@ static CURLproxycode do_SOCKS5(struct Curl_cfilter *cf, sx->outp = socksreq; /* store it here */ /* FALLTHROUGH */ case CONNECT_SOCKS_READ: - result = Curl_read_plain(data, sockfd, (char *)sx->outp, - sx->outstanding, &actualread); - if(result && (CURLE_AGAIN != result)) { - failf(data, "Unable to receive initial SOCKS5 response."); - return CURLPX_RECV_CONNECT; - } - else if(!result && !actualread) { - /* connection closed */ - failf(data, "Connection to proxy closed"); - return CURLPX_CLOSED; - } - else if(actualread != sx->outstanding) { + presult = socks_state_recv(cf, sx, data, CURLPX_RECV_CONNECT, + "initial SOCKS5 response"); + if(CURLPX_OK != presult) + return presult; + else if(sx->outstanding) { /* remain in reading state */ - sx->outstanding -= actualread; - sx->outp += actualread; return CURLPX_OK; } else if(socksreq[0] != 5) { @@ -634,7 +669,7 @@ static CURLproxycode do_SOCKS5(struct Curl_cfilter *cf, #if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI) else if(allow_gssapi && (socksreq[1] == 1)) { sxstate(sx, data, CONNECT_GSSAPI_INIT); - result = Curl_SOCKS5_gssapi_negotiate(cf->sockindex, data); + result = Curl_SOCKS5_gssapi_negotiate(cf, data); if(result) { failf(data, "Unable to negotiate SOCKS5 GSS-API context."); return CURLPX_GSSAPI; @@ -713,16 +748,12 @@ static CURLproxycode do_SOCKS5(struct Curl_cfilter *cf, } /* FALLTHROUGH */ case CONNECT_AUTH_SEND: - result = Curl_write_plain(data, sockfd, sx->outp, - sx->outstanding, &written); - if(result && (CURLE_AGAIN != result)) { - failf(data, "Failed to send SOCKS5 sub-negotiation request."); - return CURLPX_SEND_AUTH; - } - if(sx->outstanding != written) { - /* remain in state */ - sx->outstanding -= written; - sx->outp += written; + presult = socks_state_send(cf, sx, data, CURLPX_SEND_AUTH, + "SOCKS5 sub-negotiation request"); + if(CURLPX_OK != presult) + return presult; + else if(sx->outstanding) { + /* remain in sending state */ return CURLPX_OK; } sx->outp = socksreq; @@ -730,21 +761,12 @@ static CURLproxycode do_SOCKS5(struct Curl_cfilter *cf, sxstate(sx, data, CONNECT_AUTH_READ); /* FALLTHROUGH */ case CONNECT_AUTH_READ: - result = Curl_read_plain(data, sockfd, (char *)sx->outp, - sx->outstanding, &actualread); - if(result && (CURLE_AGAIN != result)) { - failf(data, "Unable to receive SOCKS5 sub-negotiation response."); - return CURLPX_RECV_AUTH; - } - else if(!result && !actualread) { - /* connection closed */ - failf(data, "connection to proxy closed"); - return CURLPX_CLOSED; - } - else if(actualread != sx->outstanding) { - /* remain in state */ - sx->outstanding -= actualread; - sx->outp += actualread; + presult = socks_state_recv(cf, sx, data, CURLPX_RECV_AUTH, + "SOCKS5 sub-negotiation response"); + if(CURLPX_OK != presult) + return presult; + else if(sx->outstanding) { + /* remain in reading state */ return CURLPX_OK; } /* ignore the first (VER) byte */ @@ -909,16 +931,12 @@ static CURLproxycode do_SOCKS5(struct Curl_cfilter *cf, sxstate(sx, data, CONNECT_REQ_SENDING); /* FALLTHROUGH */ case CONNECT_REQ_SENDING: - result = Curl_write_plain(data, sockfd, (char *)sx->outp, - sx->outstanding, &written); - if(result && (CURLE_AGAIN != result)) { - failf(data, "Failed to send SOCKS5 connect request."); - return CURLPX_SEND_REQUEST; - } - if(sx->outstanding != written) { - /* remain in state */ - sx->outstanding -= written; - sx->outp += written; + presult = socks_state_send(cf, sx, data, CURLPX_SEND_REQUEST, + "SOCKS5 connect request"); + if(CURLPX_OK != presult) + return presult; + else if(sx->outstanding) { + /* remain in send state */ return CURLPX_OK; } #if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI) @@ -932,25 +950,15 @@ static CURLproxycode do_SOCKS5(struct Curl_cfilter *cf, sxstate(sx, data, CONNECT_REQ_READ); /* FALLTHROUGH */ case CONNECT_REQ_READ: - result = Curl_read_plain(data, sockfd, (char *)sx->outp, - sx->outstanding, &actualread); - if(result && (CURLE_AGAIN != result)) { - failf(data, "Failed to receive SOCKS5 connect request ack."); - return CURLPX_RECV_REQACK; - } - else if(!result && !actualread) { - /* connection closed */ - failf(data, "connection to proxy closed"); - return CURLPX_CLOSED; - } - else if(actualread != sx->outstanding) { - /* remain in state */ - sx->outstanding -= actualread; - sx->outp += actualread; + presult = socks_state_recv(cf, sx, data, CURLPX_RECV_REQACK, + "SOCKS5 connect request ack"); + if(CURLPX_OK != presult) + return presult; + else if(sx->outstanding) { + /* remain in reading state */ return CURLPX_OK; } - - if(socksreq[0] != 5) { /* version */ + else if(socksreq[0] != 5) { /* version */ failf(data, "SOCKS5 reply has wrong version, version should be 5."); return CURLPX_BAD_VERSION; @@ -1031,21 +1039,12 @@ static CURLproxycode do_SOCKS5(struct Curl_cfilter *cf, #endif /* FALLTHROUGH */ case CONNECT_REQ_READ_MORE: - result = Curl_read_plain(data, sockfd, (char *)sx->outp, - sx->outstanding, &actualread); - if(result && (CURLE_AGAIN != result)) { - failf(data, "Failed to receive SOCKS5 connect request ack."); - return CURLPX_RECV_ADDRESS; - } - else if(!result && !actualread) { - /* connection closed */ - failf(data, "connection to proxy closed"); - return CURLPX_CLOSED; - } - else if(actualread != sx->outstanding) { - /* remain in state */ - sx->outstanding -= actualread; - sx->outp += actualread; + presult = socks_state_recv(cf, sx, data, CURLPX_RECV_ADDRESS, + "SOCKS5 connect request address"); + if(CURLPX_OK != presult) + return presult; + else if(sx->outstanding) { + /* remain in reading state */ return CURLPX_OK; } sxstate(sx, data, CONNECT_DONE); |