* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2010, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2014, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
- * $Id$
***************************************************************************/
-#include "setup.h"
-
-#include <stdio.h>
-#include <stdarg.h>
-#include <stdlib.h>
-#include <errno.h>
-
-#ifdef HAVE_SYS_SOCKET_H
-#include <sys/socket.h> /* required for send() & recv() prototypes */
-#endif
-
-#ifdef HAVE_UNISTD_H
-#include <unistd.h>
-#endif
+#include "curl_setup.h"
#include <curl/curl.h>
+
#include "urldata.h"
#include "sendf.h"
#include "connect.h"
-#include "sslgen.h"
+#include "vtls/vtls.h"
#include "ssh.h"
#include "multiif.h"
-#include "rtsp.h"
+#include "non-ascii.h"
#define _MPRINTF_REPLACE /* use the internal *printf() functions */
#include <curl/mprintf.h>
-/* the krb4 functions only exists for FTP and if krb4 or gssapi is defined */
-#if !defined(CURL_DISABLE_FTP) && (defined(HAVE_KRB4) || defined(HAVE_GSSAPI))
-#include "krb4.h"
-#else
-#define Curl_sec_send(a,b,c,d) -1
-#define Curl_sec_read(a,b,c,d) -1
-#endif
-
-#include <string.h>
#include "curl_memory.h"
#include "strerror.h"
-#include "easyif.h" /* for the Curl_convert_from_network prototype */
+
/* The last #include file should be: */
#include "memdebug.h"
return(size);
}
- if(data->state.prev_block_had_trailing_cr == TRUE) {
+ if(data->state.prev_block_had_trailing_cr) {
/* The previous block of incoming data
had a trailing CR, which was turned into a LF. */
if(*startPtr == '\n') {
struct SessionHandle *data = conn->data;
ssize_t bytes_written;
size_t write_len;
- CURLcode res = CURLE_OK;
+ CURLcode result = CURLE_OK;
char *s;
char *sptr;
va_list ap;
write_len = strlen(s);
sptr = s;
- while(1) {
+ for(;;) {
/* Write the buffer to the socket */
- res = Curl_write(conn, sockfd, sptr, write_len, &bytes_written);
+ result = Curl_write(conn, sockfd, sptr, write_len, &bytes_written);
- if(CURLE_OK != res)
+ if(result)
break;
if(data->set.verbose)
free(s); /* free the output string */
- return res;
+ return result;
}
-static ssize_t send_plain(struct connectdata *conn,
- int num,
- const void *mem,
- size_t len)
+/*
+ * Curl_write() is an internal write function that sends data to the
+ * server. Works with plain sockets, SCP, SSL or kerberos.
+ *
+ * If the write would block (CURLE_AGAIN), we return CURLE_OK and
+ * (*written == 0). Otherwise we return regular CURLcode value.
+ */
+CURLcode Curl_write(struct connectdata *conn,
+ curl_socket_t sockfd,
+ const void *mem,
+ size_t len,
+ ssize_t *written)
+{
+ ssize_t bytes_written;
+ CURLcode result = CURLE_OK;
+ int num = (sockfd == conn->sock[SECONDARYSOCKET]);
+
+ bytes_written = conn->send[num](conn, num, mem, len, &result);
+
+ *written = bytes_written;
+ if(bytes_written >= 0)
+ /* we completely ignore the curlcode value when subzero is not returned */
+ return CURLE_OK;
+
+ /* handle CURLE_AGAIN or a send failure */
+ switch(result) {
+ case CURLE_AGAIN:
+ *written = 0;
+ return CURLE_OK;
+
+ case CURLE_OK:
+ /* general send failure */
+ return CURLE_SEND_ERROR;
+
+ default:
+ /* we got a specific curlcode, forward it */
+ return result;
+ }
+}
+
+ssize_t Curl_send_plain(struct connectdata *conn, int num,
+ const void *mem, size_t len, CURLcode *code)
{
curl_socket_t sockfd = conn->sock[num];
ssize_t bytes_written = swrite(sockfd, mem, len);
+ *code = CURLE_OK;
if(-1 == bytes_written) {
int err = SOCKERRNO;
treat both error codes the same here */
(EWOULDBLOCK == err) || (EAGAIN == err) || (EINTR == err)
#endif
- )
+ ) {
/* this is just a case of EWOULDBLOCK */
bytes_written=0;
- else
+ *code = CURLE_AGAIN;
+ }
+ else {
failf(conn->data, "Send failure: %s",
Curl_strerror(conn, err));
+ conn->data->state.os_errno = err;
+ *code = CURLE_SEND_ERROR;
+ }
}
return bytes_written;
}
/*
- * Curl_write() is an internal write function that sends data to the
- * server. Works with plain sockets, SCP, SSL or kerberos.
- */
-CURLcode Curl_write(struct connectdata *conn,
- curl_socket_t sockfd,
- const void *mem,
- size_t len,
- ssize_t *written)
-{
- ssize_t bytes_written;
- CURLcode retcode;
- int num = (sockfd == conn->sock[SECONDARYSOCKET]);
-
- if(conn->ssl[num].state == ssl_connection_complete)
- bytes_written = Curl_ssl_send(conn, num, mem, len);
- else if(Curl_ssh_enabled(conn, PROT_SCP))
- bytes_written = Curl_scp_send(conn, num, mem, len);
- else if(Curl_ssh_enabled(conn, PROT_SFTP))
- bytes_written = Curl_sftp_send(conn, num, mem, len);
- else if(conn->sec_complete)
- bytes_written = Curl_sec_send(conn, num, mem, len);
- else
- bytes_written = send_plain(conn, num, mem, len);
-
- *written = bytes_written;
- retcode = (-1 != bytes_written)?CURLE_OK:CURLE_SEND_ERROR;
-
- return retcode;
-}
-
-/*
* 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()
ssize_t *written)
{
ssize_t bytes_written;
- CURLcode retcode;
+ CURLcode result;
int num = (sockfd == conn->sock[SECONDARYSOCKET]);
- bytes_written = send_plain(conn, num, mem, len);
+ bytes_written = Curl_send_plain(conn, num, mem, len, &result);
*written = bytes_written;
- retcode = (-1 != bytes_written)?CURLE_OK:CURLE_SEND_ERROR;
- return retcode;
+ return result;
+}
+
+ssize_t Curl_recv_plain(struct connectdata *conn, int num, char *buf,
+ size_t len, CURLcode *code)
+{
+ curl_socket_t sockfd = conn->sock[num];
+ ssize_t 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 therefor
+ 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 {
+ failf(conn->data, "Recv failure: %s",
+ Curl_strerror(conn, err));
+ conn->data->state.os_errno = err;
+ *code = CURLE_RECV_ERROR;
+ }
+ }
+ return nread;
}
static CURLcode pausewrite(struct SessionHandle *data,
/* mark the connection as RECV paused */
k->keepon |= KEEP_RECV_PAUSE;
- DEBUGF(infof(data, "Pausing with %d bytes in buffer for type %02x\n",
- (int)len, type));
+ DEBUGF(infof(data, "Pausing with %zu bytes in buffer for type %02x\n",
+ len, type));
return CURLE_OK;
}
-/* Curl_client_write() sends data to the write callback(s)
-
- The bit pattern defines to what "streams" to write to. Body and/or header.
- The defines are in sendf.h of course.
-
- If CURL_DO_LINEEND_CONV is enabled, data is converted IN PLACE to the
- local character encoding. This is a problem and should be changed in
- the future to leave the original data alone.
+/* Curl_client_chop_write() writes chunks of data not larger than
+ * CURL_MAX_WRITE_SIZE via client write callback(s) and
+ * takes care of pause requests from the callbacks.
*/
-CURLcode Curl_client_write(struct connectdata *conn,
- int type,
- char *ptr,
- size_t len)
+CURLcode Curl_client_chop_write(struct connectdata *conn,
+ int type,
+ char * ptr,
+ size_t len)
{
struct SessionHandle *data = conn->data;
- size_t wrote;
+ curl_write_callback writeheader = NULL;
+ curl_write_callback writebody = NULL;
- if(0 == len)
- len = strlen(ptr);
+ if(!len)
+ return CURLE_OK;
/* If reading is actually paused, we're forced to append this chunk of data
to the already held data, but only if it is the same type as otherwise it
/* update the pointer and the size */
data->state.tempwrite = newptr;
data->state.tempwritesize = newlen;
-
return CURLE_OK;
}
- if(type & CLIENTWRITE_BODY) {
- if((conn->protocol&PROT_FTP) && conn->proto.ftpc.transfertype == 'A') {
-#ifdef CURL_DOES_CONVERSIONS
- /* convert from the network encoding */
- size_t rc;
- rc = Curl_convert_from_network(data, ptr, len);
- /* Curl_convert_from_network calls failf if unsuccessful */
- if(rc != CURLE_OK)
- return rc;
-#endif /* CURL_DOES_CONVERSIONS */
-
-#ifdef CURL_DO_LINEEND_CONV
- /* convert end-of-line markers */
- len = convert_lineends(data, ptr, len);
-#endif /* CURL_DO_LINEEND_CONV */
- }
- /* If the previous block of data ended with CR and this block of data is
- just a NL, then the length might be zero */
- if(len) {
- wrote = data->set.fwrite_func(ptr, 1, len, data->set.out);
- }
- else {
- wrote = len;
- }
-
- if(CURL_WRITEFUNC_PAUSE == wrote)
- return pausewrite(data, type, ptr, len);
-
- if(wrote != len) {
- failf(data, "Failed writing body (%d != %d)", (int)wrote, (int)len);
- return CURLE_WRITE_ERROR;
- }
- }
-
+ /* Determine the callback(s) to use. */
+ if(type & CLIENTWRITE_BODY)
+ writebody = data->set.fwrite_func;
if((type & CLIENTWRITE_HEADER) &&
- (data->set.fwrite_header || data->set.writeheader) ) {
+ (data->set.fwrite_header || data->set.writeheader)) {
/*
* Write headers to the same callback or to the especially setup
* header callback function (added after version 7.7.1).
*/
- curl_write_callback writeit=
- data->set.fwrite_header?data->set.fwrite_header:data->set.fwrite_func;
-
- /* Note: The header is in the host encoding
- regardless of the ftp transfer mode (ASCII/Image) */
-
- wrote = writeit(ptr, 1, len, data->set.writeheader);
- if(CURL_WRITEFUNC_PAUSE == wrote)
- /* here we pass in the HEADER bit only since if this was body as well
- then it was passed already and clearly that didn't trigger the pause,
- so this is saved for later with the HEADER bit only */
- return pausewrite(data, CLIENTWRITE_HEADER, ptr, len);
-
- if(wrote != len) {
- failf (data, "Failed writing header");
- return CURLE_WRITE_ERROR;
+ writeheader =
+ data->set.fwrite_header? data->set.fwrite_header: data->set.fwrite_func;
+ }
+
+ /* Chop data, write chunks. */
+ while(len) {
+ size_t chunklen = len <= CURL_MAX_WRITE_SIZE? len: CURL_MAX_WRITE_SIZE;
+
+ if(writebody) {
+ size_t wrote = writebody(ptr, 1, chunklen, data->set.out);
+
+ if(CURL_WRITEFUNC_PAUSE == wrote) {
+ if(conn->handler->flags & PROTOPT_NONETWORK) {
+ /* Protocols that work without network cannot be paused. This is
+ actually only FILE:// just now, and it can't pause since the
+ transfer isn't done using the "normal" procedure. */
+ failf(data, "Write callback asked for PAUSE when not supported!");
+ return CURLE_WRITE_ERROR;
+ }
+ else
+ return pausewrite(data, type, ptr, len);
+ }
+ else if(wrote != chunklen) {
+ failf(data, "Failed writing body (%zu != %zu)", wrote, chunklen);
+ return CURLE_WRITE_ERROR;
+ }
+ }
+
+ if(writeheader) {
+ size_t wrote = writeheader(ptr, 1, chunklen, data->set.writeheader);
+
+ if(CURL_WRITEFUNC_PAUSE == wrote)
+ /* here we pass in the HEADER bit only since if this was body as well
+ then it was passed already and clearly that didn't trigger the
+ pause, so this is saved for later with the HEADER bit only */
+ return pausewrite(data, CLIENTWRITE_HEADER, ptr, len);
+
+ if(wrote != chunklen) {
+ failf (data, "Failed writing header");
+ return CURLE_WRITE_ERROR;
+ }
}
+
+ ptr += chunklen;
+ len -= chunklen;
}
return CURLE_OK;
}
-int Curl_read_plain(curl_socket_t sockfd,
+
+/* Curl_client_write() sends data to the write callback(s)
+
+ The bit pattern defines to what "streams" to write to. Body and/or header.
+ The defines are in sendf.h of course.
+
+ If CURL_DO_LINEEND_CONV is enabled, data is converted IN PLACE to the
+ local character encoding. This is a problem and should be changed in
+ the future to leave the original data alone.
+ */
+CURLcode Curl_client_write(struct connectdata *conn,
+ int type,
+ char *ptr,
+ size_t len)
+{
+ struct SessionHandle *data = conn->data;
+
+ if(0 == len)
+ len = strlen(ptr);
+
+ /* FTP data may need conversion. */
+ if((type & CLIENTWRITE_BODY) &&
+ (conn->handler->protocol & PROTO_FAMILY_FTP) &&
+ conn->proto.ftpc.transfertype == 'A') {
+ /* convert from the network encoding */
+ CURLcode result = Curl_convert_from_network(data, ptr, len);
+ /* Curl_convert_from_network calls failf if unsuccessful */
+ if(result)
+ return result;
+
+#ifdef CURL_DO_LINEEND_CONV
+ /* convert end-of-line markers */
+ len = convert_lineends(data, ptr, len);
+#endif /* CURL_DO_LINEEND_CONV */
+ }
+
+ return Curl_client_chop_write(conn, type, ptr, len);
+}
+
+CURLcode Curl_read_plain(curl_socket_t sockfd,
char *buf,
size_t bytesfromsocket,
ssize_t *n)
#else
if((EWOULDBLOCK == err) || (EAGAIN == err) || (EINTR == err))
#endif
- return -1;
+ return CURLE_AGAIN;
else
return CURLE_RECV_ERROR;
}
* Internal read-from-socket function. This is meant to deal with plain
* sockets, SSL sockets and kerberos sockets.
*
- * If the read would block (EWOULDBLOCK) we return -1. Otherwise we return
- * a regular CURLcode value.
+ * Returns a regular CURLcode value.
*/
-int Curl_read(struct connectdata *conn, /* connection data */
- 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 Curl_read(struct connectdata *conn, /* connection data */
+ 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 = CURLE_RECV_ERROR;
ssize_t nread = 0;
size_t bytesfromsocket = 0;
char *buffertofill = NULL;
- bool pipelining = (bool)(conn->data->multi &&
- Curl_multi_canPipeline(conn->data->multi));
+ bool pipelining = Curl_multi_pipeline_enabled(conn->data->multi);
/* Set 'num' to 0 or 1, depending on which socket that has been sent here.
If it is the second socket, we set num to 1. Otherwise to 0. This lets
buffertofill = buf;
}
- if(conn->ssl[num].state == ssl_connection_complete) {
- nread = Curl_ssl_recv(conn, num, buffertofill, bytesfromsocket);
+ nread = conn->recv[num](conn, num, buffertofill, bytesfromsocket, &result);
+ if(nread < 0)
+ return result;
- if(nread == -1) {
- return -1; /* -1 from Curl_ssl_recv() means EWOULDBLOCK */
- }
- }
- else if(Curl_ssh_enabled(conn, (PROT_SCP|PROT_SFTP))) {
- if(conn->protocol & PROT_SCP)
- nread = Curl_scp_recv(conn, num, buffertofill, bytesfromsocket);
- else if(conn->protocol & PROT_SFTP)
- nread = Curl_sftp_recv(conn, num, buffertofill, bytesfromsocket);
-#ifdef LIBSSH2CHANNEL_EAGAIN
- if((nread == LIBSSH2CHANNEL_EAGAIN) || (nread == 0))
- /* EWOULDBLOCK */
- return -1;
-#endif
- if(nread < 0)
- /* since it is negative and not EAGAIN, it was a protocol-layer error */
- return CURLE_RECV_ERROR;
- }
- else {
- if(conn->sec_complete)
- nread = Curl_sec_read(conn, sockfd, buffertofill,
- bytesfromsocket);
- /* TODO: Need to handle EAGAIN here somehow, similar to how it
- * is done in Curl_read_plain, either right here or in Curl_sec_read
- * itself. */
- else {
- int ret = Curl_read_plain(sockfd, buffertofill, bytesfromsocket,
- &nread);
- if(ret)
- return ret;
- }
+ if(pipelining) {
+ memcpy(buf, conn->master_buffer, nread);
+ conn->buf_len = nread;
+ conn->read_pos = nread;
}
- if(nread >= 0) {
- if(pipelining) {
- memcpy(buf, conn->master_buffer, nread);
- conn->buf_len = nread;
- conn->read_pos = nread;
- }
- *n += nread;
- }
+ *n += nread;
return CURLE_OK;
}
size_t i;
for(i = 0; i < size-4; i++) {
if(memcmp(&buf[i], "\x0d\x0a\x0d\x0a", 4) == 0) {
- /* convert everthing through this CRLFCRLF but no further */
+ /* convert everything through this CRLFCRLF but no further */
conv_size = i + 4;
break;
}
switch (type) {
case CURLINFO_HEADER_IN:
w = "Header";
+ /* FALLTHROUGH */
case CURLINFO_DATA_IN:
t = "from";
break;
case CURLINFO_HEADER_OUT:
w = "Header";
+ /* FALLTHROUGH */
case CURLINFO_DATA_OUT:
t = "to";
break;