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 | |
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
-rw-r--r-- | lib/cf-socket.c | 192 | ||||
-rw-r--r-- | lib/cfilters.c | 10 | ||||
-rw-r--r-- | lib/cfilters.h | 2 | ||||
-rw-r--r-- | lib/http2.c | 15 | ||||
-rw-r--r-- | lib/krb5.c | 24 | ||||
-rw-r--r-- | lib/sendf.c | 282 | ||||
-rw-r--r-- | lib/sendf.h | 20 | ||||
-rw-r--r-- | lib/socks.c | 281 | ||||
-rw-r--r-- | lib/socks.h | 6 | ||||
-rw-r--r-- | lib/socks_gssapi.c | 44 | ||||
-rw-r--r-- | lib/socks_sspi.c | 31 | ||||
-rw-r--r-- | lib/url.c | 46 | ||||
-rw-r--r-- | lib/urldata.h | 17 | ||||
-rw-r--r-- | lib/vtls/schannel.h | 4 |
14 files changed, 402 insertions, 572 deletions
diff --git a/lib/cf-socket.c b/lib/cf-socket.c index 98565aed4..d8b07d309 100644 --- a/lib/cf-socket.c +++ b/lib/cf-socket.c @@ -742,11 +742,29 @@ CURLcode Curl_socket_connect_result(struct Curl_easy *data, } } +#ifdef USE_RECV_BEFORE_SEND_WORKAROUND +struct io_buffer { + char *bufr; + size_t allc; /* size of the current allocation */ + size_t head; /* bufr index for next read */ + size_t tail; /* bufr index for next write */ +}; + +static void io_buffer_reset(struct io_buffer *iob) +{ + if(iob->bufr) + free(iob->bufr); + memset(iob, 0, sizeof(*iob)); +} +#endif /* USE_RECV_BEFORE_SEND_WORKAROUND */ struct cf_socket_ctx { int transport; struct Curl_sockaddr_ex addr; /* address to connect to */ curl_socket_t sock; /* current attempt socket */ +#ifdef USE_RECV_BEFORE_SEND_WORKAROUND + struct io_buffer recv_buffer; +#endif char r_ip[MAX_IPADR_LEN]; /* remote IP as string */ int r_port; /* remote port number */ char l_ip[MAX_IPADR_LEN]; /* local IP as string */ @@ -783,6 +801,9 @@ static void cf_socket_close(struct Curl_cfilter *cf, struct Curl_easy *data) DEBUGF(LOG_CF(data, cf, "cf_socket_close(%d) local", (int)ctx->sock)); sclose(ctx->sock); } +#ifdef USE_RECV_BEFORE_SEND_WORKAROUND + io_buffer_reset(&ctx->recv_buffer); +#endif ctx->sock = CURL_SOCKET_BAD; ctx->active = FALSE; } @@ -1112,12 +1133,88 @@ static int cf_socket_get_select_socks(struct Curl_cfilter *cf, return rc; } +#ifdef USE_RECV_BEFORE_SEND_WORKAROUND + +static CURLcode pre_receive_plain(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + struct cf_socket_ctx *ctx = cf->ctx; + struct io_buffer * const iob = &ctx->recv_buffer; + + /* WinSock will destroy unread received data if send() is + failed. + To avoid lossage of received data, recv() must be + performed before every send() if any incoming data is + available. However, skip this, if buffer is already full. */ + if((cf->conn->handler->protocol&PROTO_FAMILY_HTTP) != 0 && + cf->conn->recv[cf->sockindex] == Curl_conn_recv && + (!iob->bufr || (iob->allc > iob->tail))) { + const int readymask = Curl_socket_check(ctx->sock, CURL_SOCKET_BAD, + CURL_SOCKET_BAD, 0); + if(readymask != -1 && (readymask & CURL_CSELECT_IN) != 0) { + size_t bytestorecv = iob->allc - iob->tail; + ssize_t nread; + /* Have some incoming data */ + if(!iob->bufr) { + /* Use buffer double default size for intermediate buffer */ + iob->allc = 2 * data->set.buffer_size; + iob->bufr = malloc(iob->allc); + if(!iob->bufr) + return CURLE_OUT_OF_MEMORY; + iob->tail = 0; + iob->head = 0; + bytestorecv = iob->allc; + } + + nread = sread(ctx->sock, iob->bufr + iob->tail, bytestorecv); + if(nread > 0) + iob->tail += (size_t)nread; + } + } + return CURLE_OK; +} + +static ssize_t get_pre_recved(struct Curl_cfilter *cf, char *buf, size_t len) +{ + struct cf_socket_ctx *ctx = cf->ctx; + struct io_buffer * const iob = &ctx->recv_buffer; + size_t copysize; + if(!iob->bufr) + return 0; + + DEBUGASSERT(iob->allc > 0); + DEBUGASSERT(iob->tail <= iob->allc); + DEBUGASSERT(iob->head <= iob->tail); + /* Check and process data that already received and storied in internal + intermediate buffer */ + if(iob->tail > iob->head) { + copysize = CURLMIN(len, iob->tail - iob->head); + memcpy(buf, iob->bufr + iob->head, copysize); + iob->head += copysize; + } + else + copysize = 0; /* buffer was allocated, but nothing was received */ + + /* Free intermediate buffer if it has no unprocessed data */ + if(iob->head == iob->tail) + io_buffer_reset(iob); + + return (ssize_t)copysize; +} +#endif /* USE_RECV_BEFORE_SEND_WORKAROUND */ + static bool cf_socket_data_pending(struct Curl_cfilter *cf, const struct Curl_easy *data) { struct cf_socket_ctx *ctx = cf->ctx; int readable; +#ifdef USE_RECV_BEFORE_SEND_WORKAROUND + if(ctx->recv_buffer.bufr && ctx->recv_buffer.allc && + ctx->recv_buffer.tail > ctx->recv_buffer.head) + return TRUE; +#endif + (void)data; readable = SOCKET_READABLE(ctx->sock, 0); return (readable > 0 && (readable & CURL_CSELECT_IN)); @@ -1126,10 +1223,59 @@ static bool cf_socket_data_pending(struct Curl_cfilter *cf, static ssize_t cf_socket_send(struct Curl_cfilter *cf, struct Curl_easy *data, const void *buf, size_t len, CURLcode *err) { + struct cf_socket_ctx *ctx = cf->ctx; ssize_t nwritten; - DEBUGASSERT(data->conn == cf->conn); - nwritten = Curl_send_plain(data, cf->sockindex, buf, len, err); + *err = CURLE_OK; +#ifdef USE_RECV_BEFORE_SEND_WORKAROUND + /* WinSock will destroy unread received data if send() is + failed. + To avoid lossage of received data, recv() must be + performed before every send() if any incoming data is + available. */ + if(pre_receive_plain(cf, data)) { + *err = CURLE_OUT_OF_MEMORY; + return -1; + } +#endif + +#if defined(MSG_FASTOPEN) && !defined(TCP_FASTOPEN_CONNECT) /* Linux */ + if(cf->conn->bits.tcp_fastopen) { + bytes_written = sendto(ctx->sock, buf, len, MSG_FASTOPEN, + &cf->conn->remote_addr->sa_addr, + cf->conn->remote_addr->addrlen); + cf->conn->bits.tcp_fastopen = FALSE; + } + else +#endif + nwritten = swrite(ctx->sock, buf, len); + + if(-1 == nwritten) { + 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) || + (EINPROGRESS == sockerr) +#endif + ) { + /* this is just a case of EWOULDBLOCK */ + *err = CURLE_AGAIN; + } + else { + char buffer[STRERROR_LEN]; + failf(data, "Send failure: %s", + Curl_strerror(sockerr, buffer, sizeof(buffer))); + data->state.os_errno = sockerr; + *err = CURLE_SEND_ERROR; + } + } DEBUGF(LOG_CF(data, cf, "send(len=%zu) -> %d, err=%d", len, (int)nwritten, *err)); return nwritten; @@ -1138,10 +1284,48 @@ static ssize_t cf_socket_send(struct Curl_cfilter *cf, struct Curl_easy *data, static ssize_t cf_socket_recv(struct Curl_cfilter *cf, struct Curl_easy *data, char *buf, size_t len, CURLcode *err) { + struct cf_socket_ctx *ctx = cf->ctx; ssize_t nread; - DEBUGASSERT(data->conn == cf->conn); - nread = Curl_recv_plain(data, cf->sockindex, buf, len, err); + *err = CURLE_OK; + +#ifdef USE_RECV_BEFORE_SEND_WORKAROUND + /* Check and return data that already received and storied in internal + intermediate buffer */ + nread = get_pre_recved(cf, buf, len); + if(nread > 0) { + *err = CURLE_OK; + return nread; + } +#endif + + 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 { + char buffer[STRERROR_LEN]; + failf(data, "Recv failure: %s", + Curl_strerror(sockerr, buffer, sizeof(buffer))); + data->state.os_errno = sockerr; + *err = CURLE_RECV_ERROR; + } + } DEBUGF(LOG_CF(data, cf, "recv(len=%zu) -> %d, err=%d", len, (int)nread, *err)); return nread; diff --git a/lib/cfilters.c b/lib/cfilters.c index 9582a951a..97ddbb9b6 100644 --- a/lib/cfilters.c +++ b/lib/cfilters.c @@ -323,6 +323,14 @@ int Curl_conn_cf_get_select_socks(struct Curl_cfilter *cf, return 0; } +bool Curl_conn_cf_data_pending(struct Curl_cfilter *cf, + const struct Curl_easy *data) +{ + if(cf) + return cf->cft->has_data_pending(cf, data); + return FALSE; +} + ssize_t Curl_conn_cf_send(struct Curl_cfilter *cf, struct Curl_easy *data, const void *buf, size_t len, CURLcode *err) { @@ -426,8 +434,6 @@ bool Curl_conn_data_pending(struct Curl_easy *data, int sockindex) (void)data; DEBUGASSERT(data); DEBUGASSERT(data->conn); - if(Curl_recv_has_postponed_data(data->conn, sockindex)) - return TRUE; cf = data->conn->cfilter[sockindex]; while(cf && !cf->connected) { diff --git a/lib/cfilters.h b/lib/cfilters.h index 149d1ee0e..dd6e0a422 100644 --- a/lib/cfilters.h +++ b/lib/cfilters.h @@ -268,6 +268,8 @@ void Curl_conn_cf_close(struct Curl_cfilter *cf, struct Curl_easy *data); int Curl_conn_cf_get_select_socks(struct Curl_cfilter *cf, struct Curl_easy *data, curl_socket_t *socks); +bool Curl_conn_cf_data_pending(struct Curl_cfilter *cf, + const struct Curl_easy *data); ssize_t Curl_conn_cf_send(struct Curl_cfilter *cf, struct Curl_easy *data, const void *buf, size_t len, CURLcode *err); ssize_t Curl_conn_cf_recv(struct Curl_cfilter *cf, struct Curl_easy *data, diff --git a/lib/http2.c b/lib/http2.c index a1628ef44..cf19bcd19 100644 --- a/lib/http2.c +++ b/lib/http2.c @@ -1786,8 +1786,7 @@ static ssize_t h2_cf_recv(struct Curl_cfilter *cf, struct Curl_easy *data, if(ctx->inbuflen == 0) { /* Receive data from the "lower" filters */ nread = Curl_conn_cf_recv(cf->next, data, ctx->inbuf, H2_BUFSIZE, err); - - if(nread == -1) { + if(nread < 0) { if(*err != CURLE_AGAIN) failf(data, "Failed receiving HTTP2 data"); else if(stream->closed) @@ -1796,8 +1795,7 @@ static ssize_t h2_cf_recv(struct Curl_cfilter *cf, struct Curl_easy *data, return -1; } - - if(nread == 0) { + else if(nread == 0) { if(!stream->closed) { /* This will happen when the server or proxy server is SIGKILLed during data transfer. We should emit an error since our data @@ -1814,21 +1812,22 @@ static ssize_t h2_cf_recv(struct Curl_cfilter *cf, struct Curl_easy *data, return 0; } - H2BUGF(infof(data, "nread=%zd", nread)); - + H2BUGF(infof(data, "http2_recv: recvd %zd bytes", nread)); ctx->inbuflen = nread; - DEBUGASSERT(ctx->nread_inbuf == 0); } else { nread = ctx->inbuflen - ctx->nread_inbuf; - (void)nread; /* silence warning, used in debug */ H2BUGF(infof(data, "Use data left in connection buffer, nread=%zd", nread)); } if(h2_process_pending_input(cf, data, err)) return -1; + if(Curl_conn_cf_data_pending(cf->next, data)) { + H2BUGF(infof(data, "conn has pending data, set drain")); + drain_this(cf, data); + } } if(stream->memlen) { ssize_t retlen = stream->memlen; diff --git a/lib/krb5.c b/lib/krb5.c index 82ab844b8..d3edf0121 100644 --- a/lib/krb5.c +++ b/lib/krb5.c @@ -46,6 +46,7 @@ #endif #include "urldata.h" +#include "cfilters.h" #include "cf-socket.h" #include "curl_base64.h" #include "ftp.h" @@ -455,15 +456,15 @@ static int ftp_send_command(struct Curl_easy *data, const char *message, ...) /* Read |len| from the socket |fd| and store it in |to|. Return a CURLcode saying whether an error occurred or CURLE_OK if |len| was read. */ static CURLcode -socket_read(struct Curl_easy *data, curl_socket_t fd, void *to, size_t len) +socket_read(struct Curl_easy *data, int sockindex, void *to, size_t len) { char *to_p = to; CURLcode result; ssize_t nread = 0; while(len > 0) { - result = Curl_read_plain(data, fd, to_p, len, &nread); - if(!result) { + nread = Curl_conn_recv(data, sockindex, to_p, len, &result); + if(nread > 0) { len -= nread; to_p += nread; } @@ -481,7 +482,7 @@ socket_read(struct Curl_easy *data, curl_socket_t fd, void *to, size_t len) CURLcode saying whether an error occurred or CURLE_OK if |len| was written. */ static CURLcode -socket_write(struct Curl_easy *data, curl_socket_t fd, const void *to, +socket_write(struct Curl_easy *data, int sockindex, const void *to, size_t len) { const char *to_p = to; @@ -489,8 +490,8 @@ socket_write(struct Curl_easy *data, curl_socket_t fd, const void *to, ssize_t written; while(len > 0) { - result = Curl_write_plain(data, fd, to_p, len, &written); - if(!result) { + written = Curl_conn_send(data, sockindex, to_p, len, &result); + if(written > 0) { len -= written; to_p += written; } @@ -503,7 +504,7 @@ socket_write(struct Curl_easy *data, curl_socket_t fd, const void *to, return CURLE_OK; } -static CURLcode read_data(struct Curl_easy *data, curl_socket_t fd, +static CURLcode read_data(struct Curl_easy *data, int sockindex, struct krb5buffer *buf) { struct connectdata *conn = data->conn; @@ -511,7 +512,7 @@ static CURLcode read_data(struct Curl_easy *data, curl_socket_t fd, CURLcode result; int nread; - result = socket_read(data, fd, &len, sizeof(len)); + result = socket_read(data, sockindex, &len, sizeof(len)); if(result) return result; @@ -526,7 +527,7 @@ static CURLcode read_data(struct Curl_easy *data, curl_socket_t fd, if(!len || !buf->data) return CURLE_OUT_OF_MEMORY; - result = socket_read(data, fd, buf->data, len); + result = socket_read(data, sockindex, buf->data, len); if(result) return result; nread = conn->mech->decode(conn->app_data, buf->data, len, @@ -555,13 +556,12 @@ static ssize_t sec_recv(struct Curl_easy *data, int sockindex, size_t bytes_read; size_t total_read = 0; struct connectdata *conn = data->conn; - curl_socket_t fd = conn->sock[sockindex]; *err = CURLE_OK; /* Handle clear text response. */ if(conn->sec_complete == 0 || conn->data_prot == PROT_CLEAR) - return Curl_recv_plain(data, sockindex, buffer, len, err); + return Curl_conn_recv(data, sockindex, buffer, len, err); if(conn->in_buffer.eof_flag) { conn->in_buffer.eof_flag = 0; @@ -574,7 +574,7 @@ static ssize_t sec_recv(struct Curl_easy *data, int sockindex, buffer += bytes_read; while(len > 0) { - if(read_data(data, fd, &conn->in_buffer)) + if(read_data(data, sockindex, &conn->in_buffer)) return -1; if(conn->in_buffer.size == 0) { if(bytes_read > 0) diff --git a/lib/sendf.c b/lib/sendf.c index 2d976f3c4..2b0827168 100644 --- a/lib/sendf.c +++ b/lib/sendf.c @@ -138,105 +138,6 @@ static size_t convert_lineends(struct Curl_easy *data, } #endif /* CURL_DO_LINEEND_CONV && !CURL_DISABLE_FTP */ -#ifdef USE_RECV_BEFORE_SEND_WORKAROUND -bool Curl_recv_has_postponed_data(struct connectdata *conn, int sockindex) -{ - struct postponed_data * const psnd = &(conn->postponed[sockindex]); - return psnd->buffer && psnd->allocated_size && - psnd->recv_size > psnd->recv_processed; -} - -static CURLcode pre_receive_plain(struct Curl_easy *data, - struct connectdata *conn, int num) -{ - const curl_socket_t sockfd = conn->sock[num]; - struct postponed_data * const psnd = &(conn->postponed[num]); - size_t bytestorecv = psnd->allocated_size - psnd->recv_size; - ssize_t recvedbytes; - - /* WinSock will destroy unread received data if send() is - failed. - To avoid lossage of received data, recv() must be - performed before every send() if any incoming data is - available. However, skip this, if buffer is already full. */ - if((conn->handler->protocol&PROTO_FAMILY_HTTP) != 0 && - conn->recv[num] == Curl_conn_recv && - (!psnd->buffer || bytestorecv)) { - const int readymask = Curl_socket_check(sockfd, CURL_SOCKET_BAD, - CURL_SOCKET_BAD, 0); - if(readymask != -1 && (readymask & CURL_CSELECT_IN) != 0) { - /* Have some incoming data */ - if(!psnd->buffer) { - /* Use buffer double default size for intermediate buffer */ - psnd->allocated_size = 2 * data->set.buffer_size; - psnd->buffer = malloc(psnd->allocated_size); - if(!psnd->buffer) - return CURLE_OUT_OF_MEMORY; - psnd->recv_size = 0; - psnd->recv_processed = 0; -#ifdef DEBUGBUILD - psnd->bindsock = sockfd; /* Used only for DEBUGASSERT */ -#endif /* DEBUGBUILD */ - bytestorecv = psnd->allocated_size; - } - - DEBUGASSERT(psnd->bindsock == sockfd); - recvedbytes = sread(sockfd, psnd->buffer + psnd->recv_size, - bytestorecv); - if(recvedbytes > 0) - psnd->recv_size += recvedbytes; - } - } - return CURLE_OK; -} - -static ssize_t get_pre_recved(struct connectdata *conn, int num, char *buf, - size_t len) -{ - struct postponed_data * const psnd = &(conn->postponed[num]); - size_t copysize; - if(!psnd->buffer) - return 0; - - DEBUGASSERT(psnd->allocated_size > 0); - DEBUGASSERT(psnd->recv_size <= psnd->allocated_size); - DEBUGASSERT(psnd->recv_processed <= psnd->recv_size); - /* Check and process data that already received and storied in internal - intermediate buffer */ - if(psnd->recv_size > psnd->recv_processed) { - DEBUGASSERT(psnd->bindsock == conn->sock[num]); - copysize = CURLMIN(len, psnd->recv_size - psnd->recv_processed); - memcpy(buf, psnd->buffer + psnd->recv_processed, copysize); - psnd->recv_processed += copysize; - } - else - copysize = 0; /* buffer was allocated, but nothing was received */ - - /* Free intermediate buffer if it has no unprocessed data */ - if(psnd->recv_processed == psnd->recv_size) { - free(psnd->buffer); - psnd->buffer = NULL; - psnd->allocated_size = 0; - psnd->recv_size = 0; - psnd->recv_processed = 0; -#ifdef DEBUGBUILD - psnd->bindsock = CURL_SOCKET_BAD; -#endif /* DEBUGBUILD */ - } - return (ssize_t)copysize; -} -#else /* ! USE_RECV_BEFORE_SEND_WORKAROUND */ -/* Use "do-nothing" macros instead of functions when workaround not used */ -bool Curl_recv_has_postponed_data(struct connectdata *conn, int sockindex) -{ - (void)conn; - (void)sockindex; - return false; -} -#define pre_receive_plain(d,c,n) CURLE_OK -#define get_pre_recved(c,n,b,l) 0 -#endif /* ! USE_RECV_BEFORE_SEND_WORKAROUND */ - /* * Curl_write() is an internal write function that sends data to the * server. Works with plain sockets, SCP, SSL or kerberos. @@ -294,154 +195,6 @@ CURLcode Curl_write(struct Curl_easy *data, } } -/* Curl_send_plain sends raw data without a size restriction on 'len'. */ -ssize_t Curl_send_plain(struct Curl_easy *data, int num, - const void *mem, size_t len, CURLcode *code) -{ - struct connectdata *conn; - curl_socket_t sockfd; - ssize_t bytes_written; - - DEBUGASSERT(data); - DEBUGASSERT(data->conn); - conn = data->conn; - sockfd = conn->sock[num]; - /* WinSock will destroy unread received data if send() is - failed. - To avoid lossage of received data, recv() must be - performed before every send() if any incoming data is - available. */ - if(pre_receive_plain(data, conn, num)) { - *code = CURLE_OUT_OF_MEMORY; - return -1; - } - -#if defined(MSG_FASTOPEN) && !defined(TCP_FASTOPEN_CONNECT) /* Linux */ - if(conn->bits.tcp_fastopen) { - bytes_written = sendto(sockfd, mem, len, MSG_FASTOPEN, - &conn->remote_addr->sa_addr, - conn->remote_addr->addrlen); - conn->bits.tcp_fastopen = FALSE; - } - else -#endif - bytes_written = swrite(sockfd, mem, len); - - *code = CURLE_OK; - if(-1 == bytes_written) { - int err = SOCKERRNO; - - if( -#ifdef WSAEWOULDBLOCK - /* This is how Windows does it */ - (WSAEWOULDBLOCK == err) -#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 == err) || (EAGAIN == err) || (EINTR == err) || - (EINPROGRESS == err) -#endif - ) { - /* this is just a case of EWOULDBLOCK */ - *code = CURLE_AGAIN; - } - else { - char buffer[STRERROR_LEN]; - failf(data, "Send failure: %s", - Curl_strerror(err, buffer, sizeof(buffer))); - data->state.os_errno = err; - *code = CURLE_SEND_ERROR; - } - } - return bytes_written; -} - -/* - * Curl_write_plain() is an internal write function that sends data to the - * server using plain sockets only. Otherwise meant to have the exact same - * proto as Curl_write(). - * - * This function wraps Curl_send_plain(). The only difference besides the - * prototype is '*written' (bytes written) is set to 0 on error. - * 'sockfd' must be one of the connection's two main sockets and the value of - * 'len' must not be changed. - */ -CURLcode Curl_write_plain(struct Curl_easy *data, - curl_socket_t sockfd, - const void *mem, - size_t len, - ssize_t *written) -{ - CURLcode result; - struct connectdata *conn = data->conn; - int num; - DEBUGASSERT(conn); - DEBUGASSERT(sockfd == conn->sock[FIRSTSOCKET] || - sockfd == conn->sock[SECONDARYSOCKET]); - if(sockfd != conn->sock[FIRSTSOCKET] && - sockfd != conn->sock[SECONDARYSOCKET]) - return CURLE_BAD_FUNCTION_ARGUMENT; - - num = (sockfd == conn->sock[SECONDARYSOCKET]); - - *written = Curl_send_plain(data, num, mem, len, &result); - if(*written == -1) - *written = 0; - - return result; -} - -/* Curl_recv_plain receives raw data without a size restriction on 'len'. */ -ssize_t Curl_recv_plain(struct Curl_easy *data, int num, char *buf, - size_t len, CURLcode *code) -{ - struct connectdata *conn; - curl_socket_t sockfd; - ssize_t nread; - DEBUGASSERT(data); - DEBUGASSERT(data->conn); - conn = data->conn; - sockfd = conn->sock[num]; - /* Check and return data that already received and storied in internal - intermediate buffer */ - nread = get_pre_recved(conn, num, buf, len); - if(nread > 0) { - *code = CURLE_OK; - return nread; - } - - nread = sread(sockfd, buf, len); - - *code = CURLE_OK; - if(-1 == nread) { - int err = SOCKERRNO; - - if( -#ifdef WSAEWOULDBLOCK - /* This is how Windows does it */ - (WSAEWOULDBLOCK == err) -#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 == err) || (EAGAIN == err) || (EINTR == err) -#endif - ) { - /* this is just a case of EWOULDBLOCK */ - *code = CURLE_AGAIN; - } - else { - char buffer[STRERROR_LEN]; - failf(data, "Recv failure: %s", - Curl_strerror(err, buffer, sizeof(buffer))); - data->state.os_errno = err; - *code = CURLE_RECV_ERROR; - } - } - return nread; -} - static CURLcode pausewrite(struct Curl_easy *data, int type, /* what type of data */ const char *ptr, @@ -634,41 +387,6 @@ CURLcode Curl_client_write(struct Curl_easy *data, } /* - * Curl_read_plain() is an internal read function that reads data from the - * server using plain sockets only. Otherwise meant to have the exact same - * proto as Curl_read(). - * - * This function wraps Curl_recv_plain(). The only difference besides the - * prototype is '*n' (bytes read) is set to 0 on error. - * 'sockfd' must be one of the connection's two main sockets and the value of - * 'sizerequested' must not be changed. - */ -CURLcode Curl_read_plain(struct Curl_easy *data, /* transfer */ - curl_socket_t sockfd, /* read from this socket */ - char *buf, /* store read data here */ - size_t sizerequested, /* max amount to read */ - ssize_t *n) /* amount bytes read */ -{ - CURLcode result; - struct connectdata *conn = data->conn; - int num; - DEBUGASSERT(conn); - DEBUGASSERT(sockfd == conn->sock[FIRSTSOCKET] || - sockfd == conn->sock[SECONDARYSOCKET]); - if(sockfd != conn->sock[FIRSTSOCKET] && - sockfd != conn->sock[SECONDARYSOCKET]) - return CURLE_BAD_FUNCTION_ARGUMENT; - - num = (sockfd == conn->sock[SECONDARYSOCKET]); - - *n = Curl_recv_plain(data, num, buf, sizerequested, &result); - if(*n == -1) - *n = 0; - - return result; -} - -/* * Internal read-from-socket function. This is meant to deal with plain * sockets, SSL sockets and kerberos sockets. * diff --git a/lib/sendf.h b/lib/sendf.h index 409618f7e..d0c927570 100644 --- a/lib/sendf.h +++ b/lib/sendf.h @@ -40,20 +40,6 @@ CURLcode Curl_client_write(struct Curl_easy *data, int type, char *ptr, size_t len) WARN_UNUSED_RESULT; -bool Curl_recv_has_postponed_data(struct connectdata *conn, int sockindex); - -/* internal read-function, does plain socket only */ -CURLcode Curl_read_plain(struct Curl_easy *data, - curl_socket_t sockfd, - char *buf, - size_t sizerequested, - ssize_t *n); - -ssize_t Curl_recv_plain(struct Curl_easy *data, int num, char *buf, - size_t len, CURLcode *code); -ssize_t Curl_send_plain(struct Curl_easy *data, int num, - const void *mem, size_t len, CURLcode *code); - /* internal read-function, does plain socket, SSL and krb4 */ CURLcode Curl_read(struct Curl_easy *data, curl_socket_t sockfd, char *buf, size_t buffersize, @@ -65,10 +51,4 @@ CURLcode Curl_write(struct Curl_easy *data, const void *mem, size_t len, ssize_t *written); -/* internal write-function, does plain sockets ONLY */ -CURLcode Curl_write_plain(struct Curl_easy *data, - curl_socket_t sockfd, - const void *mem, size_t len, - ssize_t *written); - #endif /* HEADER_CURL_SENDF_H */ 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); diff --git a/lib/socks.h b/lib/socks.h index b6ce4534b..ba5b54a7b 100644 --- a/lib/socks.h +++ b/lib/socks.h @@ -37,8 +37,8 @@ * * This is STUPID BLOCKING behavior */ -int Curl_blockread_all(struct Curl_easy *data, - curl_socket_t sockfd, +int Curl_blockread_all(struct Curl_cfilter *cf, + struct Curl_easy *data, char *buf, ssize_t buffersize, ssize_t *n); @@ -47,7 +47,7 @@ int Curl_blockread_all(struct Curl_easy *data, /* * This function handles the SOCKS5 GSS-API negotiation and initialization */ -CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex, +CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, struct Curl_easy *data); #endif diff --git a/lib/socks_gssapi.c b/lib/socks_gssapi.c index 381a724ac..2ede8c7c2 100644 --- a/lib/socks_gssapi.c +++ b/lib/socks_gssapi.c @@ -30,6 +30,7 @@ #include "curl_gssapi.h" #include "urldata.h" #include "sendf.h" +#include "cfilters.h" #include "connect.h" #include "timeval.h" #include "socks.h" @@ -101,14 +102,14 @@ static int check_gss_err(struct Curl_easy *data, return 0; } -CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex, +CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, struct Curl_easy *data) { - struct connectdata *conn = data->conn; - curl_socket_t sock = conn->sock[sockindex]; + struct connectdata *conn = cf->conn; + curl_socket_t sock = conn->sock[cf->sockindex]; CURLcode code; ssize_t actualread; - ssize_t written; + ssize_t nwritten; int result; OM_uint32 gss_major_status, gss_minor_status, gss_status; OM_uint32 gss_ret_flags; @@ -203,8 +204,8 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex, us_length = htons((short)gss_send_token.length); memcpy(socksreq + 2, &us_length, sizeof(short)); - code = Curl_write_plain(data, sock, (char *)socksreq, 4, &written); - if(code || (4 != written)) { + nwritten = Curl_conn_cf_send(cf->next, data, (char *)socksreq, 4, &code); + if(code || (4 != nwritten)) { failf(data, "Failed to send GSS-API authentication request."); gss_release_name(&gss_status, &server); gss_release_buffer(&gss_status, &gss_recv_token); @@ -213,10 +214,10 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex, return CURLE_COULDNT_CONNECT; } - code = Curl_write_plain(data, sock, (char *)gss_send_token.value, - gss_send_token.length, &written); - - if(code || ((ssize_t)gss_send_token.length != written)) { + nwritten = Curl_conn_cf_send(cf->next, data, + (char *)gss_send_token.value, + gss_send_token.length, &code); + if(code || ((ssize_t)gss_send_token.length != nwritten)) { failf(data, "Failed to send GSS-API authentication token."); gss_release_name(&gss_status, &server); gss_release_buffer(&gss_status, &gss_recv_token); @@ -242,7 +243,7 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex, * +----+------+-----+----------------+ */ - result = Curl_blockread_all(data, sock, (char *)socksreq, 4, &actualread); + result = Curl_blockread_all(cf, data, (char *)socksreq, 4, &actualread); if(result || (actualread != 4)) { failf(data, "Failed to receive GSS-API authentication response."); gss_release_name(&gss_status, &server); @@ -281,7 +282,7 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex, return CURLE_OUT_OF_MEMORY; } - result = Curl_blockread_all(data, sock, (char *)gss_recv_token.value, + result = Curl_blockread_all(cf, data, (char *)gss_recv_token.value, gss_recv_token.length, &actualread); if(result || (actualread != us_length)) { @@ -410,8 +411,8 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex, memcpy(socksreq + 2, &us_length, sizeof(short)); } - code = Curl_write_plain(data, sock, (char *)socksreq, 4, &written); - if(code || (4 != written)) { + nwritten = Curl_conn_cf_send(cf->next, data, (char *)socksreq, 4, &code); + if(code || (4 != nwritten)) { failf(data, "Failed to send GSS-API encryption request."); gss_release_buffer(&gss_status, &gss_w_token); gss_delete_sec_context(&gss_status, &gss_context, NULL); @@ -420,17 +421,18 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex, if(data->set.socks5_gssapi_nec) { memcpy(socksreq, &gss_enc, 1); - code = Curl_write_plain(data, sock, socksreq, 1, &written); - if(code || ( 1 != written)) { + nwritten = Curl_conn_cf_send(cf->next, data, (char *)socksreq, 1, &code); + if(code || ( 1 != nwritten)) { failf(data, "Failed to send GSS-API encryption type."); gss_delete_sec_context(&gss_status, &gss_context, NULL); return CURLE_COULDNT_CONNECT; } } else { - code = Curl_write_plain(data, sock, (char *)gss_w_token.value, - gss_w_token.length, &written); - if(code || ((ssize_t)gss_w_token.length != written)) { + nwritten = Curl_conn_cf_send(cf->next, data, + (char *)gss_w_token.value, + gss_w_token.length, &code); + if(code || ((ssize_t)gss_w_token.length != nwritten)) { failf(data, "Failed to send GSS-API encryption type."); gss_release_buffer(&gss_status, &gss_w_token); gss_delete_sec_context(&gss_status, &gss_context, NULL); @@ -439,7 +441,7 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex, gss_release_buffer(&gss_status, &gss_w_token); } - result = Curl_blockread_all(data, sock, (char *)socksreq, 4, &actualread); + result = Curl_blockread_all(cf, data, (char *)socksreq, 4, &actualread); if(result || (actualread != 4)) { failf(data, "Failed to receive GSS-API encryption response."); gss_delete_sec_context(&gss_status, &gss_context, NULL); @@ -470,7 +472,7 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex, gss_delete_sec_context(&gss_status, &gss_context, NULL); return CURLE_OUT_OF_MEMORY; } - result = Curl_blockread_all(data, sock, (char *)gss_recv_token.value, + result = Curl_blockread_all(cf, data, (char *)gss_recv_token.value, gss_recv_token.length, &actualread); if(result || (actualread != us_length)) { diff --git a/lib/socks_sspi.c b/lib/socks_sspi.c index eb717f4d5..d1200ea03 100644 --- a/lib/socks_sspi.c +++ b/lib/socks_sspi.c @@ -29,6 +29,7 @@ #include "urldata.h" #include "sendf.h" +#include "cfilters.h" #include "connect.h" #include "strerror.h" #include "timeval.h" @@ -62,11 +63,11 @@ static int check_sspi_err(struct Curl_easy *data, } /* This is the SSPI-using version of this function */ -CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex, +CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, struct Curl_easy *data) { - struct connectdata *conn = data->conn; - curl_socket_t sock = conn->sock[sockindex]; + struct connectdata *conn = cf->conn; + curl_socket_t sock = conn->sock[cf->sockindex]; CURLcode code; ssize_t actualread; ssize_t written; @@ -206,7 +207,7 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex, us_length = htons((short)sspi_send_token.cbBuffer); memcpy(socksreq + 2, &us_length, sizeof(short)); - code = Curl_write_plain(data, sock, (char *)socksreq, 4, &written); + written = Curl_conn_cf_send(cf->next, data, (char *)socksreq, 4, &code); if(code || (4 != written)) { failf(data, "Failed to send SSPI authentication request."); free(service_name); @@ -219,8 +220,9 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex, return CURLE_COULDNT_CONNECT; } - code = Curl_write_plain(data, sock, (char *)sspi_send_token.pvBuffer, - sspi_send_token.cbBuffer, &written); + written = Curl_conn_cf_send(cf->next, data, + (char *)sspi_send_token.pvBuffer, + sspi_send_token.cbBuffer, &code); if(code || (sspi_send_token.cbBuffer != (size_t)written)) { failf(data, "Failed to send SSPI authentication token."); free(service_name); @@ -260,7 +262,7 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex, * +----+------+-----+----------------+ */ - result = Curl_blockread_all(data, sock, (char *)socksreq, 4, &actualread); + result = Curl_blockread_all(cf, data, (char *)socksreq, 4, &actualread); if(result || (actualread != 4)) { failf(data, "Failed to receive SSPI authentication response."); free(service_name); @@ -300,7 +302,7 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex, s_pSecFn->DeleteSecurityContext(&sspi_context); return CURLE_OUT_OF_MEMORY; } - result = Curl_blockread_all(data, sock, (char *)sspi_recv_token.pvBuffer, + result = Curl_blockread_all(cf, data, (char *)sspi_recv_token.pvBuffer, sspi_recv_token.cbBuffer, &actualread); if(result || (actualread != us_length)) { @@ -468,7 +470,7 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex, memcpy(socksreq + 2, &us_length, sizeof(short)); } - code = Curl_write_plain(data, sock, (char *)socksreq, 4, &written); + written = Curl_conn_cf_send(cf->next, data, (char *)socksreq, 4, &code); if(code || (4 != written)) { failf(data, "Failed to send SSPI encryption request."); if(sspi_send_token.pvBuffer) @@ -479,7 +481,7 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex, if(data->set.socks5_gssapi_nec) { memcpy(socksreq, &gss_enc, 1); - code = Curl_write_plain(data, sock, (char *)socksreq, 1, &written); + written = Curl_conn_cf_send(cf->next, data, (char *)socksreq, 1, &code); if(code || (1 != written)) { failf(data, "Failed to send SSPI encryption type."); s_pSecFn->DeleteSecurityContext(&sspi_context); @@ -487,8 +489,9 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex, } } else { - code = Curl_write_plain(data, sock, (char *)sspi_send_token.pvBuffer, - sspi_send_token.cbBuffer, &written); + written = Curl_conn_cf_send(cf->next, data, + (char *)sspi_send_token.pvBuffer, + sspi_send_token.cbBuffer, &code); if(code || (sspi_send_token.cbBuffer != (size_t)written)) { failf(data, "Failed to send SSPI encryption type."); if(sspi_send_token.pvBuffer) @@ -500,7 +503,7 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex, s_pSecFn->FreeContextBuffer(sspi_send_token.pvBuffer); } - result = Curl_blockread_all(data, sock, (char *)socksreq, 4, &actualread); + result = Curl_blockread_all(cf, data, (char *)socksreq, 4, &actualread); if(result || (actualread != 4)) { failf(data, "Failed to receive SSPI encryption response."); s_pSecFn->DeleteSecurityContext(&sspi_context); @@ -532,7 +535,7 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex, return CURLE_OUT_OF_MEMORY; } - result = Curl_blockread_all(data, sock, (char *)sspi_w_token[0].pvBuffer, + result = Curl_blockread_all(cf, data, (char *)sspi_w_token[0].pvBuffer, sspi_w_token[0].cbBuffer, &actualread); if(result || (actualread != us_length)) { @@ -705,45 +705,6 @@ CURLcode Curl_open(struct Curl_easy **curl) return result; } -#ifdef USE_RECV_BEFORE_SEND_WORKAROUND -static void conn_reset_postponed_data(struct connectdata *conn, int num) -{ - struct postponed_data * const psnd = &(conn->postponed[num]); - if(psnd->buffer) { - DEBUGASSERT(psnd->allocated_size > 0); - DEBUGASSERT(psnd->recv_size <= psnd->allocated_size); - DEBUGASSERT(psnd->recv_size ? - (psnd->recv_processed < psnd->recv_size) : - (psnd->recv_processed == 0)); - DEBUGASSERT(psnd->bindsock != CURL_SOCKET_BAD); - free(psnd->buffer); - psnd->buffer = NULL; - psnd->allocated_size = 0; - psnd->recv_size = 0; - psnd->recv_processed = 0; -#ifdef DEBUGBUILD - psnd->bindsock = CURL_SOCKET_BAD; /* used only for DEBUGASSERT */ -#endif /* DEBUGBUILD */ - } - else { - DEBUGASSERT(psnd->allocated_size == 0); - DEBUGASSERT(psnd->recv_size == 0); - DEBUGASSERT(psnd->recv_processed == 0); - DEBUGASSERT(psnd->bindsock == CURL_SOCKET_BAD); - } -} - -static void conn_reset_all_postponed_data(struct connectdata *conn) -{ - conn_reset_postponed_data(conn, 0); - conn_reset_postponed_data(conn, 1); -} -#else /* ! USE_RECV_BEFORE_SEND_WORKAROUND */ -/* Use "do-nothing" macro instead of function when workaround not used */ -#define conn_reset_all_postponed_data(c) do {} while(0) -#endif /* ! USE_RECV_BEFORE_SEND_WORKAROUND */ - - static void conn_shutdown(struct Curl_easy *data) { DEBUGASSERT(data); @@ -792,7 +753,6 @@ static void conn_free(struct Curl_easy *data, struct connectdata *conn) Curl_safefree(conn->hostname_resolve); Curl_safefree(conn->secondaryhostname); - conn_reset_all_postponed_data(conn); Curl_llist_destroy(&conn->easyq, NULL); Curl_safefree(conn->localdev); Curl_free_primary_ssl_config(&conn->ssl_config); @@ -1545,10 +1505,6 @@ static struct connectdata *allocate_conn(struct Curl_easy *data) conn->connection_id = -1; /* no ID */ conn->port = -1; /* unknown at this point */ conn->remote_port = -1; /* unknown at this point */ -#if defined(USE_RECV_BEFORE_SEND_WORKAROUND) && defined(DEBUGBUILD) - conn->postponed[0].bindsock = CURL_SOCKET_BAD; /* no file descriptor */ - conn->postponed[1].bindsock = CURL_SOCKET_BAD; /* no file descriptor */ -#endif /* USE_RECV_BEFORE_SEND_WORKAROUND && DEBUGBUILD */ /* Default protocol-independent behavior doesn't support persistent connections, so we set this to force-close. Protocols that support @@ -3399,8 +3355,6 @@ static void reuse_conn(struct Curl_easy *data, existing->hostname_resolve = temp->hostname_resolve; temp->hostname_resolve = NULL; - conn_reset_all_postponed_data(temp); /* free buffers */ - /* re-use init */ existing->bits.reuse = TRUE; /* yes, we're re-using here */ diff --git a/lib/urldata.h b/lib/urldata.h index 7610b237a..ce0d73118 100644 --- a/lib/urldata.h +++ b/lib/urldata.h @@ -830,20 +830,6 @@ struct Curl_handler { #define CONNRESULT_NONE 0 /* No extra information. */ #define CONNRESULT_DEAD (1<<0) /* The connection is dead. */ -#ifdef USE_RECV_BEFORE_SEND_WORKAROUND -struct postponed_data { - char *buffer; /* Temporal store for received data during - sending, must be freed */ - size_t allocated_size; /* Size of temporal store */ - size_t recv_size; /* Size of received data during sending */ - size_t recv_processed; /* Size of processed part of postponed data */ -#ifdef DEBUGBUILD - curl_socket_t bindsock;/* Structure must be bound to specific socket, - used only for DEBUGASSERT */ -#endif /* DEBUGBUILD */ -}; -#endif /* USE_RECV_BEFORE_SEND_WORKAROUND */ - struct proxy_info { struct hostname host; int port; @@ -926,9 +912,6 @@ struct connectdata { Curl_send *send[2]; struct Curl_cfilter *cfilter[2]; /* connection filters */ -#ifdef USE_RECV_BEFORE_SEND_WORKAROUND - struct postponed_data postponed[2]; /* two buffers for two sockets */ -#endif /* USE_RECV_BEFORE_SEND_WORKAROUND */ struct ssl_primary_config ssl_config; #ifndef CURL_DISABLE_PROXY struct ssl_primary_config proxy_ssl_config; diff --git a/lib/vtls/schannel.h b/lib/vtls/schannel.h index 93c7238f3..7fae39fa0 100644 --- a/lib/vtls/schannel.h +++ b/lib/vtls/schannel.h @@ -179,8 +179,8 @@ struct ssl_backend_data { size_t encdata_offset, decdata_offset; unsigned char *encdata_buffer, *decdata_buffer; /* encdata_is_incomplete: if encdata contains only a partial record that - can't be decrypted without another Curl_read_plain (that is, status is - SEC_E_INCOMPLETE_MESSAGE) then set this true. after Curl_read_plain writes + can't be decrypted without another recv() (that is, status is + SEC_E_INCOMPLETE_MESSAGE) then set this true. after an recv() adds more bytes into encdata then set this back to false. */ bool encdata_is_incomplete; unsigned long req_flags, ret_flags; |