diff options
Diffstat (limited to 'plugins/FTPFileYM/curl/lib')
122 files changed, 11813 insertions, 4434 deletions
diff --git a/plugins/FTPFileYM/curl/lib/asyn-ares.c b/plugins/FTPFileYM/curl/lib/asyn-ares.c index 97b3f79031..0ef36cbbe2 100644 --- a/plugins/FTPFileYM/curl/lib/asyn-ares.c +++ b/plugins/FTPFileYM/curl/lib/asyn-ares.c @@ -315,6 +315,7 @@ CURLcode Curl_resolver_is_resolved(struct connectdata *conn, struct SessionHandle *data = conn->data; struct ResolverResults *res = (struct ResolverResults *) conn->async.os_specific; + CURLcode rc = CURLE_OK; *dns = NULL; @@ -325,19 +326,19 @@ CURLcode Curl_resolver_is_resolved(struct connectdata *conn, /* temp_ai ownership is moved to the connection, so we need not free-up them */ res->temp_ai = NULL; - destroy_async_data(&conn->async); if(!conn->async.dns) { - failf(data, "Could not resolve %s: %s (%s)", - conn->bits.proxy?"proxy":"host", - conn->host.dispname, - ares_strerror(conn->async.status)); - return conn->bits.proxy?CURLE_COULDNT_RESOLVE_PROXY: + failf(data, "Could not resolve: %s (%s)", + conn->async.hostname, ares_strerror(conn->async.status)); + rc = conn->bits.proxy?CURLE_COULDNT_RESOLVE_PROXY: CURLE_COULDNT_RESOLVE_HOST; } - *dns = conn->async.dns; + else + *dns = conn->async.dns; + + destroy_async_data(&conn->async); } - return CURLE_OK; + return rc; } /* @@ -415,37 +416,12 @@ CURLcode Curl_resolver_wait_resolv(struct connectdata *conn, if(entry) *entry = conn->async.dns; - if(!conn->async.dns) { - /* a name was not resolved */ - if((timeout < 0) || (conn->async.status == ARES_ETIMEOUT)) { - if(conn->bits.proxy) { - failf(data, "Resolving proxy timed out: %s", conn->proxy.dispname); - rc = CURLE_COULDNT_RESOLVE_PROXY; - } - else { - failf(data, "Resolving host timed out: %s", conn->host.dispname); - rc = CURLE_COULDNT_RESOLVE_HOST; - } - } - else if(conn->async.done) { - if(conn->bits.proxy) { - failf(data, "Could not resolve proxy: %s (%s)", conn->proxy.dispname, - ares_strerror(conn->async.status)); - rc = CURLE_COULDNT_RESOLVE_PROXY; - } - else { - failf(data, "Could not resolve host: %s (%s)", conn->host.dispname, - ares_strerror(conn->async.status)); - rc = CURLE_COULDNT_RESOLVE_HOST; - } - } - else - rc = CURLE_OPERATION_TIMEDOUT; - + if(rc) /* close the connection, since we can't return failure here without - cleaning up this connection properly */ + cleaning up this connection properly. + TODO: remove this action from here, it is not a name resolver decision. + */ conn->bits.close = TRUE; - } return rc; } @@ -614,8 +590,19 @@ CURLcode Curl_set_dns_servers(struct SessionHandle *data, char *servers) { CURLcode result = CURLE_NOT_BUILT_IN; + int ares_result; + + /* If server is NULL or empty, this would purge all DNS servers + * from ares library, which will cause any and all queries to fail. + * So, just return OK if none are configured and don't actually make + * any changes to c-ares. This lets c-ares use it's defaults, which + * it gets from the OS (for instance from /etc/resolv.conf on Linux). + */ + if(!(servers && servers[0])) + return CURLE_OK; + #if (ARES_VERSION >= 0x010704) - int ares_result = ares_set_servers_csv(data->state.resolver, servers); + ares_result = ares_set_servers_csv(data->state.resolver, servers); switch(ares_result) { case ARES_SUCCESS: result = CURLE_OK; @@ -632,8 +619,76 @@ CURLcode Curl_set_dns_servers(struct SessionHandle *data, } #else /* too old c-ares version! */ (void)data; - (void)servers; + (void)(ares_result); #endif return result; } + +CURLcode Curl_set_dns_interface(struct SessionHandle *data, + const char *interf) +{ +#if (ARES_VERSION >= 0x010704) + if(!interf) + interf = ""; + + ares_set_local_dev((ares_channel)data->state.resolver, interf); + + return CURLE_OK; +#else /* c-ares version too old! */ + (void)data; + (void)interf; + return CURLE_NOT_BUILT_IN; +#endif +} + +CURLcode Curl_set_dns_local_ip4(struct SessionHandle *data, + const char *local_ip4) +{ +#if (ARES_VERSION >= 0x010704) + uint32_t a4; + + if((!local_ip4) || (local_ip4[0] == 0)) { + a4 = 0; /* disabled: do not bind to a specific address */ + } + else { + if(Curl_inet_pton(AF_INET, local_ip4, &a4) != 1) { + return CURLE_BAD_FUNCTION_ARGUMENT; + } + } + + ares_set_local_ip4((ares_channel)data->state.resolver, ntohl(a4)); + + return CURLE_OK; +#else /* c-ares version too old! */ + (void)data; + (void)local_ip4; + return CURLE_NOT_BUILT_IN; +#endif +} + +CURLcode Curl_set_dns_local_ip6(struct SessionHandle *data, + const char *local_ip6) +{ +#if (ARES_VERSION >= 0x010704) + unsigned char a6[INET6_ADDRSTRLEN]; + + if((!local_ip6) || (local_ip6[0] == 0)) { + /* disabled: do not bind to a specific address */ + memset(a6, 0, sizeof(a6)); + } + else { + if(Curl_inet_pton(AF_INET6, local_ip6, a6) != 1) { + return CURLE_BAD_FUNCTION_ARGUMENT; + } + } + + ares_set_local_ip6((ares_channel)data->state.resolver, a6); + + return CURLE_OK; +#else /* c-ares version too old! */ + (void)data; + (void)local_ip6; + return CURLE_NOT_BUILT_IN; +#endif +} #endif /* CURLRES_ARES */ diff --git a/plugins/FTPFileYM/curl/lib/asyn-thread.c b/plugins/FTPFileYM/curl/lib/asyn-thread.c index 7a8294ddf5..4882c37203 100644 --- a/plugins/FTPFileYM/curl/lib/asyn-thread.c +++ b/plugins/FTPFileYM/curl/lib/asyn-thread.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2012, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2013, 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 @@ -170,7 +170,7 @@ struct thread_sync_data { struct thread_data { curl_thread_t thread_hnd; unsigned int poll_interval; - int interval_end; + long interval_end; struct thread_sync_data tsd; }; @@ -265,7 +265,7 @@ static int getaddrinfo_complete(struct connectdata *conn) static unsigned int CURL_STDCALL getaddrinfo_thread (void *arg) { struct thread_sync_data *tsd = (struct thread_sync_data*)arg; - char service [NI_MAXSERV]; + char service[12]; int rc; snprintf(service, sizeof(service), "%d", tsd->port); @@ -387,61 +387,27 @@ static bool init_resolve_thread (struct connectdata *conn, return FALSE; } -#if defined(HAVE_GETADDRINFO) && !defined(HAVE_GAI_STRERROR) && !defined(WIN32) -/* NetWare has getaddrinfo but lacks gai_strerror. - Windows has a gai_strerror but it is bad (not thread-safe) and the generic - socket error string function can be used for this pupose. */ -static const char *gai_strerror(int ecode) -{ - switch (ecode) { - case EAI_AGAIN: - return "The name could not be resolved at this time"; - case EAI_BADFLAGS: - return "The flags parameter had an invalid value"; - case EAI_FAIL: - return "A non-recoverable error occurred when attempting to " - "resolve the name"; - case EAI_FAMILY: - return "The address family was not recognized"; - case EAI_MEMORY: - return "Out of memory"; - case EAI_NONAME: - return "The name does not resolve for the supplied parameters"; - case EAI_SERVICE: - return "The service passed was not recognized for the " - "specified socket type" - case EAI_SOCKTYPE: - return "The intended socket type was not recognized" - case EAI_SYSTEM: - return "A system error occurred"; - case EAI_OVERFLOW: - return "An argument buffer overflowed"; - default: - return "Unknown error"; - -/* define this now as this is a private implementation of said function */ -#define HAVE_GAI_STRERROR -} -#endif - - /* * resolver_error() calls failf() with the appropriate message after a resolve * error */ -static void resolver_error(struct connectdata *conn, const char *host_or_proxy) +static CURLcode resolver_error(struct connectdata *conn) { - failf(conn->data, "Could not resolve %s: %s; %s", host_or_proxy, - conn->async.hostname, -#ifdef HAVE_GAI_STRERROR - /* NetWare doesn't have gai_strerror and on Windows it isn't deemed - thread-safe */ - gai_strerror(conn->async.status) -#else - Curl_strerror(conn, conn->async.status) -#endif - ); + const char *host_or_proxy; + CURLcode rc; + if(conn->bits.httpproxy) { + host_or_proxy = "proxy"; + rc = CURLE_COULDNT_RESOLVE_PROXY; + } + else { + host_or_proxy = "host"; + rc = CURLE_COULDNT_RESOLVE_HOST; + } + + failf(conn->data, "Could not resolve %s: %s", host_or_proxy, + conn->async.hostname); + return rc; } /* @@ -473,17 +439,9 @@ CURLcode Curl_resolver_wait_resolv(struct connectdata *conn, if(entry) *entry = conn->async.dns; - if(!conn->async.dns) { - /* a name was not resolved */ - if(conn->bits.httpproxy) { - resolver_error(conn, "proxy"); - rc = CURLE_COULDNT_RESOLVE_PROXY; - } - else { - resolver_error(conn, "host"); - rc = CURLE_COULDNT_RESOLVE_HOST; - } - } + if(!conn->async.dns) + /* a name was not resolved, report error */ + rc = resolver_error(conn); destroy_async_data(&conn->async); @@ -518,17 +476,18 @@ CURLcode Curl_resolver_is_resolved(struct connectdata *conn, if(done) { getaddrinfo_complete(conn); - destroy_async_data(&conn->async); if(!conn->async.dns) { - resolver_error(conn, "host"); - return CURLE_COULDNT_RESOLVE_HOST; + CURLcode rc = resolver_error(conn); + destroy_async_data(&conn->async); + return rc; } + destroy_async_data(&conn->async); *entry = conn->async.dns; } else { /* poll for name lookup done with exponential backoff up to 250ms */ - int elapsed = Curl_tvdiff(Curl_tvnow(), data->progress.t_startsingle); + long elapsed = Curl_tvdiff(Curl_tvnow(), data->progress.t_startsingle); if(elapsed < 0) elapsed = 0; @@ -600,7 +559,7 @@ Curl_addrinfo *Curl_resolver_getaddrinfo(struct connectdata *conn, struct in_addr in; Curl_addrinfo *res; int error; - char sbuf[NI_MAXSERV]; + char sbuf[12]; int pf = PF_INET; #ifdef CURLRES_IPV6 struct in6_addr in6; @@ -676,4 +635,28 @@ CURLcode Curl_set_dns_servers(struct SessionHandle *data, } +CURLcode Curl_set_dns_interface(struct SessionHandle *data, + const char *interf) +{ + (void)data; + (void)interf; + return CURLE_NOT_BUILT_IN; +} + +CURLcode Curl_set_dns_local_ip4(struct SessionHandle *data, + const char *local_ip4) +{ + (void)data; + (void)local_ip4; + return CURLE_NOT_BUILT_IN; +} + +CURLcode Curl_set_dns_local_ip6(struct SessionHandle *data, + const char *local_ip6) +{ + (void)data; + (void)local_ip6; + return CURLE_NOT_BUILT_IN; +} + #endif /* CURLRES_THREADED */ diff --git a/plugins/FTPFileYM/curl/lib/axtls.c b/plugins/FTPFileYM/curl/lib/axtls.c index d512950c2c..44e6b9303c 100644 --- a/plugins/FTPFileYM/curl/lib/axtls.c +++ b/plugins/FTPFileYM/curl/lib/axtls.c @@ -41,26 +41,12 @@ #define _MPRINTF_REPLACE /* use our functions only */ #include <curl/mprintf.h> #include "curl_memory.h" +#include <unistd.h> /* The last #include file should be: */ #include "memdebug.h" #include "hostcheck.h" -/* SSL_read is opied from axTLS compat layer */ -static int SSL_read(SSL *ssl, void *buf, int num) -{ - uint8_t *read_buf; - int ret; - - while((ret = ssl_read(ssl, &read_buf)) == SSL_OK); - - if(ret > SSL_OK) { - memcpy(buf, read_buf, ret > num ? num : ret); - } - - return ret; -} - /* Global axTLS init, called from Curl_ssl_init() */ int Curl_axtls_init(void) { @@ -131,31 +117,40 @@ static CURLcode map_error_to_curl(int axtls_err) static Curl_recv axtls_recv; static Curl_send axtls_send; +static void free_ssl_structs(struct ssl_connect_data *connssl) +{ + if(connssl->ssl) { + ssl_free (connssl->ssl); + connssl->ssl = NULL; + } + if(connssl->ssl_ctx) { + ssl_ctx_free(connssl->ssl_ctx); + connssl->ssl_ctx = NULL; + } +} + /* - * This function is called after the TCP connect has completed. Setup the TLS - * layer and do all necessary magic. + * For both blocking and non-blocking connects, this function sets up the + * ssl context and state. This function is called after the TCP connect + * has completed. */ -CURLcode -Curl_axtls_connect(struct connectdata *conn, - int sockindex) - +static CURLcode connect_prep(struct connectdata *conn, int sockindex) { struct SessionHandle *data = conn->data; SSL_CTX *ssl_ctx; - SSL *ssl; + SSL *ssl = NULL; int cert_types[] = {SSL_OBJ_X509_CERT, SSL_OBJ_PKCS12, 0}; int key_types[] = {SSL_OBJ_RSA_KEY, SSL_OBJ_PKCS8, SSL_OBJ_PKCS12, 0}; int i, ssl_fcn_return; const uint8_t *ssl_sessionid; size_t ssl_idsize; - const char *peer_CN; - uint32_t dns_altname_index; - const char *dns_altname; - int8_t found_subject_alt_names = 0; - int8_t found_subject_alt_name_matching_conn = 0; - /* Assuming users will not compile in custom key/cert to axTLS */ - uint32_t client_option = SSL_NO_DEFAULT_KEY|SSL_SERVER_VERIFY_LATER; + /* Assuming users will not compile in custom key/cert to axTLS. + * Also, even for blocking connects, use axTLS non-blocking feature. + */ + uint32_t client_option = SSL_NO_DEFAULT_KEY | + SSL_SERVER_VERIFY_LATER | + SSL_CONNECT_IN_PARTS; if(conn->ssl[sockindex].state == ssl_connection_complete) /* to make us tolerant against being called more than once for the @@ -184,6 +179,9 @@ Curl_axtls_connect(struct connectdata *conn, return CURLE_SSL_CONNECT_ERROR; } + conn->ssl[sockindex].ssl_ctx = ssl_ctx; + conn->ssl[sockindex].ssl = NULL; + /* Load the trusted CA cert bundle file */ if(data->set.ssl.CAfile) { if(ssl_obj_load(ssl_ctx, SSL_OBJ_X509_CACERT, data->set.ssl.CAfile, NULL) @@ -191,7 +189,6 @@ Curl_axtls_connect(struct connectdata *conn, infof(data, "error reading ca cert file %s \n", data->set.ssl.CAfile); if(data->set.ssl.verifypeer) { - Curl_axtls_close(conn, sockindex); return CURLE_SSL_CACERT_BADFILE; } } @@ -225,7 +222,6 @@ Curl_axtls_connect(struct connectdata *conn, if(cert_types[i] == 0) { failf(data, "%s is not x509 or pkcs12 format", data->set.str[STRING_CERT]); - Curl_axtls_close(conn, sockindex); return CURLE_SSL_CERTPROBLEM; } } @@ -250,7 +246,6 @@ Curl_axtls_connect(struct connectdata *conn, if(key_types[i] == 0) { failf(data, "Failure: %s is not a supported key file", data->set.str[STRING_KEY]); - Curl_axtls_close(conn, sockindex); return CURLE_SSL_CONNECT_ERROR; } } @@ -271,14 +266,25 @@ Curl_axtls_connect(struct connectdata *conn, else ssl = ssl_client_new(ssl_ctx, conn->sock[sockindex], NULL, 0); - /* Check to make sure handshake was ok. */ - ssl_fcn_return = ssl_handshake_status(ssl); - if(ssl_fcn_return != SSL_OK) { - Curl_axtls_close(conn, sockindex); - ssl_display_error(ssl_fcn_return); /* goes to stdout. */ - return map_error_to_curl(ssl_fcn_return); - } - infof (data, "handshake completed successfully\n"); + conn->ssl[sockindex].ssl = ssl; + return CURLE_OK; +} + +/* + * For both blocking and non-blocking connects, this function finalizes the + * SSL connection. + */ +static CURLcode connect_finish(struct connectdata *conn, int sockindex) +{ + struct SessionHandle *data = conn->data; + SSL *ssl = conn->ssl[sockindex].ssl; + const uint8_t *ssl_sessionid; + size_t ssl_idsize; + const char *peer_CN; + uint32_t dns_altname_index; + const char *dns_altname; + int8_t found_subject_alt_names = 0; + int8_t found_subject_alt_name_matching_conn = 0; /* Here, gtls.c gets the peer certificates and fails out depending on * settings in "data." axTLS api doesn't have get cert chain fcn, so omit? @@ -289,7 +295,7 @@ Curl_axtls_connect(struct connectdata *conn, if(ssl_verify_cert(ssl) != SSL_OK) { Curl_axtls_close(conn, sockindex); failf(data, "server cert verify failed"); - return CURLE_SSL_CONNECT_ERROR; + return CURLE_PEER_FAILED_VERIFICATION; } } else @@ -306,7 +312,6 @@ Curl_axtls_connect(struct connectdata *conn, * this, but a couple fields are available. */ - /* There is no (DNS) Altnames count in the version 1.4.8 API. There is a risk of an inifite loop */ for(dns_altname_index = 0; ; dns_altname_index++) { @@ -326,20 +331,29 @@ Curl_axtls_connect(struct connectdata *conn, /* RFC2818 checks */ if(found_subject_alt_names && !found_subject_alt_name_matching_conn) { - /* Break connection ! */ - Curl_axtls_close(conn, sockindex); - failf(data, "\tsubjectAltName(s) do not match %s\n", conn->host.dispname); - return CURLE_PEER_FAILED_VERIFICATION; + if(data->set.ssl.verifyhost) { + /* Break connection ! */ + Curl_axtls_close(conn, sockindex); + failf(data, "\tsubjectAltName(s) do not match %s\n", + conn->host.dispname); + return CURLE_PEER_FAILED_VERIFICATION; + } + else + infof(data, "\tsubjectAltName(s) do not match %s\n", + conn->host.dispname); } else if(found_subject_alt_names == 0) { /* Per RFC2818, when no Subject Alt Names were available, examine the peer CN as a legacy fallback */ peer_CN = ssl_get_cert_dn(ssl, SSL_X509_CERT_COMMON_NAME); if(peer_CN == NULL) { - /* Similar behaviour to the OpenSSL interface */ - Curl_axtls_close(conn, sockindex); - failf(data, "unable to obtain common name from peer certificate"); - return CURLE_PEER_FAILED_VERIFICATION; + if(data->set.ssl.verifyhost) { + Curl_axtls_close(conn, sockindex); + failf(data, "unable to obtain common name from peer certificate"); + return CURLE_PEER_FAILED_VERIFICATION; + } + else + infof(data, "unable to obtain common name from peer certificate"); } else { if(!Curl_cert_hostcheck((const char *)peer_CN, conn->host.name)) { @@ -359,8 +373,6 @@ Curl_axtls_connect(struct connectdata *conn, /* General housekeeping */ conn->ssl[sockindex].state = ssl_connection_complete; - conn->ssl[sockindex].ssl = ssl; - conn->ssl[sockindex].ssl_ctx = ssl_ctx; conn->recv[sockindex] = axtls_recv; conn->send[sockindex] = axtls_send; @@ -374,6 +386,107 @@ Curl_axtls_connect(struct connectdata *conn, return CURLE_OK; } +/* + * Use axTLS's non-blocking connection feature to open an SSL connection. + * This is called after a TCP connection is already established. +*/ +CURLcode Curl_axtls_connect_nonblocking( + struct connectdata *conn, + int sockindex, + bool *done) +{ + CURLcode conn_step; + int ssl_fcn_return; + + *done = FALSE; + /* connectdata is calloc'd and connecting_state is only changed in this + function, so this is safe, as the state is effectively initialized. */ + if(conn->ssl[sockindex].connecting_state == ssl_connect_1) { + conn_step = connect_prep(conn, sockindex); + if(conn_step != CURLE_OK) { + Curl_axtls_close(conn, sockindex); + return conn_step; + } + conn->ssl[sockindex].connecting_state = ssl_connect_2; + } + + if(conn->ssl[sockindex].connecting_state == ssl_connect_2) { + /* Check to make sure handshake was ok. */ + if(ssl_handshake_status(conn->ssl[sockindex].ssl) != SSL_OK) { + ssl_fcn_return = ssl_read(conn->ssl[sockindex].ssl, NULL); + if(ssl_fcn_return < 0) { + Curl_axtls_close(conn, sockindex); + ssl_display_error(ssl_fcn_return); /* goes to stdout. */ + return map_error_to_curl(ssl_fcn_return); + } + else { + return CURLE_OK; /* Return control to caller for retries */ + } + } + infof (conn->data, "handshake completed successfully\n"); + conn->ssl[sockindex].connecting_state = ssl_connect_3; + } + + if(conn->ssl[sockindex].connecting_state == ssl_connect_3) { + conn_step = connect_finish(conn, sockindex); + if(conn_step != CURLE_OK) { + Curl_axtls_close(conn, sockindex); + return conn_step; + } + + /* Reset connect state */ + conn->ssl[sockindex].connecting_state = ssl_connect_1; + + *done = TRUE; + return CURLE_OK; + } + + /* Unrecognized state. Things are very bad. */ + conn->ssl[sockindex].state = ssl_connection_none; + conn->ssl[sockindex].connecting_state = ssl_connect_1; + /* Return value perhaps not strictly correct, but distinguishes the issue.*/ + return CURLE_BAD_FUNCTION_ARGUMENT; +} + + +/* + * This function is called after the TCP connect has completed. Setup the TLS + * layer and do all necessary magic for a blocking connect. + */ +CURLcode +Curl_axtls_connect(struct connectdata *conn, + int sockindex) + +{ + CURLcode conn_step = connect_prep(conn, sockindex); + int ssl_fcn_return; + SSL *ssl = conn->ssl[sockindex].ssl; + + if(conn_step != CURLE_OK) { + Curl_axtls_close(conn, sockindex); + return conn_step; + } + + /* Check to make sure handshake was ok. */ + while(ssl_handshake_status(ssl) != SSL_OK) { + ssl_fcn_return = ssl_read(ssl, NULL); + if(ssl_fcn_return < 0) { + Curl_axtls_close(conn, sockindex); + ssl_display_error(ssl_fcn_return); /* goes to stdout. */ + return map_error_to_curl(ssl_fcn_return); + } + usleep(10000); + } + infof (conn->data, "handshake completed successfully\n"); + + conn_step = connect_finish(conn, sockindex); + if(conn_step != CURLE_OK) { + Curl_axtls_close(conn, sockindex); + return conn_step; + } + + return CURLE_OK; +} /* return number of sent (non-SSL) bytes */ static ssize_t axtls_send(struct connectdata *conn, @@ -407,7 +520,7 @@ void Curl_axtls_close(struct connectdata *conn, int sockindex) struct ssl_connect_data *connssl = &conn->ssl[sockindex]; infof(conn->data, " Curl_axtls_close\n"); - if(connssl->ssl) { + /* line from ssluse.c: (void)SSL_shutdown(connssl->ssl); axTLS compat layer does nothing for SSL_shutdown */ @@ -415,13 +528,7 @@ void Curl_axtls_close(struct connectdata *conn, int sockindex) equivalent. ssl_free and ssl_ctx_free close things. SSL_set_connect_state(connssl->handle); */ - ssl_free (connssl->ssl); - connssl->ssl = NULL; - } - if(connssl->ssl_ctx) { - ssl_ctx_free (connssl->ssl_ctx); - connssl->ssl_ctx = NULL; - } + free_ssl_structs(connssl); } /* @@ -436,8 +543,7 @@ int Curl_axtls_shutdown(struct connectdata *conn, int sockindex) int retval = 0; struct ssl_connect_data *connssl = &conn->ssl[sockindex]; struct SessionHandle *data = conn->data; - char buf[120]; /* We will use this for the OpenSSL error buffer, so it has - to be at least 120 bytes long. */ + uint8_t *buf; ssize_t nread; infof(conn->data, " Curl_axtls_shutdown\n"); @@ -457,9 +563,10 @@ int Curl_axtls_shutdown(struct connectdata *conn, int sockindex) CURL_SOCKET_BAD, SSL_SHUTDOWN_TIMEOUT); if(what > 0) { /* Something to read, let's do it and hope that it is the close - notify alert from the server */ - nread = (ssize_t)SSL_read(conn->ssl[sockindex].ssl, buf, - sizeof(buf)); + notify alert from the server. buf is managed internally by + axTLS and will be released upon calling ssl_free via + free_ssl_structs. */ + nread = (ssize_t)ssl_read(connssl->ssl, &buf); if(nread < SSL_OK) { failf(data, "close notify alert not received during shutdown"); @@ -476,8 +583,7 @@ int Curl_axtls_shutdown(struct connectdata *conn, int sockindex) retval = -1; } - ssl_free (connssl->ssl); - connssl->ssl = NULL; + free_ssl_structs(connssl); } return retval; } @@ -490,26 +596,36 @@ static ssize_t axtls_recv(struct connectdata *conn, /* connection data */ { struct ssl_connect_data *connssl = &conn->ssl[num]; ssize_t ret = 0; + uint8_t *read_buf; infof(conn->data, " axtls_recv\n"); + *err = CURLE_OK; if(connssl) { - ret = (ssize_t)SSL_read(conn->ssl[num].ssl, buf, (int)buffersize); - - /* axTLS isn't terribly generous about error reporting */ - /* With patched axTLS, SSL_CLOSE_NOTIFY=-3. Hard-coding until axTLS - team approves proposed fix. */ - if(ret == -3 ) { + ret = ssl_read(connssl->ssl, &read_buf); + if(ret > SSL_OK) { + /* ssl_read returns SSL_OK if there is more data to read, so if it is + larger, then all data has been read already. */ + memcpy(buf, read_buf, + (size_t)ret > buffersize ? buffersize : (size_t)ret); + } + else if(ret == SSL_OK) { + /* more data to be read, signal caller to call again */ + *err = CURLE_AGAIN; + ret = -1; + } + else if(ret == -3) { + /* With patched axTLS, SSL_CLOSE_NOTIFY=-3. Hard-coding until axTLS + team approves proposed fix. */ Curl_axtls_close(conn, num); } - else if(ret < 0) { - failf(conn->data, "axTLS recv error (%d)", (int)ret); + else { + failf(conn->data, "axTLS recv error (%d)", ret); *err = map_error_to_curl(ret); - return -1; + ret = -1; } } - *err = CURLE_OK; return ret; } diff --git a/plugins/FTPFileYM/curl/lib/axtls.h b/plugins/FTPFileYM/curl/lib/axtls.h index 8d33b1c7a7..db91365976 100644 --- a/plugins/FTPFileYM/curl/lib/axtls.h +++ b/plugins/FTPFileYM/curl/lib/axtls.h @@ -30,6 +30,10 @@ int Curl_axtls_init(void); int Curl_axtls_cleanup(void); CURLcode Curl_axtls_connect(struct connectdata *conn, int sockindex); +CURLcode Curl_axtls_connect_nonblocking( + struct connectdata *conn, + int sockindex, + bool *done); /* tell axTLS to close down all open information regarding connections (and thus session ID caching etc) */ @@ -47,6 +51,7 @@ int Curl_axtls_check_cxn(struct connectdata *conn); #define curlssl_init Curl_axtls_init #define curlssl_cleanup Curl_axtls_cleanup #define curlssl_connect Curl_axtls_connect +#define curlssl_connect_nonblocking Curl_axtls_connect_nonblocking #define curlssl_session_free(x) Curl_axtls_session_free(x) #define curlssl_close_all Curl_axtls_close_all #define curlssl_close Curl_axtls_close diff --git a/plugins/FTPFileYM/curl/lib/config-dos.h b/plugins/FTPFileYM/curl/lib/config-dos.h index 68614e8f45..cce5e81065 100644 --- a/plugins/FTPFileYM/curl/lib/config-dos.h +++ b/plugins/FTPFileYM/curl/lib/config-dos.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2011, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2013, 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 @@ -79,7 +79,6 @@ #define HAVE_SYS_SOCKET_H 1 #define HAVE_SYS_STAT_H 1 #define HAVE_SYS_TYPES_H 1 -#define HAVE_TERMIOS_H 1 #define HAVE_TIME_H 1 #define HAVE_UNISTD_H 1 @@ -149,7 +148,7 @@ #if defined(__HIGHC__) || \ (defined(__GNUC__) && (__GNUC__ < 4)) -#define ssize_t int + #define ssize_t int #endif #define CURL_CA_BUNDLE getenv("CURL_CA_BUNDLE") @@ -162,12 +161,9 @@ #define HAVE_SIGACTION 1 #define HAVE_SIGSETJMP 1 #define HAVE_SYS_TIME_H 1 + #define HAVE_TERMIOS_H 1 #define HAVE_VARIADIC_MACROS_GCC 1 - #if (DJGPP_MINOR >= 4) - #define HAVE_STRLCAT 1 - #endif - /* Because djgpp <= 2.03 doesn't have snprintf() etc. */ #if (DJGPP_MINOR < 4) #define _MPRINTF_REPLACE @@ -178,11 +174,11 @@ #elif defined(__HIGHC__) #define HAVE_SYS_TIME_H 1 + #define strerror(e) strerror_s_((e)) #endif #ifdef MSDOS /* Watt-32 */ - #define HAVE_CLOSESOCKET_CAMEL 1 - #define CloseSocket(s) close_s((s)) + #define HAVE_CLOSE_S 1 #endif #undef word diff --git a/plugins/FTPFileYM/curl/lib/config-os400.h b/plugins/FTPFileYM/curl/lib/config-os400.h index 208c029036..a290fe43d7 100644 --- a/plugins/FTPFileYM/curl/lib/config-os400.h +++ b/plugins/FTPFileYM/curl/lib/config-os400.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2012, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2013, 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 @@ -277,30 +277,33 @@ /* Define if you have the <stdlib.h> header file. */ #define HAVE_STDLIB_H + +/* The following define is needed on OS400 to enable strcmpi(), stricmp() and + strdup(). */ +#define __cplusplus__strings__ + /* Define if you have the `strcasecmp' function. */ #undef HAVE_STRCASECMP /* Define if you have the `strcmpi' function. */ -#undef HAVE_STRCMPI +#define HAVE_STRCMPI + +/* Define if you have the `stricmp' function. */ +#define HAVE_STRICMP /* Define if you have the `strdup' function. */ -#undef HAVE_STRDUP +#define HAVE_STRDUP + /* Define if you have the `strftime' function. */ #define HAVE_STRFTIME -/* Define if you have the `stricmp' function. */ -#undef HAVE_STRICMP - /* Define if you have the <strings.h> header file. */ #define HAVE_STRINGS_H /* Define if you have the <string.h> header file. */ #define HAVE_STRING_H -/* Define if you have the `strlcat' function. */ -#undef HAVE_STRLCAT - /* Define if you have the `strlcpy' function. */ #undef HAVE_STRLCPY @@ -528,6 +531,9 @@ /* Define to use the QsoSSL package. */ #define USE_QSOSSL +/* Define to use the GSKit package. */ +#undef USE_GSKIT + /* Use the system keyring as the default CA bundle. */ #define CURL_CA_BUNDLE "/QIBM/UserData/ICSS/Cert/Server/DEFAULT.KDB" diff --git a/plugins/FTPFileYM/curl/lib/config-riscos.h b/plugins/FTPFileYM/curl/lib/config-riscos.h index e2af9af6c0..e400577895 100644 --- a/plugins/FTPFileYM/curl/lib/config-riscos.h +++ b/plugins/FTPFileYM/curl/lib/config-riscos.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2012, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2013, 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 @@ -305,9 +305,6 @@ /* Define if you have the <string.h> header file. */ #define HAVE_STRING_H -/* Define if you have the `strlcat' function. */ -#undef HAVE_STRLCAT - /* Define if you have the `strlcpy' function. */ #undef HAVE_STRLCPY diff --git a/plugins/FTPFileYM/curl/lib/config-symbian.h b/plugins/FTPFileYM/curl/lib/config-symbian.h index fcfb4058c5..17d92b0619 100644 --- a/plugins/FTPFileYM/curl/lib/config-symbian.h +++ b/plugins/FTPFileYM/curl/lib/config-symbian.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2012, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2013, 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 @@ -504,9 +504,6 @@ /* Define to 1 if you have the `strcasecmp' function. */ #define HAVE_STRCASECMP 1 -/* Define to 1 if you have the `strcasestr' function. */ -#define HAVE_STRCASESTR 1 - /* Define to 1 if you have the `strcmpi' function. */ /* #undef HAVE_STRCMPI */ @@ -525,9 +522,6 @@ /* Define to 1 if you have the <string.h> header file. */ #define HAVE_STRING_H 1 -/* Define to 1 if you have the `strlcat' function. */ -#define HAVE_STRLCAT 1 - /* Define to 1 if you have the `strlcpy' function. */ #define HAVE_STRLCPY 1 diff --git a/plugins/FTPFileYM/curl/lib/config-tpf.h b/plugins/FTPFileYM/curl/lib/config-tpf.h index 0208ab835f..ddb8f778c2 100644 --- a/plugins/FTPFileYM/curl/lib/config-tpf.h +++ b/plugins/FTPFileYM/curl/lib/config-tpf.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2012, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2013, 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 @@ -471,9 +471,6 @@ /* Define to 1 if you have the <string.h> header file. */ #define HAVE_STRING_H 1 -/* Define to 1 if you have the `strlcat' function. */ -/* #undef HAVE_STRLCAT */ - /* Define to 1 if you have the `strlcpy' function. */ /* #undef HAVE_STRLCPY */ diff --git a/plugins/FTPFileYM/curl/lib/config-vxworks.h b/plugins/FTPFileYM/curl/lib/config-vxworks.h index 5b224c0453..c94534afb8 100644 --- a/plugins/FTPFileYM/curl/lib/config-vxworks.h +++ b/plugins/FTPFileYM/curl/lib/config-vxworks.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2012, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2013, 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 @@ -571,9 +571,6 @@ /* Define to 1 if you have the strcasecmp function. */ #define HAVE_STRCASECMP 1 -/* Define to 1 if you have the strcasestr function. */ -/* #undef HAVE_STRCASESTR */ - /* Define to 1 if you have the strcmpi function. */ /* #undef HAVE_STRCMPI */ @@ -592,9 +589,6 @@ /* Define to 1 if you have the <string.h> header file. */ #define HAVE_STRING_H 1 -/* Define to 1 if you have the strlcat function. */ -/* #undef HAVE_STRLCAT */ - /* Define to 1 if you have the `strlcpy' function. */ /* #undef HAVE_STRLCPY */ diff --git a/plugins/FTPFileYM/curl/lib/config-win32.h b/plugins/FTPFileYM/curl/lib/config-win32.h index cfbca9ca38..59ba4e538f 100644 --- a/plugins/FTPFileYM/curl/lib/config-win32.h +++ b/plugins/FTPFileYM/curl/lib/config-win32.h @@ -391,21 +391,6 @@ #endif /* ---------------------------------------------------------------- */ -/* STRUCT RELATED */ -/* ---------------------------------------------------------------- */ - -/* Define if you have struct sockaddr_storage. */ -#if !defined(__SALFORDC__) && !defined(__BORLANDC__) -#define HAVE_STRUCT_SOCKADDR_STORAGE 1 -#endif - -/* Define if you have struct timeval. */ -#define HAVE_STRUCT_TIMEVAL 1 - -/* Define if struct sockaddr_in6 has the sin6_scope_id member. */ -#define HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID 1 - -/* ---------------------------------------------------------------- */ /* BSD-style lwIP TCP/IP stack SPECIFIC */ /* ---------------------------------------------------------------- */ @@ -573,6 +558,25 @@ #endif /* ---------------------------------------------------------------- */ +/* STRUCT RELATED */ +/* ---------------------------------------------------------------- */ + +/* Define if you have struct sockaddr_storage. */ +#if !defined(__SALFORDC__) && !defined(__BORLANDC__) +#define HAVE_STRUCT_SOCKADDR_STORAGE 1 +#endif + +/* Define if you have struct timeval. */ +#define HAVE_STRUCT_TIMEVAL 1 + +/* Define if struct sockaddr_in6 has the sin6_scope_id member. */ +#define HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID 1 + +#if HAVE_WINSOCK2_H && defined(_WIN32_WINNT) && (_WIN32_WINNT >= 0x0600) +#define HAVE_STRUCT_POLLFD 1 +#endif + +/* ---------------------------------------------------------------- */ /* LARGE FILE SUPPORT */ /* ---------------------------------------------------------------- */ @@ -611,8 +615,11 @@ /* Define to enable c-ares asynchronous DNS lookups. */ /* #define USE_ARES 1 */ -/* Define to enable threaded asynchronous DNS lookups. */ -#define USE_THREADS_WIN32 1 +/* Default define to enable threaded asynchronous DNS lookups. */ +#if !defined(USE_SYNC_DNS) && !defined(USE_ARES) && \ + !defined(USE_THREADS_WIN32) +# define USE_THREADS_WIN32 1 +#endif #if defined(USE_ARES) && defined(USE_THREADS_WIN32) # error "Only one DNS lookup specialty may be defined at most" diff --git a/plugins/FTPFileYM/curl/lib/conncache.c b/plugins/FTPFileYM/curl/lib/conncache.c index 530cdc2ec3..48271f7510 100644 --- a/plugins/FTPFileYM/curl/lib/conncache.c +++ b/plugins/FTPFileYM/curl/lib/conncache.c @@ -38,8 +38,6 @@ /* The last #include file should be: */ #include "memdebug.h" -#define CONNECTION_HASH_SIZE 97 - static void free_bundle_hash_entry(void *freethis) { struct connectbundle *b = (struct connectbundle *) freethis; @@ -47,7 +45,7 @@ static void free_bundle_hash_entry(void *freethis) Curl_bundle_destroy(b); } -struct conncache *Curl_conncache_init(void) +struct conncache *Curl_conncache_init(int size) { struct conncache *connc; @@ -55,7 +53,7 @@ struct conncache *Curl_conncache_init(void) if(!connc) return NULL; - connc->hash = Curl_hash_alloc(CONNECTION_HASH_SIZE, Curl_hash_str, + connc->hash = Curl_hash_alloc(size, Curl_hash_str, Curl_str_key_compare, free_bundle_hash_entry); if(!connc->hash) { diff --git a/plugins/FTPFileYM/curl/lib/conncache.h b/plugins/FTPFileYM/curl/lib/conncache.h index fad17d8f7a..f5e41f187b 100644 --- a/plugins/FTPFileYM/curl/lib/conncache.h +++ b/plugins/FTPFileYM/curl/lib/conncache.h @@ -27,7 +27,7 @@ struct conncache { size_t num_connections; }; -struct conncache *Curl_conncache_init(void); +struct conncache *Curl_conncache_init(int size); void Curl_conncache_destroy(struct conncache *connc); diff --git a/plugins/FTPFileYM/curl/lib/connect.c b/plugins/FTPFileYM/curl/lib/connect.c index 0afb1ee616..2b5719d123 100644 --- a/plugins/FTPFileYM/curl/lib/connect.c +++ b/plugins/FTPFileYM/curl/lib/connect.c @@ -87,13 +87,23 @@ static bool verifyconnect(curl_socket_t sockfd, int *error); -#ifdef __DragonFly__ -/* DragonFlyBSD uses millisecond as KEEPIDLE and KEEPINTVL units */ +#if defined(__DragonFly__) || defined(HAVE_WINSOCK_H) +/* DragonFlyBSD and Windows use millisecond units */ #define KEEPALIVE_FACTOR(x) (x *= 1000) #else #define KEEPALIVE_FACTOR(x) #endif +#if defined(HAVE_WINSOCK_H) && !defined(SIO_KEEPALIVE_VALS) +#define SIO_KEEPALIVE_VALS _WSAIOW(IOC_VENDOR,4) + +struct tcp_keepalive { + u_long onoff; + u_long keepalivetime; + u_long keepaliveinterval; +}; +#endif + static void tcpkeepalive(struct SessionHandle *data, curl_socket_t sockfd) @@ -106,6 +116,22 @@ tcpkeepalive(struct SessionHandle *data, infof(data, "Failed to set SO_KEEPALIVE on fd %d\n", sockfd); } else { +#if defined(SIO_KEEPALIVE_VALS) + struct tcp_keepalive vals; + DWORD dummy; + vals.onoff = 1; + optval = curlx_sltosi(data->set.tcp_keepidle); + KEEPALIVE_FACTOR(optval); + vals.keepalivetime = optval; + optval = curlx_sltosi(data->set.tcp_keepintvl); + KEEPALIVE_FACTOR(optval); + vals.keepaliveinterval = optval; + if(WSAIoctl(sockfd, SIO_KEEPALIVE_VALS, (LPVOID) &vals, sizeof(vals), + NULL, 0, &dummy, NULL, NULL) != 0) { + infof(data, "Failed to set SIO_KEEPALIVE_VALS on fd %d: %d\n", + (int)sockfd, WSAGetLastError()); + } +#else #ifdef TCP_KEEPIDLE optval = curlx_sltosi(data->set.tcp_keepidle); KEEPALIVE_FACTOR(optval); @@ -122,6 +148,16 @@ tcpkeepalive(struct SessionHandle *data, infof(data, "Failed to set TCP_KEEPINTVL on fd %d\n", sockfd); } #endif +#ifdef TCP_KEEPALIVE + /* Mac OS X style */ + optval = curlx_sltosi(data->set.tcp_keepidle); + KEEPALIVE_FACTOR(optval); + if(setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPALIVE, + (void *)&optval, sizeof(optval)) < 0) { + infof(data, "Failed to set TCP_KEEPALIVE on fd %d\n", sockfd); + } +#endif +#endif } } @@ -283,41 +319,54 @@ static CURLcode bindlocal(struct connectdata *conn, } /* interface */ - if(!is_host && (is_interface || Curl_if_is_interface_name(dev))) { - if(Curl_if2ip(af, dev, myhost, sizeof(myhost)) == NULL) - return CURLE_INTERFACE_FAILED; - - /* - * We now have the numerical IP address in the 'myhost' buffer - */ - infof(data, "Local Interface %s is ip %s using address family %i\n", - dev, myhost, af); - done = 1; + if(!is_host) { + switch(Curl_if2ip(af, conn->scope, dev, myhost, sizeof(myhost))) { + case IF2IP_NOT_FOUND: + if(is_interface) { + /* Do not fall back to treating it as a host name */ + failf(data, "Couldn't bind to interface '%s'", dev); + return CURLE_INTERFACE_FAILED; + } + break; + case IF2IP_AF_NOT_SUPPORTED: + /* Signal the caller to try another address family if available */ + return CURLE_UNSUPPORTED_PROTOCOL; + case IF2IP_FOUND: + is_interface = TRUE; + /* + * We now have the numerical IP address in the 'myhost' buffer + */ + infof(data, "Local Interface %s is ip %s using address family %i\n", + dev, myhost, af); + done = 1; #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. - */ - if(setsockopt(sockfd, SOL_SOCKET, SO_BINDTODEVICE, - dev, (curl_socklen_t)strlen(dev)+1) != 0) { - error = SOCKERRNO; - infof(data, "SO_BINDTODEVICE %s failed with errno %d: %s;" - " will do regular bind\n", - dev, error, Curl_strerror(conn, error)); - /* This is typically "errno 1, error: Operation not permitted" if - you're not running as root or another suitable privileged user */ - } + /* 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. + */ + if(setsockopt(sockfd, SOL_SOCKET, SO_BINDTODEVICE, + dev, (curl_socklen_t)strlen(dev)+1) != 0) { + error = SOCKERRNO; + infof(data, "SO_BINDTODEVICE %s failed with errno %d: %s;" + " will do regular bind\n", + dev, error, Curl_strerror(conn, error)); + /* This is typically "errno 1, error: Operation not permitted" if + you're not running as root or another suitable privileged + user */ + } #endif + break; + } } - else { + if(!is_interface) { /* * This was not an interface, resolve the name as a host name * or IP number @@ -361,10 +410,24 @@ static CURLcode bindlocal(struct connectdata *conn, if(done > 0) { #ifdef ENABLE_IPV6 /* ipv6 address */ - if((af == AF_INET6) && - (Curl_inet_pton(AF_INET6, myhost, &si6->sin6_addr) > 0)) { - si6->sin6_family = AF_INET6; - si6->sin6_port = htons(port); + if(af == AF_INET6) { +#ifdef HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID + char *scope_ptr = strchr(myhost, '%'); + if(scope_ptr) + *(scope_ptr++) = 0; +#endif + if(Curl_inet_pton(AF_INET6, myhost, &si6->sin6_addr) > 0) { + si6->sin6_family = AF_INET6; + si6->sin6_port = htons(port); +#ifdef HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID + if(scope_ptr) + /* The "myhost" string either comes from Curl_if2ip or from + Curl_printable_address. The latter returns only numeric scope + IDs and the former returns none at all. So the scope ID, if + present, is known to be numeric */ + si6->sin6_scope_id = atoi(scope_ptr); +#endif + } sizeof_sa = sizeof(struct sockaddr_in6); } else @@ -825,13 +888,35 @@ static void nosigpipe(struct connectdata *conn, Work-around: Make the Socket Send Buffer Size Larger Than the Program Send Buffer Size + The problem described in this knowledge-base is applied only to pre-Vista + Windows. Following function trying to detect OS version and skips + SO_SNDBUF adjustment for Windows Vista and above. */ +#define DETECT_OS_NONE 0 +#define DETECT_OS_PREVISTA 1 +#define DETECT_OS_VISTA_OR_LATER 2 + void Curl_sndbufset(curl_socket_t sockfd) { int val = CURL_MAX_WRITE_SIZE + 32; int curval = 0; int curlen = sizeof(curval); + OSVERSIONINFO osver; + static int detectOsState = DETECT_OS_NONE; + + if(detectOsState == DETECT_OS_NONE) { + memset(&osver, 0, sizeof(osver)); + osver.dwOSVersionInfoSize = sizeof(osver); + detectOsState = DETECT_OS_PREVISTA; + if(GetVersionEx(&osver)) { + if(osver.dwMajorVersion >= 6) + detectOsState = DETECT_OS_VISTA_OR_LATER; + } + } + if(detectOsState == DETECT_OS_VISTA_OR_LATER) + return; + if(getsockopt(sockfd, SOL_SOCKET, SO_SNDBUF, (char *)&curval, &curlen) == 0) if(curval > val) return; @@ -917,6 +1002,11 @@ singleipconnect(struct connectdata *conn, res = bindlocal(conn, sockfd, addr.family); if(res) { Curl_closesocket(conn, sockfd); /* close socket and bail out */ + if(res == CURLE_UNSUPPORTED_PROTOCOL) { + /* The address family is not supported on this interface. + We can continue trying addresses */ + return CURLE_OK; + } return res; } @@ -1054,7 +1144,7 @@ CURLcode Curl_connecthost(struct connectdata *conn, /* context */ if(sockfd == CURL_SOCKET_BAD) { /* no good connect was made */ - failf(data, "couldn't connect to %s at %s:%d", + failf(data, "couldn't connect to %s at %s:%ld", conn->bits.proxy?"proxy":"host", conn->bits.proxy?conn->proxy.name:conn->host.name, conn->port); return CURLE_COULDNT_CONNECT; @@ -1149,7 +1239,7 @@ curl_socket_t Curl_getconnectinfo(struct SessionHandle *data, * 'conn' can be NULL, beware! */ int Curl_closesocket(struct connectdata *conn, - curl_socket_t sock) + curl_socket_t sock) { if(conn && conn->fclosesocket) { if((sock == conn->sock[SECONDARYSOCKET]) && @@ -1161,7 +1251,13 @@ int Curl_closesocket(struct connectdata *conn, else return conn->fclosesocket(conn->closesocket_client, sock); } - return sclose(sock); + sclose(sock); + + if(conn) + /* tell the multi-socket code about this */ + Curl_multi_closed(conn, sock); + + return 0; } /* diff --git a/plugins/FTPFileYM/curl/lib/cookie.c b/plugins/FTPFileYM/curl/lib/cookie.c index 18b91559ca..9deeb1ac49 100644 --- a/plugins/FTPFileYM/curl/lib/cookie.c +++ b/plugins/FTPFileYM/curl/lib/cookie.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2012, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2013, 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 @@ -89,6 +89,7 @@ Example set of cookies: #include "strequal.h" #include "strtok.h" #include "sendf.h" +#include "slist.h" #include "curl_memory.h" #include "share.h" #include "strtoofft.h" @@ -106,6 +107,8 @@ static void freecookie(struct Cookie *co) free(co->domain); if(co->path) free(co->path); + if(co->spath) + free(co->spath); if(co->name) free(co->name); if(co->value) @@ -118,15 +121,139 @@ static void freecookie(struct Cookie *co) free(co); } -static bool tailmatch(const char *little, const char *bigone) +static bool tailmatch(const char *cooke_domain, const char *hostname) { - size_t littlelen = strlen(little); - size_t biglen = strlen(bigone); + size_t cookie_domain_len = strlen(cooke_domain); + size_t hostname_len = strlen(hostname); - if(littlelen > biglen) + if(hostname_len < cookie_domain_len) return FALSE; - return Curl_raw_equal(little, bigone+biglen-littlelen) ? TRUE : FALSE; + if(!Curl_raw_equal(cooke_domain, hostname+hostname_len-cookie_domain_len)) + return FALSE; + + /* A lead char of cookie_domain is not '.'. + RFC6265 4.1.2.3. The Domain Attribute says: + For example, if the value of the Domain attribute is + "example.com", the user agent will include the cookie in the Cookie + header when making HTTP requests to example.com, www.example.com, and + www.corp.example.com. + */ + if(hostname_len == cookie_domain_len) + return TRUE; + if('.' == *(hostname + hostname_len - cookie_domain_len - 1)) + return TRUE; + return FALSE; +} + +/* + * matching cookie path and url path + * RFC6265 5.1.4 Paths and Path-Match + */ +static bool pathmatch(const char* cookie_path, const char* request_uri) +{ + size_t cookie_path_len; + size_t uri_path_len; + char* uri_path = NULL; + char* pos; + bool ret = FALSE; + + /* cookie_path must not have last '/' separator. ex: /sample */ + cookie_path_len = strlen(cookie_path); + if(1 == cookie_path_len) { + /* cookie_path must be '/' */ + return TRUE; + } + + uri_path = strdup(request_uri); + if(!uri_path) + return FALSE; + pos = strchr(uri_path, '?'); + if(pos) + *pos = 0x0; + + /* #-fragments are already cut off! */ + if(0 == strlen(uri_path) || uri_path[0] != '/') { + free(uri_path); + uri_path = strdup("/"); + if(!uri_path) + return FALSE; + } + + /* here, RFC6265 5.1.4 says + 4. Output the characters of the uri-path from the first character up + to, but not including, the right-most %x2F ("/"). + but URL path /hoge?fuga=xxx means /hoge/index.cgi?fuga=xxx in some site + without redirect. + Ignore this algorithm because /hoge is uri path for this case + (uri path is not /). + */ + + uri_path_len = strlen(uri_path); + + if(uri_path_len < cookie_path_len) { + ret = FALSE; + goto pathmatched; + } + + /* not using checkprefix() because matching should be case-sensitive */ + if(strncmp(cookie_path, uri_path, cookie_path_len)) { + ret = FALSE; + goto pathmatched; + } + + /* The cookie-path and the uri-path are identical. */ + if(cookie_path_len == uri_path_len) { + ret = TRUE; + goto pathmatched; + } + + /* here, cookie_path_len < url_path_len */ + if(uri_path[cookie_path_len] == '/') { + ret = TRUE; + goto pathmatched; + } + + ret = FALSE; + +pathmatched: + free(uri_path); + return ret; +} + +/* + * cookie path sanitize + */ +static char *sanitize_cookie_path(const char *cookie_path) +{ + size_t len; + char *new_path = strdup(cookie_path); + if(!new_path) + return NULL; + + /* some stupid site sends path attribute with '"'. */ + if(new_path[0] == '\"') { + memmove((void *)new_path, (const void *)(new_path + 1), strlen(new_path)); + } + if(new_path[strlen(new_path) - 1] == '\"') { + new_path[strlen(new_path) - 1] = 0x0; + } + + /* RFC6265 5.2.4 The Path Attribute */ + if(new_path[0] != '/') { + /* Let cookie-path be the default-path. */ + free(new_path); + new_path = strdup("/"); + return new_path; + } + + /* convert /hoge/ to /hoge */ + len = strlen(new_path); + if(1 < len && new_path[len - 1] == '/') { + new_path[len - 1] = 0x0; + } + + return new_path; } /* @@ -163,6 +290,34 @@ static void strstore(char **str, const char *newstr) *str = strdup(newstr); } +/* + * remove_expired() removes expired cookies. + */ +static void remove_expired(struct CookieInfo *cookies) +{ + struct Cookie *co, *nx, *pv; + curl_off_t now = (curl_off_t)time(NULL); + + co = cookies->cookies; + pv = NULL; + while(co) { + nx = co->next; + if((co->expirestr || co->maxage) && co->expires < now) { + if(co == cookies->cookies) { + cookies->cookies = co->next; + } + else { + pv->next = co->next; + } + cookies->numcookies--; + freecookie(co); + } + else { + pv = co; + } + co = nx; + } +} /**************************************************************************** * @@ -170,6 +325,9 @@ static void strstore(char **str, const char *newstr) * * Add a single cookie line to the cookie keeping object. * + * Be aware that sometimes we get an IP-only host name, and that might also be + * a numerical IPv6 address. + * ***************************************************************************/ struct Cookie * @@ -274,72 +432,39 @@ Curl_cookie_add(struct SessionHandle *data, badcookie = TRUE; /* out of memory bad */ break; } + co->spath = sanitize_cookie_path(co->path); + if(!co->spath) { + badcookie = TRUE; /* out of memory bad */ + break; + } } else if(Curl_raw_equal("domain", name)) { - /* note that this name may or may not have a preceding dot, but - we don't care about that, we treat the names the same anyway */ - - const char *domptr=whatptr; - const char *nextptr; - int dotcount=1; - - /* Count the dots, we need to make sure that there are enough - of them. */ + /* Now, we make sure that our host is within the given domain, + or the given domain is not valid and thus cannot be set. */ if('.' == whatptr[0]) - /* don't count the initial dot, assume it */ - domptr++; - - do { - nextptr = strchr(domptr, '.'); - if(nextptr) { - if(domptr != nextptr) - dotcount++; - domptr = nextptr+1; + whatptr++; /* ignore preceding dot */ + + if(!domain || tailmatch(whatptr, domain)) { + const char *tailptr=whatptr; + if(tailptr[0] == '.') + tailptr++; + strstore(&co->domain, tailptr); /* don't prefix w/dots + internally */ + if(!co->domain) { + badcookie = TRUE; + break; } - } while(nextptr); - - /* The original Netscape cookie spec defined that this domain name - MUST have three dots (or two if one of the seven holy TLDs), - but it seems that these kinds of cookies are in use "out there" - so we cannot be that strict. I've therefore lowered the check - to not allow less than two dots. */ - - if(dotcount < 2) { - /* Received and skipped a cookie with a domain using too few - dots. */ - badcookie=TRUE; /* mark this as a bad cookie */ - infof(data, "skipped cookie with illegal dotcount domain: %s\n", - whatptr); + co->tailmatch=TRUE; /* we always do that if the domain name was + given */ } else { - /* Now, we make sure that our host is within the given domain, - or the given domain is not valid and thus cannot be set. */ - - if('.' == whatptr[0]) - whatptr++; /* ignore preceding dot */ - - if(!domain || tailmatch(whatptr, domain)) { - const char *tailptr=whatptr; - if(tailptr[0] == '.') - tailptr++; - strstore(&co->domain, tailptr); /* don't prefix w/dots - internally */ - if(!co->domain) { - badcookie = TRUE; - break; - } - co->tailmatch=TRUE; /* we always do that if the domain name was - given */ - } - else { - /* we did not get a tailmatch and then the attempted set domain - is not a domain to which the current host belongs. Mark as - bad. */ - badcookie=TRUE; - infof(data, "skipped cookie with bad tailmatch domain: %s\n", - whatptr); - } + /* we did not get a tailmatch and then the attempted set domain + is not a domain to which the current host belongs. Mark as + bad. */ + badcookie=TRUE; + infof(data, "skipped cookie with bad tailmatch domain: %s\n", + whatptr); } } else if(Curl_raw_equal("version", name)) { @@ -447,6 +572,9 @@ Curl_cookie_add(struct SessionHandle *data, if(co->path) { memcpy(co->path, path, pathlen); co->path[pathlen]=0; /* zero terminate */ + co->spath = sanitize_cookie_path(co->path); + if(!co->spath) + badcookie = TRUE; /* out of memory bad */ } else badcookie = TRUE; @@ -498,12 +626,6 @@ Curl_cookie_add(struct SessionHandle *data, firstptr=strtok_r(lineptr, "\t", &tok_buf); /* tokenize it on the TAB */ - /* Here's a quick check to eliminate normal HTTP-headers from this */ - if(!firstptr || strchr(firstptr, ':')) { - free(co); - return NULL; - } - /* Now loop through the fields and init the struct we already have allocated */ for(ptr=firstptr, fields=0; ptr && !badcookie; @@ -538,12 +660,21 @@ Curl_cookie_add(struct SessionHandle *data, co->path = strdup(ptr); if(!co->path) badcookie = TRUE; + else { + co->spath = sanitize_cookie_path(co->path); + if(!co->spath) { + badcookie = TRUE; /* out of memory bad */ + } + } break; } /* this doesn't look like a path, make one up! */ co->path = strdup("/"); if(!co->path) badcookie = TRUE; + co->spath = strdup("/"); + if(!co->spath) + badcookie = TRUE; fields++; /* add a field and fall down to secure */ /* FALLTHROUGH */ case 3: @@ -597,6 +728,9 @@ Curl_cookie_add(struct SessionHandle *data, superceeds an already existing cookie, which it may if the previous have the same domain and path as this */ + /* at first, remove expired cookies */ + remove_expired(c); + clist = c->cookies; replace_old = FALSE; while(clist) { @@ -614,14 +748,14 @@ Curl_cookie_add(struct SessionHandle *data, if(replace_old) { /* the domains were identical */ - if(clist->path && co->path) { - if(Curl_raw_equal(clist->path, co->path)) { + if(clist->spath && co->spath) { + if(Curl_raw_equal(clist->spath, co->spath)) { replace_old = TRUE; } else replace_old = FALSE; } - else if(!clist->path && !co->path) + else if(!clist->spath && !co->spath) replace_old = TRUE; else replace_old = FALSE; @@ -650,6 +784,8 @@ Curl_cookie_add(struct SessionHandle *data, free(clist->domain); if(clist->path) free(clist->path); + if(clist->spath) + free(clist->spath); if(clist->expirestr) free(clist->expirestr); @@ -689,9 +825,9 @@ Curl_cookie_add(struct SessionHandle *data, lastc->next = co; else c->cookies = co; + c->numcookies++; /* one more cookie in the jar */ } - c->numcookies++; /* one more cookie in the jar */ return co; } @@ -777,11 +913,28 @@ static int cookie_sort(const void *p1, const void *p2) { struct Cookie *c1 = *(struct Cookie **)p1; struct Cookie *c2 = *(struct Cookie **)p2; + size_t l1, l2; + + /* 1 - compare cookie path lengths */ + l1 = c1->path ? strlen(c1->path) : 0; + l2 = c2->path ? strlen(c2->path) : 0; + + if(l1 != l2) + return (l2 > l1) ? 1 : -1 ; /* avoid size_t <=> int conversions */ + + /* 2 - compare cookie domain lengths */ + l1 = c1->domain ? strlen(c1->domain) : 0; + l2 = c2->domain ? strlen(c2->domain) : 0; - size_t l1 = c1->path?strlen(c1->path):0; - size_t l2 = c2->path?strlen(c2->path):0; + if(l1 != l2) + return (l2 > l1) ? 1 : -1 ; /* avoid size_t <=> int conversions */ - return (l2 > l1) ? 1 : (l2 < l1) ? -1 : 0 ; + /* 3 - compare cookie names */ + if(c1->name && c2->name) + return strcmp(c1->name, c2->name); + + /* sorry, can't be more deterministic */ + return 0; } /***************************************************************************** @@ -809,6 +962,9 @@ struct Cookie *Curl_cookie_getlist(struct CookieInfo *c, if(!c || !c->cookies) return NULL; /* no cookie struct or no cookies in the struct */ + /* at first, remove expired cookies */ + remove_expired(c); + co = c->cookies; while(co) { @@ -827,10 +983,7 @@ struct Cookie *Curl_cookie_getlist(struct CookieInfo *c, /* now check the left part of the path with the cookies path requirement */ - if(!co->path || - /* not using checkprefix() because matching should be - case-sensitive */ - !strncmp(co->path, path, strlen(co->path)) ) { + if(!co->spath || pathmatch(co->spath, path) ) { /* and now, we know this is a match and we should create an entry for the return-linked-list */ @@ -1054,6 +1207,9 @@ static int cookie_output(struct CookieInfo *c, const char *dumphere) destination file */ return 0; + /* at first, remove expired cookies */ + remove_expired(c); + if(strequal("-", dumphere)) { /* use stdout */ out = stdout; @@ -1114,9 +1270,9 @@ struct curl_slist *Curl_cookie_list(struct SessionHandle *data) curl_slist_free_all(list); return NULL; } - beg = curl_slist_append(list, line); - free(line); + beg = Curl_slist_append_nodup(list, line); if(!beg) { + free(line); curl_slist_free_all(list); return NULL; } diff --git a/plugins/FTPFileYM/curl/lib/cookie.h b/plugins/FTPFileYM/curl/lib/cookie.h index d3b63f780f..bd890827c7 100644 --- a/plugins/FTPFileYM/curl/lib/cookie.h +++ b/plugins/FTPFileYM/curl/lib/cookie.h @@ -29,7 +29,8 @@ struct Cookie { struct Cookie *next; /* next in the chain */ char *name; /* <this> = value */ char *value; /* name = <this> */ - char *path; /* path = <this> */ + char *path; /* path = <this> which is in Set-Cookie: */ + char *spath; /* sanitized cookie path */ char *domain; /* domain = <this> */ curl_off_t expires; /* expires = <this> */ char *expirestr; /* the plain text version */ diff --git a/plugins/FTPFileYM/curl/lib/curl_darwinssl.c b/plugins/FTPFileYM/curl/lib/curl_darwinssl.c index 827c876e0e..43fe053369 100644 --- a/plugins/FTPFileYM/curl/lib/curl_darwinssl.c +++ b/plugins/FTPFileYM/curl/lib/curl_darwinssl.c @@ -38,9 +38,58 @@ #include <Security/SecureTransport.h> #include <CoreFoundation/CoreFoundation.h> #include <CommonCrypto/CommonDigest.h> + +/* The Security framework has changed greatly between iOS and different OS X + versions, and we will try to support as many of them as we can (back to + Leopard and iOS 5) by using macros and weak-linking. + + IMPORTANT: If TLS 1.1 and 1.2 support are important for you on OS X, then + you must build this project against the 10.8 SDK or later. */ #if (TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE)) + +#if MAC_OS_X_VERSION_MAX_ALLOWED < 1050 +#error "The darwinssl back-end requires Leopard or later." +#endif /* MAC_OS_X_VERSION_MAX_ALLOWED < 1050 */ + +#define CURL_BUILD_IOS 0 +#define CURL_BUILD_IOS_7 0 +#define CURL_BUILD_MAC 1 +/* This is the maximum API level we are allowed to use when building: */ +#define CURL_BUILD_MAC_10_5 MAC_OS_X_VERSION_MAX_ALLOWED >= 1050 +#define CURL_BUILD_MAC_10_6 MAC_OS_X_VERSION_MAX_ALLOWED >= 1060 +#define CURL_BUILD_MAC_10_7 MAC_OS_X_VERSION_MAX_ALLOWED >= 1070 +#define CURL_BUILD_MAC_10_8 MAC_OS_X_VERSION_MAX_ALLOWED >= 1080 +#define CURL_BUILD_MAC_10_9 MAC_OS_X_VERSION_MAX_ALLOWED >= 1090 +/* These macros mean "the following code is present to allow runtime backward + compatibility with at least this cat or earlier": + (You set this at build-time by setting the MACOSX_DEPLOYMENT_TARGET + environmental variable.) */ +#define CURL_SUPPORT_MAC_10_5 MAC_OS_X_VERSION_MIN_REQUIRED <= 1050 +#define CURL_SUPPORT_MAC_10_6 MAC_OS_X_VERSION_MIN_REQUIRED <= 1060 +#define CURL_SUPPORT_MAC_10_7 MAC_OS_X_VERSION_MIN_REQUIRED <= 1070 +#define CURL_SUPPORT_MAC_10_8 MAC_OS_X_VERSION_MIN_REQUIRED <= 1080 +#define CURL_SUPPORT_MAC_10_9 MAC_OS_X_VERSION_MIN_REQUIRED <= 1090 + +#elif TARGET_OS_EMBEDDED || TARGET_OS_IPHONE +#define CURL_BUILD_IOS 1 +#define CURL_BUILD_IOS_7 __IPHONE_OS_VERSION_MAX_ALLOWED >= 70000 +#define CURL_BUILD_MAC 0 +#define CURL_BUILD_MAC_10_5 0 +#define CURL_BUILD_MAC_10_6 0 +#define CURL_BUILD_MAC_10_7 0 +#define CURL_BUILD_MAC_10_8 0 +#define CURL_SUPPORT_MAC_10_5 0 +#define CURL_SUPPORT_MAC_10_6 0 +#define CURL_SUPPORT_MAC_10_7 0 +#define CURL_SUPPORT_MAC_10_8 0 + +#else +#error "The darwinssl back-end requires iOS or OS X." +#endif /* (TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE)) */ + +#if CURL_BUILD_MAC #include <sys/sysctl.h> -#endif +#endif /* CURL_BUILD_MAC */ #include "urldata.h" #include "sendf.h" @@ -59,16 +108,7 @@ /* From MacTypes.h (which we can't include because it isn't present in iOS: */ #define ioErr -36 - -/* In Mountain Lion and iOS 5, Apple made some changes to the API. They - added TLS 1.1 and 1.2 support, and deprecated and replaced some - functions. You need to build against the Mountain Lion or iOS 5 SDK - or later to get TLS 1.1 or 1.2 support working in cURL. We'll weak-link - to the newer functions and use them if present in the user's OS. - - Builders: If you want TLS 1.1 and 1.2 but still want to retain support - for older cats, don't forget to set the MACOSX_DEPLOYMENT_TARGET - environmental variable prior to building cURL. */ +#define paramErr -50 /* The following two functions were ripped from Apple sample code, * with some modifications: */ @@ -97,8 +137,8 @@ static OSStatus SocketRead(SSLConnectionRef connection, if(rrtn <= 0) { /* this is guesswork... */ theErr = errno; - if((rrtn == 0) && (theErr == 0)) { - /* try fix for iSync */ + if(rrtn == 0) { /* EOF = server hung up */ + /* the framework will turn this into errSSLClosedNoNotify */ rtn = errSSLClosedGraceful; } else /* do the switch */ @@ -360,6 +400,7 @@ CF_INLINE const char *TLSCipherNameForNumber(SSLCipherSuite cipher) { case TLS_DH_anon_WITH_AES_256_CBC_SHA: return "TLS_DH_anon_WITH_AES_256_CBC_SHA"; break; +#if CURL_BUILD_MAC_10_6 || CURL_BUILD_IOS /* TLS 1.0 with ECDSA (RFC 4492) */ case TLS_ECDH_ECDSA_WITH_NULL_SHA: return "TLS_ECDH_ECDSA_WITH_NULL_SHA"; @@ -436,7 +477,8 @@ CF_INLINE const char *TLSCipherNameForNumber(SSLCipherSuite cipher) { case TLS_ECDH_anon_WITH_AES_256_CBC_SHA: return "TLS_ECDH_anon_WITH_AES_256_CBC_SHA"; break; -#if defined(__MAC_10_8) || defined(__IPHONE_5_0) +#endif /* CURL_BUILD_MAC_10_6 || CURL_BUILD_IOS */ +#if CURL_BUILD_MAC_10_8 || CURL_BUILD_IOS /* TLS 1.2 (RFC 5246) */ case TLS_RSA_WITH_NULL_MD5: return "TLS_RSA_WITH_NULL_MD5"; @@ -621,44 +663,330 @@ CF_INLINE const char *TLSCipherNameForNumber(SSLCipherSuite cipher) { case SSL_DH_anon_WITH_3DES_EDE_CBC_SHA: return "TLS_DH_anon_WITH_3DES_EDE_CBC_SHA"; break; -#endif /* defined(__MAC_10_8) || defined(__IPHONE_5_0) */ +#endif /* CURL_BUILD_MAC_10_8 || CURL_BUILD_IOS */ +#if CURL_BUILD_MAC_10_9 || CURL_BUILD_IOS_7 + /* TLS PSK (RFC 4279): */ + case TLS_PSK_WITH_RC4_128_SHA: + return "TLS_PSK_WITH_RC4_128_SHA"; + break; + case TLS_PSK_WITH_3DES_EDE_CBC_SHA: + return "TLS_PSK_WITH_3DES_EDE_CBC_SHA"; + break; + case TLS_PSK_WITH_AES_128_CBC_SHA: + return "TLS_PSK_WITH_AES_128_CBC_SHA"; + break; + case TLS_PSK_WITH_AES_256_CBC_SHA: + return "TLS_PSK_WITH_AES_256_CBC_SHA"; + break; + case TLS_DHE_PSK_WITH_RC4_128_SHA: + return "TLS_DHE_PSK_WITH_RC4_128_SHA"; + break; + case TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA: + return "TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA"; + break; + case TLS_DHE_PSK_WITH_AES_128_CBC_SHA: + return "TLS_DHE_PSK_WITH_AES_128_CBC_SHA"; + break; + case TLS_DHE_PSK_WITH_AES_256_CBC_SHA: + return "TLS_DHE_PSK_WITH_AES_256_CBC_SHA"; + break; + case TLS_RSA_PSK_WITH_RC4_128_SHA: + return "TLS_RSA_PSK_WITH_RC4_128_SHA"; + break; + case TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA: + return "TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA"; + break; + case TLS_RSA_PSK_WITH_AES_128_CBC_SHA: + return "TLS_RSA_PSK_WITH_AES_128_CBC_SHA"; + break; + case TLS_RSA_PSK_WITH_AES_256_CBC_SHA: + return "TLS_RSA_PSK_WITH_AES_256_CBC_SHA"; + break; + /* More TLS PSK (RFC 4785): */ + case TLS_PSK_WITH_NULL_SHA: + return "TLS_PSK_WITH_NULL_SHA"; + break; + case TLS_DHE_PSK_WITH_NULL_SHA: + return "TLS_DHE_PSK_WITH_NULL_SHA"; + break; + case TLS_RSA_PSK_WITH_NULL_SHA: + return "TLS_RSA_PSK_WITH_NULL_SHA"; + break; + /* Even more TLS PSK (RFC 5487): */ + case TLS_PSK_WITH_AES_128_GCM_SHA256: + return "TLS_PSK_WITH_AES_128_GCM_SHA256"; + break; + case TLS_PSK_WITH_AES_256_GCM_SHA384: + return "TLS_PSK_WITH_AES_256_GCM_SHA384"; + break; + case TLS_DHE_PSK_WITH_AES_128_GCM_SHA256: + return "TLS_DHE_PSK_WITH_AES_128_GCM_SHA256"; + break; + case TLS_DHE_PSK_WITH_AES_256_GCM_SHA384: + return "TLS_DHE_PSK_WITH_AES_256_GCM_SHA384"; + break; + case TLS_RSA_PSK_WITH_AES_128_GCM_SHA256: + return "TLS_RSA_PSK_WITH_AES_128_GCM_SHA256"; + break; + case TLS_RSA_PSK_WITH_AES_256_GCM_SHA384: + return "TLS_PSK_WITH_AES_256_GCM_SHA384"; + break; + case TLS_PSK_WITH_AES_128_CBC_SHA256: + return "TLS_PSK_WITH_AES_128_CBC_SHA256"; + break; + case TLS_PSK_WITH_AES_256_CBC_SHA384: + return "TLS_PSK_WITH_AES_256_CBC_SHA384"; + break; + case TLS_PSK_WITH_NULL_SHA256: + return "TLS_PSK_WITH_NULL_SHA256"; + break; + case TLS_PSK_WITH_NULL_SHA384: + return "TLS_PSK_WITH_NULL_SHA384"; + break; + case TLS_DHE_PSK_WITH_AES_128_CBC_SHA256: + return "TLS_DHE_PSK_WITH_AES_128_CBC_SHA256"; + break; + case TLS_DHE_PSK_WITH_AES_256_CBC_SHA384: + return "TLS_DHE_PSK_WITH_AES_256_CBC_SHA384"; + break; + case TLS_DHE_PSK_WITH_NULL_SHA256: + return "TLS_DHE_PSK_WITH_NULL_SHA256"; + break; + case TLS_DHE_PSK_WITH_NULL_SHA384: + return "TLS_RSA_PSK_WITH_NULL_SHA384"; + break; + case TLS_RSA_PSK_WITH_AES_128_CBC_SHA256: + return "TLS_RSA_PSK_WITH_AES_128_CBC_SHA256"; + break; + case TLS_RSA_PSK_WITH_AES_256_CBC_SHA384: + return "TLS_RSA_PSK_WITH_AES_256_CBC_SHA384"; + break; + case TLS_RSA_PSK_WITH_NULL_SHA256: + return "TLS_RSA_PSK_WITH_NULL_SHA256"; + break; + case TLS_RSA_PSK_WITH_NULL_SHA384: + return "TLS_RSA_PSK_WITH_NULL_SHA384"; + break; +#endif /* CURL_BUILD_MAC_10_9 || CURL_BUILD_IOS_7 */ } return "TLS_NULL_WITH_NULL_NULL"; } -CF_INLINE bool IsRunningMountainLionOrLater(void) +#if CURL_BUILD_MAC +CF_INLINE void GetDarwinVersionNumber(int *major, int *minor) { -#if (TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE)) int mib[2]; char *os_version; size_t os_version_len; - char *os_version_major/*, *os_version_minor, *os_version_point*/; - int os_version_major_int; + char *os_version_major, *os_version_minor/*, *os_version_point*/; /* Get the Darwin kernel version from the kernel using sysctl(): */ mib[0] = CTL_KERN; mib[1] = KERN_OSRELEASE; if(sysctl(mib, 2, NULL, &os_version_len, NULL, 0) == -1) - return false; + return; os_version = malloc(os_version_len*sizeof(char)); if(!os_version) - return false; + return; if(sysctl(mib, 2, os_version, &os_version_len, NULL, 0) == -1) { free(os_version); - return false; + return; } - /* Parse the version. If it's version 12.0.0 or later, then this user is - using Mountain Lion. */ + /* Parse the version: */ os_version_major = strtok(os_version, "."); - /*os_version_minor = strtok(NULL, "."); - os_version_point = strtok(NULL, ".");*/ - os_version_major_int = atoi(os_version_major); + os_version_minor = strtok(NULL, "."); + /*os_version_point = strtok(NULL, ".");*/ + *major = atoi(os_version_major); + *minor = atoi(os_version_minor); free(os_version); - return os_version_major_int >= 12; +} +#endif /* CURL_BUILD_MAC */ + +/* Apple provides a myriad of ways of getting information about a certificate + into a string. Some aren't available under iOS or newer cats. So here's + a unified function for getting a string describing the certificate that + ought to work in all cats starting with Leopard. */ +CF_INLINE CFStringRef CopyCertSubject(SecCertificateRef cert) +{ + CFStringRef server_cert_summary = CFSTR("(null)"); + +#if CURL_BUILD_IOS + /* iOS: There's only one way to do this. */ + server_cert_summary = SecCertificateCopySubjectSummary(cert); #else - return true; /* iOS users: this doesn't concern you */ -#endif +#if CURL_BUILD_MAC_10_7 + /* Lion & later: Get the long description if we can. */ + if(SecCertificateCopyLongDescription != NULL) + server_cert_summary = + SecCertificateCopyLongDescription(NULL, cert, NULL); + else +#endif /* CURL_BUILD_MAC_10_7 */ +#if CURL_BUILD_MAC_10_6 + /* Snow Leopard: Get the certificate summary. */ + if(SecCertificateCopySubjectSummary != NULL) + server_cert_summary = SecCertificateCopySubjectSummary(cert); + else +#endif /* CURL_BUILD_MAC_10_6 */ + /* Leopard is as far back as we go... */ + (void)SecCertificateCopyCommonName(cert, &server_cert_summary); +#endif /* CURL_BUILD_IOS */ + return server_cert_summary; +} + +#if CURL_SUPPORT_MAC_10_7 +/* The SecKeychainSearch API was deprecated in Lion, and using it will raise + deprecation warnings, so let's not compile this unless it's necessary: */ +static OSStatus CopyIdentityWithLabelOldSchool(char *label, + SecIdentityRef *out_c_a_k) +{ + OSStatus status = errSecItemNotFound; + SecKeychainAttributeList attr_list; + SecKeychainAttribute attr; + SecKeychainSearchRef search = NULL; + SecCertificateRef cert = NULL; + + /* Set up the attribute list: */ + attr_list.count = 1L; + attr_list.attr = &attr; + + /* Set up our lone search criterion: */ + attr.tag = kSecLabelItemAttr; + attr.data = label; + attr.length = (UInt32)strlen(label); + + /* Start searching: */ + status = SecKeychainSearchCreateFromAttributes(NULL, + kSecCertificateItemClass, + &attr_list, + &search); + if(status == noErr) { + status = SecKeychainSearchCopyNext(search, + (SecKeychainItemRef *)&cert); + if(status == noErr && cert) { + /* If we found a certificate, does it have a private key? */ + status = SecIdentityCreateWithCertificate(NULL, cert, out_c_a_k); + CFRelease(cert); + } + } + + if(search) + CFRelease(search); + return status; +} +#endif /* CURL_SUPPORT_MAC_10_7 */ + +static OSStatus CopyIdentityWithLabel(char *label, + SecIdentityRef *out_cert_and_key) +{ + OSStatus status = errSecItemNotFound; + +#if CURL_BUILD_MAC_10_7 || CURL_BUILD_IOS + /* SecItemCopyMatching() was introduced in iOS and Snow Leopard. + kSecClassIdentity was introduced in Lion. If both exist, let's use them + to find the certificate. */ + if(SecItemCopyMatching != NULL && kSecClassIdentity != NULL) { + CFTypeRef keys[4]; + CFTypeRef values[4]; + CFDictionaryRef query_dict; + CFStringRef label_cf = CFStringCreateWithCString(NULL, label, + kCFStringEncodingUTF8); + + /* Set up our search criteria and expected results: */ + values[0] = kSecClassIdentity; /* we want a certificate and a key */ + keys[0] = kSecClass; + values[1] = kCFBooleanTrue; /* we want a reference */ + keys[1] = kSecReturnRef; + values[2] = kSecMatchLimitOne; /* one is enough, thanks */ + keys[2] = kSecMatchLimit; + /* identity searches need a SecPolicyRef in order to work */ + values[3] = SecPolicyCreateSSL(false, label_cf); + keys[3] = kSecMatchPolicy; + query_dict = CFDictionaryCreate(NULL, (const void **)keys, + (const void **)values, 4L, + &kCFCopyStringDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); + CFRelease(values[3]); + CFRelease(label_cf); + + /* Do we have a match? */ + status = SecItemCopyMatching(query_dict, (CFTypeRef *)out_cert_and_key); + CFRelease(query_dict); + } + else { +#if CURL_SUPPORT_MAC_10_7 + /* On Leopard and Snow Leopard, fall back to SecKeychainSearch. */ + status = CopyIdentityWithLabelOldSchool(label, out_cert_and_key); +#endif /* CURL_SUPPORT_MAC_10_7 */ + } +#elif CURL_SUPPORT_MAC_10_7 + /* For developers building on older cats, we have no choice but to fall back + to SecKeychainSearch. */ + status = CopyIdentityWithLabelOldSchool(label, out_cert_and_key); +#endif /* CURL_BUILD_MAC_10_7 || CURL_BUILD_IOS */ + return status; +} + +static OSStatus CopyIdentityFromPKCS12File(const char *cPath, + const char *cPassword, + SecIdentityRef *out_cert_and_key) +{ + OSStatus status = errSecItemNotFound; + CFURLRef pkcs_url = CFURLCreateFromFileSystemRepresentation(NULL, + (const UInt8 *)cPath, strlen(cPath), false); + CFStringRef password = cPassword ? CFStringCreateWithCString(NULL, + cPassword, kCFStringEncodingUTF8) : NULL; + CFDataRef pkcs_data = NULL; + + /* We can import P12 files on iOS or OS X 10.6 or later: */ +#if CURL_BUILD_MAC_10_6 || CURL_BUILD_IOS + if(CFURLCreateDataAndPropertiesFromResource(NULL, pkcs_url, &pkcs_data, + NULL, NULL, &status)) { + const void *cKeys[] = {kSecImportExportPassphrase}; + const void *cValues[] = {password}; + CFDictionaryRef options = CFDictionaryCreate(NULL, cKeys, cValues, + password ? 1L : 0L, NULL, NULL); + CFArrayRef items = NULL; + + /* Here we go: */ + status = SecPKCS12Import(pkcs_data, options, &items); + if(status == noErr) { + CFDictionaryRef identity_and_trust = CFArrayGetValueAtIndex(items, 0L); + const void *temp_identity = CFDictionaryGetValue(identity_and_trust, + kSecImportItemIdentity); + + /* Retain the identity; we don't care about any other data... */ + CFRetain(temp_identity); + *out_cert_and_key = (SecIdentityRef)temp_identity; + CFRelease(items); + } + CFRelease(options); + CFRelease(pkcs_data); + } +#endif /* CURL_BUILD_MAC_10_6 || CURL_BUILD_IOS */ + if(password) + CFRelease(password); + CFRelease(pkcs_url); + return status; +} + +/* This code was borrowed from nss.c, with some modifications: + * Determine whether the nickname passed in is a filename that needs to + * be loaded as a PEM or a regular NSS nickname. + * + * returns 1 for a file + * returns 0 for not a file + */ +CF_INLINE bool is_file(const char *filename) +{ + struct_stat st; + + if(filename == NULL) + return false; + + if(stat(filename, &st) == 0) + return S_ISREG(st.st_mode); + return false; } static CURLcode darwinssl_connect_step1(struct connectdata *conn, @@ -671,11 +999,19 @@ static CURLcode darwinssl_connect_step1(struct connectdata *conn, struct in6_addr addr; #else struct in_addr addr; -#endif - /*SSLConnectionRef ssl_connection;*/ +#endif /* ENABLE_IPV6 */ + size_t all_ciphers_count = 0UL, allowed_ciphers_count = 0UL, i; + SSLCipherSuite *all_ciphers = NULL, *allowed_ciphers = NULL; + char *ssl_sessionid; + size_t ssl_sessionid_len; OSStatus err = noErr; +#if CURL_BUILD_MAC + int darwinver_maj = 0, darwinver_min = 0; + + GetDarwinVersionNumber(&darwinver_maj, &darwinver_min); +#endif /* CURL_BUILD_MAC */ -#if defined(__MAC_10_8) || defined(__IPHONE_5_0) +#if CURL_BUILD_MAC_10_8 || CURL_BUILD_IOS if(SSLCreateContext != NULL) { /* use the newer API if avaialble */ if(connssl->ssl_ctx) CFRelease(connssl->ssl_ctx); @@ -687,7 +1023,7 @@ static CURLcode darwinssl_connect_step1(struct connectdata *conn, } else { /* The old ST API does not exist under iOS, so don't compile it: */ -#if (TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE)) +#if CURL_SUPPORT_MAC_10_8 if(connssl->ssl_ctx) (void)SSLDisposeContext(connssl->ssl_ctx); err = SSLNewContext(false, &(connssl->ssl_ctx)); @@ -695,7 +1031,7 @@ static CURLcode darwinssl_connect_step1(struct connectdata *conn, failf(data, "SSL: couldn't create a context: OSStatus %d", err); return CURLE_OUT_OF_MEMORY; } -#endif /* (TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE)) */ +#endif /* CURL_SUPPORT_MAC_10_8 */ } #else if(connssl->ssl_ctx) @@ -705,10 +1041,11 @@ static CURLcode darwinssl_connect_step1(struct connectdata *conn, failf(data, "SSL: couldn't create a context: OSStatus %d", err); return CURLE_OUT_OF_MEMORY; } -#endif /* defined(__MAC_10_8) || defined(__IPHONE_5_0) */ +#endif /* CURL_BUILD_MAC_10_8 || CURL_BUILD_IOS */ + connssl->ssl_write_buffered_length = 0UL; /* reset buffered write length */ /* check to see if we've been told to use an explicit SSL/TLS version */ -#if defined(__MAC_10_8) || defined(__IPHONE_5_0) +#if CURL_BUILD_MAC_10_8 || CURL_BUILD_IOS if(SSLSetProtocolVersionMax != NULL) { switch(data->set.ssl.version) { case CURL_SSLVERSION_DEFAULT: default: @@ -724,12 +1061,16 @@ static CURLcode darwinssl_connect_step1(struct connectdata *conn, (void)SSLSetProtocolVersionMax(connssl->ssl_ctx, kSSLProtocol3); break; case CURL_SSLVERSION_SSLv2: - (void)SSLSetProtocolVersionMin(connssl->ssl_ctx, kSSLProtocol2); + err = SSLSetProtocolVersionMin(connssl->ssl_ctx, kSSLProtocol2); + if(err != noErr) { + failf(data, "Your version of the OS does not support SSLv2"); + return CURLE_SSL_CONNECT_ERROR; + } (void)SSLSetProtocolVersionMax(connssl->ssl_ctx, kSSLProtocol2); } } else { -#if (TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE)) +#if CURL_SUPPORT_MAC_10_8 (void)SSLSetProtocolVersionEnabled(connssl->ssl_ctx, kSSLProtocolAll, false); @@ -765,12 +1106,16 @@ static CURLcode darwinssl_connect_step1(struct connectdata *conn, true); break; case CURL_SSLVERSION_SSLv2: - (void)SSLSetProtocolVersionEnabled(connssl->ssl_ctx, + err = SSLSetProtocolVersionEnabled(connssl->ssl_ctx, kSSLProtocol2, true); + if(err != noErr) { + failf(data, "Your version of the OS does not support SSLv2"); + return CURLE_SSL_CONNECT_ERROR; + } break; } -#endif /* (TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE)) */ +#endif /* CURL_SUPPORT_MAC_10_8 */ } #else (void)SSLSetProtocolVersionEnabled(connssl->ssl_ctx, kSSLProtocolAll, false); @@ -790,9 +1135,13 @@ static CURLcode darwinssl_connect_step1(struct connectdata *conn, true); break; case CURL_SSLVERSION_SSLv2: - (void)SSLSetProtocolVersionEnabled(connssl->ssl_ctx, + err = SSLSetProtocolVersionEnabled(connssl->ssl_ctx, kSSLProtocol2, true); + if(err != noErr) { + failf(data, "Your version of the OS does not support SSLv2"); + return CURLE_SSL_CONNECT_ERROR; + } break; case CURL_SSLVERSION_SSLv3: (void)SSLSetProtocolVersionEnabled(connssl->ssl_ctx, @@ -800,16 +1149,104 @@ static CURLcode darwinssl_connect_step1(struct connectdata *conn, true); break; } -#endif /* defined(__MAC_10_8) || defined(__IPHONE_5_0) */ +#endif /* CURL_BUILD_MAC_10_8 || CURL_BUILD_IOS */ - /* No need to load certificates here. SecureTransport uses the Keychain - * (which is also part of the Security framework) to evaluate trust. */ + if(data->set.str[STRING_KEY]) { + infof(data, "WARNING: SSL: CURLOPT_SSLKEY is ignored by Secure " + "Transport. The private key must be in the Keychain.\n"); + } + + if(data->set.str[STRING_CERT]) { + SecIdentityRef cert_and_key = NULL; + bool is_cert_file = is_file(data->set.str[STRING_CERT]); + + /* User wants to authenticate with a client cert. Look for it: + If we detect that this is a file on disk, then let's load it. + Otherwise, assume that the user wants to use an identity loaded + from the Keychain. */ + if(is_cert_file) { + if(!data->set.str[STRING_CERT_TYPE]) + infof(data, "WARNING: SSL: Certificate type not set, assuming " + "PKCS#12 format.\n"); + else if(strncmp(data->set.str[STRING_CERT_TYPE], "P12", + strlen(data->set.str[STRING_CERT_TYPE])) != 0) + infof(data, "WARNING: SSL: The Security framework only supports " + "loading identities that are in PKCS#12 format.\n"); + + err = CopyIdentityFromPKCS12File(data->set.str[STRING_CERT], + data->set.str[STRING_KEY_PASSWD], &cert_and_key); + } + else + err = CopyIdentityWithLabel(data->set.str[STRING_CERT], &cert_and_key); + + if(err == noErr) { + SecCertificateRef cert = NULL; + CFTypeRef certs_c[1]; + CFArrayRef certs; + + /* If we found one, print it out: */ + err = SecIdentityCopyCertificate(cert_and_key, &cert); + if(err == noErr) { + CFStringRef cert_summary = CopyCertSubject(cert); + char cert_summary_c[128]; + + if(cert_summary) { + memset(cert_summary_c, 0, 128); + if(CFStringGetCString(cert_summary, + cert_summary_c, + 128, + kCFStringEncodingUTF8)) { + infof(data, "Client certificate: %s\n", cert_summary_c); + } + CFRelease(cert_summary); + CFRelease(cert); + } + } + certs_c[0] = cert_and_key; + certs = CFArrayCreate(NULL, (const void **)certs_c, 1L, + &kCFTypeArrayCallBacks); + err = SSLSetCertificate(connssl->ssl_ctx, certs); + if(certs) + CFRelease(certs); + if(err != noErr) { + failf(data, "SSL: SSLSetCertificate() failed: OSStatus %d", err); + return CURLE_SSL_CERTPROBLEM; + } + CFRelease(cert_and_key); + } + else { + switch(err) { + case errSecPkcs12VerifyFailure: case errSecAuthFailed: + failf(data, "SSL: Incorrect password for the certificate \"%s\" " + "and its private key.", data->set.str[STRING_CERT]); + break; + case errSecDecode: case errSecUnknownFormat: + failf(data, "SSL: Couldn't make sense of the data in the " + "certificate \"%s\" and its private key.", + data->set.str[STRING_CERT]); + break; + case errSecPassphraseRequired: + failf(data, "SSL The certificate \"%s\" requires a password.", + data->set.str[STRING_CERT]); + break; + case errSecItemNotFound: + failf(data, "SSL: Can't find the certificate \"%s\" and its private " + "key in the Keychain.", data->set.str[STRING_CERT]); + break; + default: + failf(data, "SSL: Can't load the certificate \"%s\" and its private " + "key: OSStatus %d", data->set.str[STRING_CERT], err); + break; + } + return CURLE_SSL_CERTPROBLEM; + } + } /* SSL always tries to verify the peer, this only says whether it should * fail to connect if the verification fails, or if it should continue * anyway. In the latter case the result of the verification is checked with * SSL_get_verify_result() below. */ -#if defined(__MAC_10_6) || defined(__IPHONE_5_0) +#if CURL_BUILD_MAC_10_6 || CURL_BUILD_IOS /* Snow Leopard introduced the SSLSetSessionOption() function, but due to a library bug with the way the kSSLSessionOptionBreakOnServerAuth flag works, it doesn't work as expected under Snow Leopard or Lion. @@ -817,7 +1254,12 @@ static CURLcode darwinssl_connect_step1(struct connectdata *conn, to disable certificate validation if the user turned that off. (SecureTransport will always validate the certificate chain by default.) */ - if(SSLSetSessionOption != NULL && IsRunningMountainLionOrLater()) { + /* (Note: Darwin 12.x.x is Mountain Lion.) */ +#if CURL_BUILD_MAC + if(SSLSetSessionOption != NULL && darwinver_maj >= 12) { +#else + if(SSLSetSessionOption != NULL) { +#endif /* CURL_BUILD_MAC */ err = SSLSetSessionOption(connssl->ssl_ctx, kSSLSessionOptionBreakOnServerAuth, data->set.ssl.verifypeer?false:true); @@ -827,14 +1269,14 @@ static CURLcode darwinssl_connect_step1(struct connectdata *conn, } } else { -#if (TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE)) +#if CURL_SUPPORT_MAC_10_8 err = SSLSetEnableCertVerify(connssl->ssl_ctx, data->set.ssl.verifypeer?true:false); if(err != noErr) { failf(data, "SSL: SSLSetEnableCertVerify() failed: OSStatus %d", err); return CURLE_SSL_CONNECT_ERROR; } -#endif /* (TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE)) */ +#endif /* CURL_SUPPORT_MAC_10_8 */ } #else err = SSLSetEnableCertVerify(connssl->ssl_ctx, @@ -843,7 +1285,7 @@ static CURLcode darwinssl_connect_step1(struct connectdata *conn, failf(data, "SSL: SSLSetEnableCertVerify() failed: OSStatus %d", err); return CURLE_SSL_CONNECT_ERROR; } -#endif /* defined(__MAC_10_6) || defined(__IPHONE_5_0) */ +#endif /* CURL_BUILD_MAC_10_6 || CURL_BUILD_IOS */ /* If this is a domain name and not an IP address, then configure SNI. * Also: the verifyhost setting influences SNI usage */ @@ -856,11 +1298,147 @@ static CURLcode darwinssl_connect_step1(struct connectdata *conn, err = SSLSetPeerDomainName(connssl->ssl_ctx, conn->host.name, strlen(conn->host.name)); if(err != noErr) { - infof(data, "WARNING: SSL: SSLSetPeerDomainName() failed: OSStatus %d", + infof(data, "WARNING: SSL: SSLSetPeerDomainName() failed: OSStatus %d\n", err); } } + /* Disable cipher suites that ST supports but are not safe. These ciphers + are unlikely to be used in any case since ST gives other ciphers a much + higher priority, but it's probably better that we not connect at all than + to give the user a false sense of security if the server only supports + insecure ciphers. (Note: We don't care about SSLv2-only ciphers.) */ + (void)SSLGetNumberSupportedCiphers(connssl->ssl_ctx, &all_ciphers_count); + all_ciphers = malloc(all_ciphers_count*sizeof(SSLCipherSuite)); + allowed_ciphers = malloc(all_ciphers_count*sizeof(SSLCipherSuite)); + if(all_ciphers && allowed_ciphers && + SSLGetSupportedCiphers(connssl->ssl_ctx, all_ciphers, + &all_ciphers_count) == noErr) { + for(i = 0UL ; i < all_ciphers_count ; i++) { +#if CURL_BUILD_MAC + /* There's a known bug in early versions of Mountain Lion where ST's ECC + ciphers (cipher suite 0xC001 through 0xC032) simply do not work. + Work around the problem here by disabling those ciphers if we are + running in an affected version of OS X. */ + if(darwinver_maj == 12 && darwinver_min <= 3 && + all_ciphers[i] >= 0xC001 && all_ciphers[i] <= 0xC032) { + continue; + } +#endif /* CURL_BUILD_MAC */ + switch(all_ciphers[i]) { + /* Disable NULL ciphersuites: */ + case SSL_NULL_WITH_NULL_NULL: + case SSL_RSA_WITH_NULL_MD5: + case SSL_RSA_WITH_NULL_SHA: + case 0x003B: /* TLS_RSA_WITH_NULL_SHA256 */ + case SSL_FORTEZZA_DMS_WITH_NULL_SHA: + case 0xC001: /* TLS_ECDH_ECDSA_WITH_NULL_SHA */ + case 0xC006: /* TLS_ECDHE_ECDSA_WITH_NULL_SHA */ + case 0xC00B: /* TLS_ECDH_RSA_WITH_NULL_SHA */ + case 0xC010: /* TLS_ECDHE_RSA_WITH_NULL_SHA */ + case 0x002C: /* TLS_PSK_WITH_NULL_SHA */ + case 0x002D: /* TLS_DHE_PSK_WITH_NULL_SHA */ + case 0x002E: /* TLS_RSA_PSK_WITH_NULL_SHA */ + case 0x00B0: /* TLS_PSK_WITH_NULL_SHA256 */ + case 0x00B1: /* TLS_PSK_WITH_NULL_SHA384 */ + case 0x00B4: /* TLS_DHE_PSK_WITH_NULL_SHA256 */ + case 0x00B5: /* TLS_DHE_PSK_WITH_NULL_SHA384 */ + case 0x00B8: /* TLS_RSA_PSK_WITH_NULL_SHA256 */ + case 0x00B9: /* TLS_RSA_PSK_WITH_NULL_SHA384 */ + /* Disable anonymous ciphersuites: */ + case SSL_DH_anon_EXPORT_WITH_RC4_40_MD5: + case SSL_DH_anon_WITH_RC4_128_MD5: + case SSL_DH_anon_EXPORT_WITH_DES40_CBC_SHA: + case SSL_DH_anon_WITH_DES_CBC_SHA: + case SSL_DH_anon_WITH_3DES_EDE_CBC_SHA: + case TLS_DH_anon_WITH_AES_128_CBC_SHA: + case TLS_DH_anon_WITH_AES_256_CBC_SHA: + case 0xC015: /* TLS_ECDH_anon_WITH_NULL_SHA */ + case 0xC016: /* TLS_ECDH_anon_WITH_RC4_128_SHA */ + case 0xC017: /* TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA */ + case 0xC018: /* TLS_ECDH_anon_WITH_AES_128_CBC_SHA */ + case 0xC019: /* TLS_ECDH_anon_WITH_AES_256_CBC_SHA */ + case 0x006C: /* TLS_DH_anon_WITH_AES_128_CBC_SHA256 */ + case 0x006D: /* TLS_DH_anon_WITH_AES_256_CBC_SHA256 */ + case 0x00A6: /* TLS_DH_anon_WITH_AES_128_GCM_SHA256 */ + case 0x00A7: /* TLS_DH_anon_WITH_AES_256_GCM_SHA384 */ + /* Disable weak key ciphersuites: */ + case SSL_RSA_EXPORT_WITH_RC4_40_MD5: + case SSL_RSA_EXPORT_WITH_RC2_CBC_40_MD5: + case SSL_RSA_EXPORT_WITH_DES40_CBC_SHA: + case SSL_DH_DSS_EXPORT_WITH_DES40_CBC_SHA: + case SSL_DH_RSA_EXPORT_WITH_DES40_CBC_SHA: + case SSL_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA: + case SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA: + case SSL_RSA_WITH_DES_CBC_SHA: + case SSL_DH_DSS_WITH_DES_CBC_SHA: + case SSL_DH_RSA_WITH_DES_CBC_SHA: + case SSL_DHE_DSS_WITH_DES_CBC_SHA: + case SSL_DHE_RSA_WITH_DES_CBC_SHA: + /* Disable IDEA: */ + case SSL_RSA_WITH_IDEA_CBC_SHA: + case SSL_RSA_WITH_IDEA_CBC_MD5: + break; + default: /* enable everything else */ + allowed_ciphers[allowed_ciphers_count++] = all_ciphers[i]; + break; + } + } + err = SSLSetEnabledCiphers(connssl->ssl_ctx, allowed_ciphers, + allowed_ciphers_count); + if(err != noErr) { + failf(data, "SSL: SSLSetEnabledCiphers() failed: OSStatus %d", err); + return CURLE_SSL_CONNECT_ERROR; + } + } + else { + Curl_safefree(all_ciphers); + Curl_safefree(allowed_ciphers); + failf(data, "SSL: Failed to allocate memory for allowed ciphers"); + return CURLE_OUT_OF_MEMORY; + } + Curl_safefree(all_ciphers); + Curl_safefree(allowed_ciphers); + +#if CURL_BUILD_MAC_10_9 || CURL_BUILD_IOS_7 + /* We want to enable 1/n-1 when using a CBC cipher unless the user + specifically doesn't want us doing that: */ + SSLSetSessionOption(connssl->ssl_ctx, kSSLSessionOptionSendOneByteRecord, + !data->set.ssl_enable_beast); +#endif /* CURL_BUILD_MAC_10_9 || CURL_BUILD_IOS_7 */ + + /* Check if there's a cached ID we can/should use here! */ + if(!Curl_ssl_getsessionid(conn, (void **)&ssl_sessionid, + &ssl_sessionid_len)) { + /* we got a session id, use it! */ + err = SSLSetPeerID(connssl->ssl_ctx, ssl_sessionid, ssl_sessionid_len); + if(err != noErr) { + failf(data, "SSL: SSLSetPeerID() failed: OSStatus %d", err); + return CURLE_SSL_CONNECT_ERROR; + } + /* Informational message */ + infof(data, "SSL re-using session ID\n"); + } + /* If there isn't one, then let's make one up! This has to be done prior + to starting the handshake. */ + else { + CURLcode retcode; + + ssl_sessionid = malloc(256*sizeof(char)); + ssl_sessionid_len = snprintf(ssl_sessionid, 256, "curl:%s:%hu", + conn->host.name, conn->remote_port); + err = SSLSetPeerID(connssl->ssl_ctx, ssl_sessionid, ssl_sessionid_len); + if(err != noErr) { + failf(data, "SSL: SSLSetPeerID() failed: OSStatus %d", err); + return CURLE_SSL_CONNECT_ERROR; + } + retcode = Curl_ssl_addsessionid(conn, ssl_sessionid, ssl_sessionid_len); + if(retcode!= CURLE_OK) { + failf(data, "failed to store ssl session"); + return retcode; + } + } + err = SSLSetIOFuncs(connssl->ssl_ctx, SocketRead, SocketWrite); if(err != noErr) { failf(data, "SSL: SSLSetIOFuncs() failed: OSStatus %d", err); @@ -872,8 +1450,6 @@ static CURLcode darwinssl_connect_step1(struct connectdata *conn, * SSLSetConnection() will not copy that address. I've found that * conn->sock[sockindex] may change on its own. */ connssl->ssl_sockfd = sockfd; - /*ssl_connection = &(connssl->ssl_sockfd); - err = SSLSetConnection(connssl->ssl_ctx, ssl_connection);*/ err = SSLSetConnection(connssl->ssl_ctx, connssl); if(err != noErr) { failf(data, "SSL: SSLSetConnection() failed: %d", err); @@ -907,22 +1483,71 @@ darwinssl_connect_step2(struct connectdata *conn, int sockindex) ssl_connect_2_writing : ssl_connect_2_reading; return CURLE_OK; - case errSSLServerAuthCompleted: + /* The below is errSSLServerAuthCompleted; it's not defined in + Leopard's headers */ + case -9841: /* the documentation says we need to call SSLHandshake() again */ return darwinssl_connect_step2(conn, sockindex); + /* These are all certificate problems with the server: */ case errSSLXCertChainInvalid: + failf(data, "SSL certificate problem: Invalid certificate chain"); + return CURLE_SSL_CACERT; case errSSLUnknownRootCert: + failf(data, "SSL certificate problem: Untrusted root certificate"); + return CURLE_SSL_CACERT; case errSSLNoRootCert: + failf(data, "SSL certificate problem: No root certificate"); + return CURLE_SSL_CACERT; case errSSLCertExpired: - failf(data, "SSL certificate problem: OSStatus %d", err); + failf(data, "SSL certificate problem: Certificate chain had an " + "expired certificate"); return CURLE_SSL_CACERT; + case errSSLBadCert: + failf(data, "SSL certificate problem: Couldn't understand the server " + "certificate format"); + return CURLE_SSL_CONNECT_ERROR; + /* These are all certificate problems with the client: */ + case errSecAuthFailed: + failf(data, "SSL authentication failed"); + return CURLE_SSL_CONNECT_ERROR; + case errSSLPeerHandshakeFail: + failf(data, "SSL peer handshake failed, the server most likely " + "requires a client certificate to connect"); + return CURLE_SSL_CONNECT_ERROR; + case errSSLPeerUnknownCA: + failf(data, "SSL server rejected the client certificate due to " + "the certificate being signed by an unknown certificate " + "authority"); + return CURLE_SSL_CONNECT_ERROR; + + /* This error is raised if the server's cert didn't match the server's + host name: */ case errSSLHostNameMismatch: failf(data, "SSL certificate peer verification failed, the " "certificate did not match \"%s\"\n", conn->host.dispname); return CURLE_PEER_FAILED_VERIFICATION; + /* Generic handshake errors: */ + case errSSLConnectionRefused: + failf(data, "Server dropped the connection during the SSL handshake"); + return CURLE_SSL_CONNECT_ERROR; + case errSSLClosedAbort: + failf(data, "Server aborted the SSL handshake"); + return CURLE_SSL_CONNECT_ERROR; + case errSSLNegotiation: + failf(data, "Could not negotiate an SSL cipher suite with the server"); + return CURLE_SSL_CONNECT_ERROR; + /* Sometimes paramErr happens with buggy ciphers: */ + case paramErr: case errSSLInternal: + failf(data, "Internal SSL engine error encountered during the " + "SSL handshake"); + return CURLE_SSL_CONNECT_ERROR; + case errSSLFatalAlert: + failf(data, "Fatal SSL engine error encountered during the SSL " + "handshake"); + return CURLE_SSL_CONNECT_ERROR; default: failf(data, "Unknown SSL protocol error in connection to %s:%d", conn->host.name, err); @@ -949,7 +1574,7 @@ darwinssl_connect_step2(struct connectdata *conn, int sockindex) infof(data, "TLS 1.0 connection using %s\n", TLSCipherNameForNumber(cipher)); break; -#if defined(__MAC_10_8) || defined(__IPHONE_5_0) +#if CURL_BUILD_MAC_10_8 || CURL_BUILD_IOS case kTLSProtocol11: infof(data, "TLS 1.1 connection using %s\n", TLSCipherNameForNumber(cipher)); @@ -976,24 +1601,26 @@ darwinssl_connect_step3(struct connectdata *conn, struct ssl_connect_data *connssl = &conn->ssl[sockindex]; CFStringRef server_cert_summary; char server_cert_summary_c[128]; - CFArrayRef server_certs; + CFArrayRef server_certs = NULL; SecCertificateRef server_cert; OSStatus err; CFIndex i, count; - SecTrustRef trust; + SecTrustRef trust = NULL; /* There is no step 3! * Well, okay, if verbose mode is on, let's print the details of the * server certificates. */ -#if defined(__MAC_10_7) || defined(__IPHONE_5_0) -#if (TARGET_OS_EMBEDDED || TARGET_OS_IPHONE) +#if CURL_BUILD_MAC_10_7 || CURL_BUILD_IOS +#if CURL_BUILD_IOS #pragma unused(server_certs) err = SSLCopyPeerTrust(connssl->ssl_ctx, &trust); - if(err == noErr) { + /* For some reason, SSLCopyPeerTrust() can return noErr and yet return + a null trust, so be on guard for that: */ + if(err == noErr && trust) { count = SecTrustGetCertificateCount(trust); for(i = 0L ; i < count ; i++) { server_cert = SecTrustGetCertificateAtIndex(trust, i); - server_cert_summary = SecCertificateCopySubjectSummary(server_cert); + server_cert_summary = CopyCertSubject(server_cert); memset(server_cert_summary_c, 0, 128); if(CFStringGetCString(server_cert_summary, server_cert_summary_c, @@ -1015,12 +1642,13 @@ darwinssl_connect_step3(struct connectdata *conn, if(SecTrustEvaluateAsync != NULL) { #pragma unused(server_certs) err = SSLCopyPeerTrust(connssl->ssl_ctx, &trust); - if(err == noErr) { + /* For some reason, SSLCopyPeerTrust() can return noErr and yet return + a null trust, so be on guard for that: */ + if(err == noErr && trust) { count = SecTrustGetCertificateCount(trust); for(i = 0L ; i < count ; i++) { server_cert = SecTrustGetCertificateAtIndex(trust, i); - server_cert_summary = - SecCertificateCopyLongDescription(NULL, server_cert, NULL); + server_cert_summary = CopyCertSubject(server_cert); memset(server_cert_summary_c, 0, 128); if(CFStringGetCString(server_cert_summary, server_cert_summary_c, @@ -1034,14 +1662,16 @@ darwinssl_connect_step3(struct connectdata *conn, } } else { +#if CURL_SUPPORT_MAC_10_8 err = SSLCopyPeerCertificates(connssl->ssl_ctx, &server_certs); - if(err == noErr) { + /* Just in case SSLCopyPeerCertificates() returns null too... */ + if(err == noErr && server_certs) { count = CFArrayGetCount(server_certs); for(i = 0L ; i < count ; i++) { server_cert = (SecCertificateRef)CFArrayGetValueAtIndex(server_certs, i); - server_cert_summary = SecCertificateCopySubjectSummary(server_cert); + server_cert_summary = CopyCertSubject(server_cert); memset(server_cert_summary_c, 0, 128); if(CFStringGetCString(server_cert_summary, server_cert_summary_c, @@ -1053,8 +1683,9 @@ darwinssl_connect_step3(struct connectdata *conn, } CFRelease(server_certs); } +#endif /* CURL_SUPPORT_MAC_10_8 */ } -#endif /* (TARGET_OS_EMBEDDED || TARGET_OS_IPHONE) */ +#endif /* CURL_BUILD_IOS */ #else #pragma unused(trust) err = SSLCopyPeerCertificates(connssl->ssl_ctx, &server_certs); @@ -1062,8 +1693,7 @@ darwinssl_connect_step3(struct connectdata *conn, count = CFArrayGetCount(server_certs); for(i = 0L ; i < count ; i++) { server_cert = (SecCertificateRef)CFArrayGetValueAtIndex(server_certs, i); - - server_cert_summary = SecCertificateCopySubjectSummary(server_cert); + server_cert_summary = CopyCertSubject(server_cert); memset(server_cert_summary_c, 0, 128); if(CFStringGetCString(server_cert_summary, server_cert_summary_c, @@ -1075,7 +1705,7 @@ darwinssl_connect_step3(struct connectdata *conn, } CFRelease(server_certs); } -#endif /* defined(__MAC_10_7) || defined(__IPHONE_5_0) */ +#endif /* CURL_BUILD_MAC_10_7 || CURL_BUILD_IOS */ connssl->connecting_state = ssl_connect_done; return CURLE_OK; @@ -1227,16 +1857,16 @@ void Curl_darwinssl_close(struct connectdata *conn, int sockindex) if(connssl->ssl_ctx) { (void)SSLClose(connssl->ssl_ctx); -#if defined(__MAC_10_8) || defined(__IPHONE_5_0) +#if CURL_BUILD_MAC_10_8 || CURL_BUILD_IOS if(SSLCreateContext != NULL) CFRelease(connssl->ssl_ctx); -#if (TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE)) +#if CURL_SUPPORT_MAC_10_8 else (void)SSLDisposeContext(connssl->ssl_ctx); -#endif /* (TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE)) */ +#endif /* CURL_SUPPORT_MAC_10_8 */ #else (void)SSLDisposeContext(connssl->ssl_ctx); -#endif /* defined(__MAC_10_8) || defined(__IPHONE_5_0) */ +#endif /* CURL_BUILD_MAC_10_8 || CURL_BUILD_IOS */ connssl->ssl_ctx = NULL; } connssl->ssl_sockfd = 0; @@ -1302,6 +1932,17 @@ int Curl_darwinssl_shutdown(struct connectdata *conn, int sockindex) return rc; } +void Curl_darwinssl_session_free(void *ptr) +{ + /* ST, as of iOS 5 and Mountain Lion, has no public method of deleting a + cached session ID inside the Security framework. There is a private + function that does this, but I don't want to have to explain to you why I + got your application rejected from the App Store due to the use of a + private API, so the best we can do is free up our own char array that we + created way back in darwinssl_connect_step1... */ + Curl_safefree(ptr); +} + size_t Curl_darwinssl_version(char *buffer, size_t size) { return snprintf(buffer, size, "SecureTransport"); @@ -1384,22 +2025,58 @@ static ssize_t darwinssl_send(struct connectdata *conn, /*struct SessionHandle *data = conn->data;*/ struct ssl_connect_data *connssl = &conn->ssl[sockindex]; size_t processed = 0UL; - OSStatus err = SSLWrite(connssl->ssl_ctx, mem, len, &processed); + OSStatus err; - if(err != noErr) { + /* The SSLWrite() function works a little differently than expected. The + fourth argument (processed) is currently documented in Apple's + documentation as: "On return, the length, in bytes, of the data actually + written." + + Now, one could interpret that as "written to the socket," but actually, + it returns the amount of data that was written to a buffer internal to + the SSLContextRef instead. So it's possible for SSLWrite() to return + errSSLWouldBlock and a number of bytes "written" because those bytes were + encrypted and written to a buffer, not to the socket. + + So if this happens, then we need to keep calling SSLWrite() over and + over again with no new data until it quits returning errSSLWouldBlock. */ + + /* Do we have buffered data to write from the last time we were called? */ + if(connssl->ssl_write_buffered_length) { + /* Write the buffered data: */ + err = SSLWrite(connssl->ssl_ctx, NULL, 0UL, &processed); switch (err) { - case errSSLWouldBlock: /* return how much we sent (if anything) */ - if(processed) - return (ssize_t)processed; - *curlcode = CURLE_AGAIN; - return -1; + case noErr: + /* processed is always going to be 0 because we didn't write to + the buffer, so return how much was written to the socket */ + processed = connssl->ssl_write_buffered_length; + connssl->ssl_write_buffered_length = 0UL; break; - + case errSSLWouldBlock: /* argh, try again */ + *curlcode = CURLE_AGAIN; + return -1L; default: - failf(conn->data, "SSLWrite() return error %d", err); + failf(conn->data, "SSLWrite() returned error %d", err); *curlcode = CURLE_SEND_ERROR; - return -1; - break; + return -1L; + } + } + else { + /* We've got new data to write: */ + err = SSLWrite(connssl->ssl_ctx, mem, len, &processed); + if(err != noErr) { + switch (err) { + case errSSLWouldBlock: + /* Data was buffered but not sent, we have to tell the caller + to try sending again, and remember how much was buffered */ + connssl->ssl_write_buffered_length = len; + *curlcode = CURLE_AGAIN; + return -1L; + default: + failf(conn->data, "SSLWrite() returned error %d", err); + *curlcode = CURLE_SEND_ERROR; + return -1L; + } } } return (ssize_t)processed; @@ -1422,18 +2099,23 @@ static ssize_t darwinssl_recv(struct connectdata *conn, if(processed) return (ssize_t)processed; *curlcode = CURLE_AGAIN; - return -1; + return -1L; break; - case errSSLClosedGraceful: /* they're done; fail gracefully */ + /* errSSLClosedGraceful - server gracefully shut down the SSL session + errSSLClosedNoNotify - server hung up on us instead of sending a + closure alert notice, read() is returning 0 + Either way, inform the caller that the server disconnected. */ + case errSSLClosedGraceful: + case errSSLClosedNoNotify: *curlcode = CURLE_OK; - return -1; + return -1L; break; default: failf(conn->data, "SSLRead() return error %d", err); *curlcode = CURLE_RECV_ERROR; - return -1; + return -1L; break; } } diff --git a/plugins/FTPFileYM/curl/lib/curl_darwinssl.h b/plugins/FTPFileYM/curl/lib/curl_darwinssl.h index 183d9371c7..432d3d7ce4 100644 --- a/plugins/FTPFileYM/curl/lib/curl_darwinssl.h +++ b/plugins/FTPFileYM/curl/lib/curl_darwinssl.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 2012, Nick Zitzmann, <nickzman@gmail.com>. + * Copyright (C) 2012 - 2013, Nick Zitzmann, <nickzman@gmail.com>. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -37,6 +37,7 @@ void Curl_darwinssl_close_all(struct SessionHandle *data); /* close a SSL connection */ void Curl_darwinssl_close(struct connectdata *conn, int sockindex); +void Curl_darwinssl_session_free(void *ptr); size_t Curl_darwinssl_version(char *buffer, size_t size); int Curl_darwinssl_shutdown(struct connectdata *conn, int sockindex); int Curl_darwinssl_check_cxn(struct connectdata *conn); @@ -51,12 +52,16 @@ void Curl_darwinssl_md5sum(unsigned char *tmp, /* input */ unsigned char *md5sum, /* output */ size_t md5len); +/* this backend provides these functions: */ +#define have_curlssl_random 1 +#define have_curlssl_md5sum 1 + /* API setup for SecureTransport */ #define curlssl_init() (1) #define curlssl_cleanup() Curl_nop_stmt #define curlssl_connect Curl_darwinssl_connect #define curlssl_connect_nonblocking Curl_darwinssl_connect_nonblocking -#define curlssl_session_free(x) Curl_nop_stmt +#define curlssl_session_free(x) Curl_darwinssl_session_free(x) #define curlssl_close_all Curl_darwinssl_close_all #define curlssl_close Curl_darwinssl_close #define curlssl_shutdown(x,y) 0 diff --git a/plugins/FTPFileYM/curl/lib/curl_memory.h b/plugins/FTPFileYM/curl/lib/curl_memory.h index e119458234..e3cdc721c4 100644 --- a/plugins/FTPFileYM/curl/lib/curl_memory.h +++ b/plugins/FTPFileYM/curl/lib/curl_memory.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2009, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2013, 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 @@ -22,17 +22,86 @@ * ***************************************************************************/ -#include <curl/curl.h> /* for the typedefs */ +/* + * Nasty internal details ahead... + * + * File curl_memory.h must be included by _all_ *.c source files + * that use memory related functions strdup, malloc, calloc, realloc + * or free, and given source file is used to build libcurl library. + * + * There is nearly no exception to above rule. All libcurl source + * files in 'lib' subdirectory as well as those living deep inside + * 'packages' subdirectories and linked together in order to build + * libcurl library shall follow it. + * + * File lib/strdup.c is an exception, given that it provides a strdup + * clone implementation while using malloc. Extra care needed inside + * this one. TODO: revisit this paragraph and related code. + * + * The need for curl_memory.h inclusion is due to libcurl's feature + * of allowing library user to provide memory replacement functions, + * memory callbacks, at runtime with curl_global_init_mem() + * + * Any *.c source file used to build libcurl library that does not + * include curl_memory.h and uses any memory function of the five + * mentioned above will compile without any indication, but it will + * trigger weird memory related issues at runtime. + * + * OTOH some source files from 'lib' subdirectory may additionally be + * used directly as source code when using some curlx_ functions by + * third party programs that don't even use libcurl at all. When using + * these source files in this way it is necessary these are compiled + * with CURLX_NO_MEMORY_CALLBACKS defined, in order to ensure that no + * attempt of calling libcurl's memory callbacks is done from code + * which can not use this machinery. + * + * Notice that libcurl's 'memory tracking' system works chaining into + * the memory callback machinery. This implies that when compiling + * 'lib' source files with CURLX_NO_MEMORY_CALLBACKS defined this file + * disengages usage of libcurl's 'memory tracking' system, defining + * MEMDEBUG_NODEFINES and overriding CURLDEBUG purpose. + * + * CURLX_NO_MEMORY_CALLBACKS takes precedence over CURLDEBUG. This is + * done in order to allow building a 'memory tracking' enabled libcurl + * and at the same time allow building programs which do not use it. + * + * Programs and libraries in 'tests' subdirectories have specific + * purposes and needs, and as such each one will use whatever fits + * best, depending additionally wether it links with libcurl or not. + * + * Caveat emptor. Proper curlx_* separation is a work in progress + * the same as CURLX_NO_MEMORY_CALLBACKS usage, some adjustments may + * still be required. IOW don't use them yet, there are sharp edges. + */ + +#ifdef HEADER_CURL_MEMDEBUG_H +#error "Header memdebug.h shall not be included before curl_memory.h" +#endif + +#ifndef CURLX_NO_MEMORY_CALLBACKS + +#include <curl/curl.h> /* for the callback typedefs */ extern curl_malloc_callback Curl_cmalloc; extern curl_free_callback Curl_cfree; extern curl_realloc_callback Curl_crealloc; extern curl_strdup_callback Curl_cstrdup; extern curl_calloc_callback Curl_ccalloc; +#if defined(WIN32) && defined(UNICODE) +extern curl_wcsdup_callback Curl_cwcsdup; +#endif #ifndef CURLDEBUG -/* Only do this define-mania if we're not using the memdebug system, as that - has preference on this magic. */ + +/* + * libcurl's 'memory tracking' system defines strdup, malloc, calloc, + * realloc and free, along with others, in memdebug.h in a different + * way although still using memory callbacks forward declared above. + * When using the 'memory tracking' system (CURLDEBUG defined) we do + * not define here the five memory functions given that definitions + * from memdebug.h are the ones that shall be used. + */ + #undef strdup #define strdup(ptr) Curl_cstrdup(ptr) #undef malloc @@ -44,6 +113,28 @@ extern curl_calloc_callback Curl_ccalloc; #undef free #define free(ptr) Curl_cfree(ptr) +#ifdef WIN32 +# ifdef UNICODE +# undef wcsdup +# define wcsdup(ptr) Curl_cwcsdup(ptr) +# undef _wcsdup +# define _wcsdup(ptr) Curl_cwcsdup(ptr) +# undef _tcsdup +# define _tcsdup(ptr) Curl_cwcsdup(ptr) +# else +# undef _tcsdup +# define _tcsdup(ptr) Curl_cstrdup(ptr) +# endif #endif +#endif /* CURLDEBUG */ + +#else /* CURLX_NO_MEMORY_CALLBACKS */ + +#ifndef MEMDEBUG_NODEFINES +#define MEMDEBUG_NODEFINES +#endif + +#endif /* CURLX_NO_MEMORY_CALLBACKS */ + #endif /* HEADER_CURL_MEMORY_H */ diff --git a/plugins/FTPFileYM/curl/lib/curl_ntlm.c b/plugins/FTPFileYM/curl/lib/curl_ntlm.c index 72e446c8fd..4d126a573e 100644 --- a/plugins/FTPFileYM/curl/lib/curl_ntlm.c +++ b/plugins/FTPFileYM/curl/lib/curl_ntlm.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2011, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2013, 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 @@ -181,7 +181,6 @@ CURLcode Curl_output_ntlm(struct connectdata *conn, /* Create a type-1 message */ error = Curl_ntlm_create_type1_message(userp, passwdp, ntlm, &base64, &len); - if(error) return error; @@ -190,8 +189,10 @@ CURLcode Curl_output_ntlm(struct connectdata *conn, *allocuserpwd = aprintf("%sAuthorization: NTLM %s\r\n", proxy ? "Proxy-" : "", base64); - DEBUG_OUT(fprintf(stderr, "**** Header %s\n ", *allocuserpwd)); free(base64); + if(!*allocuserpwd) + return CURLE_OUT_OF_MEMORY; + DEBUG_OUT(fprintf(stderr, "**** Header %s\n ", *allocuserpwd)); } break; @@ -207,8 +208,10 @@ CURLcode Curl_output_ntlm(struct connectdata *conn, *allocuserpwd = aprintf("%sAuthorization: NTLM %s\r\n", proxy ? "Proxy-" : "", base64); - DEBUG_OUT(fprintf(stderr, "**** %s\n ", *allocuserpwd)); free(base64); + if(!*allocuserpwd) + return CURLE_OUT_OF_MEMORY; + DEBUG_OUT(fprintf(stderr, "**** %s\n ", *allocuserpwd)); ntlm->state = NTLMSTATE_TYPE3; /* we send a type-3 */ authp->done = TRUE; @@ -218,10 +221,7 @@ CURLcode Curl_output_ntlm(struct connectdata *conn, case NTLMSTATE_TYPE3: /* connection is already authenticated, * don't send a header in future requests */ - if(*allocuserpwd) { - free(*allocuserpwd); - *allocuserpwd = NULL; - } + Curl_safefree(*allocuserpwd); authp->done = TRUE; break; } diff --git a/plugins/FTPFileYM/curl/lib/curl_ntlm_core.c b/plugins/FTPFileYM/curl/lib/curl_ntlm_core.c index 8f3c00d645..79aeb08b3f 100644 --- a/plugins/FTPFileYM/curl/lib/curl_ntlm_core.c +++ b/plugins/FTPFileYM/curl/lib/curl_ntlm_core.c @@ -421,7 +421,7 @@ CURLcode Curl_ntlm_core_mk_nt_hash(struct SessionHandle *data, #elif defined(USE_NSS) Curl_md4it(ntbuffer, pw, 2 * len); #elif defined(USE_DARWINSSL) - (void)CC_MD4(pw, 2 * len, ntbuffer); + (void)CC_MD4(pw, (CC_LONG)(2 * len), ntbuffer); #endif memset(ntbuffer + 16, 0, 21 - 16); diff --git a/plugins/FTPFileYM/curl/lib/curl_ntlm_msgs.c b/plugins/FTPFileYM/curl/lib/curl_ntlm_msgs.c index 93334c6012..125ce6e496 100644 --- a/plugins/FTPFileYM/curl/lib/curl_ntlm_msgs.c +++ b/plugins/FTPFileYM/curl/lib/curl_ntlm_msgs.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2012, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2013, 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 @@ -179,10 +179,11 @@ static unsigned int readint_le(unsigned char *buf) /* * Curl_ntlm_decode_type2_message() * - * This is used to decode a ntlm type-2 message received from a: HTTP, SMTP - * or POP3 server. The message is first decoded from a base64 string into a - * raw ntlm message and checked for validity before the appropriate data for - * creating a type-3 message is written to the given ntlm data structure. + * This is used to decode a ntlm type-2 message received from a HTTP or SASL + * based (such as SMTP, POP3 or IMAP) server. The message is first decoded + * from a base64 string into a raw ntlm message and checked for validity + * before the appropriate data for creating a type-3 message is written to + * the given ntlm data structure. * * Parameters: * @@ -305,9 +306,9 @@ static void unicodecpy(unsigned char *dest, /* * Curl_ntlm_create_type1_message() * - * This is used to generate an already encoded NTLM type-1 message ready - * for sending to the recipient, be it a: HTTP, SMTP or POP3 server, - * using the appropriate compile time crypo API. + * This is used to generate an already encoded NTLM type-1 message ready for + * sending to the recipient, be it a HTTP or SASL based (such as SMTP, POP3 + * or IMAP) server, using the appropriate compile time crypo API. * * Parameters: * @@ -552,9 +553,9 @@ CURLcode Curl_ntlm_create_type1_message(const char *userp, /* * Curl_ntlm_create_type3_message() * - * This is used to generate an already encoded NTLM type-3 message ready - * for sending to the recipient, be it a: HTTP, SMTP or POP3 server, - * using the appropriate compile time crypo API. + * This is used to generate an already encoded NTLM type-3 message ready for + * sending to the recipient, be it a HTTP or SASL based (such as SMTP, POP3 + * or IMAP) server, using the appropriate compile time crypo API. * * Parameters: * diff --git a/plugins/FTPFileYM/curl/lib/curl_sasl.c b/plugins/FTPFileYM/curl/lib/curl_sasl.c index d07387d471..b3ffc66156 100644 --- a/plugins/FTPFileYM/curl/lib/curl_sasl.c +++ b/plugins/FTPFileYM/curl/lib/curl_sasl.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 2012-2013, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 2012 - 2013, 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 @@ -22,6 +22,7 @@ * RFC2831 DIGEST-MD5 authentication * RFC4422 Simple Authentication and Security Layer (SASL) * RFC4616 PLAIN authentication + * RFC6749 OAuth 2.0 Authorization Framework * ***************************************************************************/ @@ -32,7 +33,7 @@ #include "curl_base64.h" #include "curl_md5.h" -#include "curl_rand.h" +#include "sslgen.h" #include "curl_hmac.h" #include "curl_ntlm_msgs.h" #include "curl_sasl.h" @@ -90,22 +91,22 @@ static bool sasl_digest_get_key_value(const unsigned char *chlg, * Returns CURLE_OK on success. */ CURLcode Curl_sasl_create_plain_message(struct SessionHandle *data, - const char* userp, - const char* passwdp, + const char *userp, + const char *passwdp, char **outptr, size_t *outlen) { - char plainauth[2 * MAX_CURL_USER_LENGTH + MAX_CURL_PASSWORD_LENGTH]; + CURLcode result; + char *plainauth; size_t ulen; size_t plen; ulen = strlen(userp); plen = strlen(passwdp); - if(2 * ulen + plen + 2 > sizeof(plainauth)) { + plainauth = malloc(2 * ulen + plen + 2); + if(!plainauth) { *outlen = 0; *outptr = NULL; - - /* Plainauth too small */ return CURLE_OUT_OF_MEMORY; } @@ -117,8 +118,10 @@ CURLcode Curl_sasl_create_plain_message(struct SessionHandle *data, memcpy(plainauth + 2 * ulen + 2, passwdp, plen); /* Base64 encode the reply */ - return Curl_base64_encode(data, plainauth, 2 * ulen + plen + 2, outptr, - outlen); + result = Curl_base64_encode(data, plainauth, 2 * ulen + plen + 2, outptr, + outlen); + Curl_safefree(plainauth); + return result; } /* @@ -138,7 +141,7 @@ CURLcode Curl_sasl_create_plain_message(struct SessionHandle *data, * Returns CURLE_OK on success. */ CURLcode Curl_sasl_create_login_message(struct SessionHandle *data, - const char* valuep, char **outptr, + const char *valuep, char **outptr, size_t *outlen) { size_t vlen = strlen(valuep); @@ -179,9 +182,9 @@ CURLcode Curl_sasl_create_login_message(struct SessionHandle *data, * Returns CURLE_OK on success. */ CURLcode Curl_sasl_create_cram_md5_message(struct SessionHandle *data, - const char* chlg64, - const char* userp, - const char* passwdp, + const char *chlg64, + const char *userp, + const char *passwdp, char **outptr, size_t *outlen) { CURLcode result = CURLE_OK; @@ -190,7 +193,7 @@ CURLcode Curl_sasl_create_cram_md5_message(struct SessionHandle *data, size_t chlglen = 0; HMAC_context *ctxt; unsigned char digest[MD5_DIGEST_LEN]; - char response[MAX_CURL_USER_LENGTH + 2 * MD5_DIGEST_LEN + 1]; + char *response; /* Decode the challenge if necessary */ if(chlg64len && *chlg64 != '=') { @@ -220,14 +223,19 @@ CURLcode Curl_sasl_create_cram_md5_message(struct SessionHandle *data, Curl_HMAC_final(ctxt, digest); /* Prepare the response */ - snprintf(response, sizeof(response), + response = aprintf( "%s %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x", userp, digest[0], digest[1], digest[2], digest[3], digest[4], digest[5], digest[6], digest[7], digest[8], digest[9], digest[10], digest[11], digest[12], digest[13], digest[14], digest[15]); + if(!response) + return CURLE_OUT_OF_MEMORY; /* Base64 encode the reply */ - return Curl_base64_encode(data, response, 0, outptr, outlen); + result = Curl_base64_encode(data, response, 0, outptr, outlen); + + Curl_safefree(response); + return result; } /* @@ -250,10 +258,10 @@ CURLcode Curl_sasl_create_cram_md5_message(struct SessionHandle *data, * Returns CURLE_OK on success. */ CURLcode Curl_sasl_create_digest_md5_message(struct SessionHandle *data, - const char* chlg64, - const char* userp, - const char* passwdp, - const char* service, + const char *chlg64, + const char *userp, + const char *passwdp, + const char *service, char **outptr, size_t *outlen) { static const char table16[] = "0123456789abcdef"; @@ -283,6 +291,9 @@ CURLcode Curl_sasl_create_digest_md5_message(struct SessionHandle *data, if(result) return result; + if(!chlg) + return CURLE_LOGIN_DENIED; + /* Retrieve nonce string from the challenge */ if(!sasl_digest_get_key_value(chlg, "nonce=\"", nonce, sizeof(nonce), '\"')) { @@ -311,7 +322,7 @@ CURLcode Curl_sasl_create_digest_md5_message(struct SessionHandle *data, /* Generate 64 bits of random data */ for(i = 0; i < 8; i++) - cnonce[i] = table16[Curl_rand()%16]; + cnonce[i] = table16[Curl_rand(data)%16]; /* So far so good, now calculate A1 and H(A1) according to RFC 2831 */ ctxt = Curl_MD5_init(Curl_DIGEST_MD5); @@ -468,6 +479,40 @@ CURLcode Curl_sasl_create_ntlm_type3_message(struct SessionHandle *data, #endif /* USE_NTLM */ /* + * Curl_sasl_create_xoauth2_message() + * + * This is used to generate an already encoded XOAUTH2 message ready + * for sending to the recipient. + * + * Parameters: + * + * data [in] - The session handle. + * user [in] - The user name. + * bearer [in] - The XOAUTH Bearer token. + * outptr [in/out] - The address where a pointer to newly allocated memory + * holding the result will be stored upon completion. + * outlen [out] - The length of the output message. + * + * Returns CURLE_OK on success. + */ +CURLcode Curl_sasl_create_xoauth2_message(struct SessionHandle *data, + const char *user, + const char *bearer, + char **outptr, size_t *outlen) +{ + char *xoauth; + + xoauth = aprintf("user=%s\1auth=Bearer %s\1\1", user, bearer); + + if(!xoauth) + return CURLE_OUT_OF_MEMORY; + + /* Base64 encode the reply */ + return Curl_base64_encode(data, xoauth, strlen(xoauth), outptr, + outlen); +} + +/* * Curl_sasl_cleanup() * * This is used to cleanup any libraries or curl modules used by the sasl diff --git a/plugins/FTPFileYM/curl/lib/curl_sasl.h b/plugins/FTPFileYM/curl/lib/curl_sasl.h index 469c9a1e44..2b6a5a26aa 100644 --- a/plugins/FTPFileYM/curl/lib/curl_sasl.h +++ b/plugins/FTPFileYM/curl/lib/curl_sasl.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 2012, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 2012 - 2013, 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 @@ -24,41 +24,61 @@ #include "pingpong.h" +/* Authentication mechanism values */ +#define SASL_AUTH_NONE 0 +#define SASL_AUTH_ANY ~0U + /* Authentication mechanism flags */ -#define SASL_MECH_LOGIN 0x0001 -#define SASL_MECH_PLAIN 0x0002 -#define SASL_MECH_CRAM_MD5 0x0004 -#define SASL_MECH_DIGEST_MD5 0x0008 -#define SASL_MECH_GSSAPI 0x0010 -#define SASL_MECH_EXTERNAL 0x0020 -#define SASL_MECH_NTLM 0x0040 +#define SASL_MECH_LOGIN (1 << 0) +#define SASL_MECH_PLAIN (1 << 1) +#define SASL_MECH_CRAM_MD5 (1 << 2) +#define SASL_MECH_DIGEST_MD5 (1 << 3) +#define SASL_MECH_GSSAPI (1 << 4) +#define SASL_MECH_EXTERNAL (1 << 5) +#define SASL_MECH_NTLM (1 << 6) +#define SASL_MECH_XOAUTH2 (1 << 7) + +/* Authentication mechanism strings */ +#define SASL_MECH_STRING_LOGIN "LOGIN" +#define SASL_MECH_STRING_PLAIN "PLAIN" +#define SASL_MECH_STRING_CRAM_MD5 "CRAM-MD5" +#define SASL_MECH_STRING_DIGEST_MD5 "DIGEST-MD5" +#define SASL_MECH_STRING_GSSAPI "GSSAPI" +#define SASL_MECH_STRING_EXTERNAL "EXTERNAL" +#define SASL_MECH_STRING_NTLM "NTLM" +#define SASL_MECH_STRING_XOAUTH2 "XOAUTH2" + +/* This is used to test whether the line starts with the given mechanism */ +#define sasl_mech_equal(line, wordlen, mech) \ + (wordlen == (sizeof(mech) - 1) / sizeof(char) && \ + !memcmp(line, mech, wordlen)) /* This is used to generate a base64 encoded PLAIN authentication message */ CURLcode Curl_sasl_create_plain_message(struct SessionHandle *data, - const char* userp, - const char* passwdp, + const char *userp, + const char *passwdp, char **outptr, size_t *outlen); /* This is used to generate a base64 encoded LOGIN authentication message containing either the user name or password details */ CURLcode Curl_sasl_create_login_message(struct SessionHandle *data, - const char* valuep, char **outptr, + const char *valuep, char **outptr, size_t *outlen); #ifndef CURL_DISABLE_CRYPTO_AUTH /* This is used to generate a base64 encoded CRAM-MD5 response message */ CURLcode Curl_sasl_create_cram_md5_message(struct SessionHandle *data, - const char* chlg64, - const char* user, - const char* passwdp, + const char *chlg64, + const char *user, + const char *passwdp, char **outptr, size_t *outlen); /* This is used to generate a base64 encoded DIGEST-MD5 response message */ CURLcode Curl_sasl_create_digest_md5_message(struct SessionHandle *data, - const char* chlg64, - const char* user, - const char* passwdp, - const char* service, + const char *chlg64, + const char *user, + const char *passwdp, + const char *service, char **outptr, size_t *outlen); #endif @@ -81,6 +101,13 @@ CURLcode Curl_sasl_create_ntlm_type3_message(struct SessionHandle *data, #endif /* USE_NTLM */ +/* This is used to generate a base64 encoded XOAUTH2 authentication message + containing the user name and bearer token */ +CURLcode Curl_sasl_create_xoauth2_message(struct SessionHandle *data, + const char *user, + const char *bearer, + char **outptr, size_t *outlen); + /* This is used to cleanup any libraries or curl modules used by the sasl functions */ void Curl_sasl_cleanup(struct connectdata *conn, unsigned int authused); diff --git a/plugins/FTPFileYM/curl/lib/curl_schannel.c b/plugins/FTPFileYM/curl/lib/curl_schannel.c index a615f57681..68139db586 100644 --- a/plugins/FTPFileYM/curl/lib/curl_schannel.c +++ b/plugins/FTPFileYM/curl/lib/curl_schannel.c @@ -534,6 +534,7 @@ schannel_connect_step3(struct connectdata *conn, int sockindex) return retcode; } else { + connssl->cred->cached = TRUE; infof(data, "schannel: stored credential handle in session cache\n"); } } @@ -1126,15 +1127,26 @@ int Curl_schannel_shutdown(struct connectdata *conn, int sockindex) /* free SSPI Schannel API security context handle */ if(connssl->ctxt) { + infof(data, "schannel: clear security context handle\n"); s_pSecFn->DeleteSecurityContext(&connssl->ctxt->ctxt_handle); Curl_safefree(connssl->ctxt); } - /* decrement the reference counter of the credential/session handle */ - if(connssl->cred && connssl->cred->refcount > 0) { - connssl->cred->refcount--; - infof(data, "schannel: decremented credential handle refcount = %d\n", - connssl->cred->refcount); + /* free SSPI Schannel API credential handle */ + if(connssl->cred) { + /* decrement the reference counter of the credential/session handle */ + if(connssl->cred->refcount > 0) { + connssl->cred->refcount--; + infof(data, "schannel: decremented credential handle refcount = %d\n", + connssl->cred->refcount); + } + + /* if the handle was not cached and the refcount is zero */ + if(!connssl->cred->cached && connssl->cred->refcount == 0) { + infof(data, "schannel: clear credential handle\n"); + s_pSecFn->FreeCredentialsHandle(&connssl->cred->cred_handle); + Curl_safefree(connssl->cred); + } } } @@ -1159,7 +1171,7 @@ void Curl_schannel_session_free(void *ptr) { struct curl_schannel_cred *cred = ptr; - if(cred && cred->refcount == 0) { + if(cred && cred->cached && cred->refcount == 0) { s_pSecFn->FreeCredentialsHandle(&cred->cred_handle); Curl_safefree(cred); } diff --git a/plugins/FTPFileYM/curl/lib/curl_sec.h b/plugins/FTPFileYM/curl/lib/curl_sec.h new file mode 100644 index 0000000000..82151e9c78 --- /dev/null +++ b/plugins/FTPFileYM/curl/lib/curl_sec.h @@ -0,0 +1,51 @@ +#ifndef HEADER_CURL_SECURITY_H +#define HEADER_CURL_SECURITY_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2013, 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 + * are also available at http://curl.haxx.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ***************************************************************************/ + +struct Curl_sec_client_mech { + const char *name; + size_t size; + int (*init)(void *); + int (*auth)(void *, struct connectdata *); + void (*end)(void *); + int (*check_prot)(void *, int); + int (*overhead)(void *, int, int); + int (*encode)(void *, const void*, int, int, void**, struct connectdata *); + int (*decode)(void *, void*, int, int, struct connectdata *); +}; + +#define AUTH_OK 0 +#define AUTH_CONTINUE 1 +#define AUTH_ERROR 2 + +#ifdef HAVE_GSSAPI +int Curl_sec_read_msg (struct connectdata *conn, char *, + enum protection_level); +void Curl_sec_end (struct connectdata *); +CURLcode Curl_sec_login (struct connectdata *); +int Curl_sec_request_prot (struct connectdata *conn, const char *level); + +extern struct Curl_sec_client_mech Curl_krb5_client_mech; +#endif + +#endif /* HEADER_CURL_SECURITY_H */ diff --git a/plugins/FTPFileYM/curl/lib/curl_setup.h b/plugins/FTPFileYM/curl/lib/curl_setup.h index ad83fec5fa..7edeca2123 100644 --- a/plugins/FTPFileYM/curl/lib/curl_setup.h +++ b/plugins/FTPFileYM/curl/lib/curl_setup.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2012, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2013, 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 @@ -270,6 +270,9 @@ # endif # endif # include <tchar.h> +# ifdef UNICODE + typedef wchar_t *(*curl_wcsdup_callback)(const wchar_t *str); +# endif #endif /* @@ -368,7 +371,9 @@ # include <sys/stat.h> # undef lseek # define lseek(fdes,offset,whence) _lseeki64(fdes, offset, whence) +# undef fstat # define fstat(fdes,stp) _fstati64(fdes, stp) +# undef stat # define stat(fname,stp) _stati64(fname, stp) # define struct_stat struct _stati64 # define LSEEK_ERROR (__int64)-1 @@ -615,7 +620,7 @@ int netware_init(void); #if defined(USE_GNUTLS) || defined(USE_SSLEAY) || defined(USE_NSS) || \ defined(USE_QSOSSL) || defined(USE_POLARSSL) || defined(USE_AXTLS) || \ defined(USE_CYASSL) || defined(USE_SCHANNEL) || \ - defined(USE_DARWINSSL) + defined(USE_DARWINSSL) || defined(USE_GSKIT) #define USE_SSL /* SSL support has been enabled */ #endif @@ -694,4 +699,9 @@ int netware_init(void); #define S_ISREG(m) (((m) & S_IFMT) == S_IFREG) #endif +/* Define S_ISDIR if not defined by system headers, f.e. MSVC */ +#if !defined(S_ISDIR) && defined(S_IFMT) && defined(S_IFDIR) +#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR) +#endif + #endif /* HEADER_CURL_SETUP_H */ diff --git a/plugins/FTPFileYM/curl/lib/curl_setup_once.h b/plugins/FTPFileYM/curl/lib/curl_setup_once.h index 8c7f41ff87..69d6d47902 100644 --- a/plugins/FTPFileYM/curl/lib/curl_setup_once.h +++ b/plugins/FTPFileYM/curl/lib/curl_setup_once.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2012, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2013, 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 @@ -257,6 +257,8 @@ struct timeval { # define sclose(x) closesocket((x)) #elif defined(HAVE_CLOSESOCKET_CAMEL) # define sclose(x) CloseSocket((x)) +#elif defined(HAVE_CLOSE_S) +# define sclose(x) close_s((x)) #elif defined(USE_LWIPSOCK) # define sclose(x) lwip_close((x)) #else @@ -438,7 +440,7 @@ typedef int sig_atomic_t; * (or equivalent) on this platform to hide platform details to code using it. */ -#ifdef WIN32 +#if defined(WIN32) && !defined(USE_LWIPSOCK) #define ERRNO ((int)GetLastError()) #define SET_ERRNO(x) (SetLastError((DWORD)(x))) #else diff --git a/plugins/FTPFileYM/curl/lib/dotdot.c b/plugins/FTPFileYM/curl/lib/dotdot.c new file mode 100644 index 0000000000..41b73bef41 --- /dev/null +++ b/plugins/FTPFileYM/curl/lib/dotdot.c @@ -0,0 +1,170 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2013, 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 + * are also available at http://curl.haxx.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ***************************************************************************/ + +#include "curl_setup.h" + +#include "dotdot.h" + +#include "curl_memory.h" +/* The last #include file should be: */ +#include "memdebug.h" + +/* + * "Remove Dot Segments" + * http://tools.ietf.org/html/rfc3986#section-5.2.4 + */ + +/* + * Curl_dedotdotify() + * + * This function gets a zero-terminated path with dot and dotdot sequences + * passed in and strips them off according to the rules in RFC 3986 section + * 5.2.4. + * + * The function handles a query part ('?' + stuff) appended but it expects + * that fragments ('#' + stuff) have already been cut off. + * + * RETURNS + * + * an allocated dedotdotified output string + */ +char *Curl_dedotdotify(char *input) +{ + size_t inlen = strlen(input); + char *clone; + size_t clen = inlen; /* the length of the cloned input */ + char *out = malloc(inlen+1); + char *outptr; + char *orgclone; + char *queryp; + if(!out) + return NULL; /* out of memory */ + + /* get a cloned copy of the input */ + clone = strdup(input); + if(!clone) { + free(out); + return NULL; + } + orgclone = clone; + outptr = out; + + /* + * To handle query-parts properly, we must find it and remove it during the + * dotdot-operation and then append it again at the end to the output + * string. + */ + queryp = strchr(clone, '?'); + if(queryp) + *queryp = 0; + + do { + + /* A. If the input buffer begins with a prefix of "../" or "./", then + remove that prefix from the input buffer; otherwise, */ + + if(!strncmp("./", clone, 2)) { + clone+=2; + clen-=2; + } + else if(!strncmp("../", clone, 3)) { + clone+=3; + clen-=3; + } + + /* B. if the input buffer begins with a prefix of "/./" or "/.", where + "." is a complete path segment, then replace that prefix with "/" in + the input buffer; otherwise, */ + else if(!strncmp("/./", clone, 3)) { + clone+=2; + clen-=2; + } + else if(!strcmp("/.", clone)) { + clone[1]='/'; + clone++; + clen-=1; + } + + /* C. if the input buffer begins with a prefix of "/../" or "/..", where + ".." is a complete path segment, then replace that prefix with "/" in + the input buffer and remove the last segment and its preceding "/" (if + any) from the output buffer; otherwise, */ + + else if(!strncmp("/../", clone, 4)) { + clone+=3; + clen-=3; + /* remove the last segment from the output buffer */ + while(outptr > out) { + outptr--; + if(*outptr == '/') + break; + } + *outptr = 0; /* zero-terminate where it stops */ + } + else if(!strcmp("/..", clone)) { + clone[2]='/'; + clone+=2; + clen-=2; + /* remove the last segment from the output buffer */ + while(outptr > out) { + outptr--; + if(*outptr == '/') + break; + } + *outptr = 0; /* zero-terminate where it stops */ + } + + /* D. if the input buffer consists only of "." or "..", then remove + that from the input buffer; otherwise, */ + + else if(!strcmp(".", clone) || !strcmp("..", clone)) { + *clone=0; + } + + else { + /* E. move the first path segment in the input buffer to the end of + the output buffer, including the initial "/" character (if any) and + any subsequent characters up to, but not including, the next "/" + character or the end of the input buffer. */ + + do { + *outptr++ = *clone++; + clen--; + } while(*clone && (*clone != '/')); + *outptr = 0; + } + + } while(*clone); + + if(queryp) { + size_t qlen; + /* There was a query part, append that to the output. The 'clone' string + may now have been altered so we copy from the original input string + from the correct index. */ + size_t oindex = queryp - orgclone; + qlen = strlen(&input[oindex]); + memcpy(outptr, &input[oindex], qlen+1); /* include the ending zero byte */ + } + + free(orgclone); + return out; +} diff --git a/plugins/FTPFileYM/curl/lib/dotdot.h b/plugins/FTPFileYM/curl/lib/dotdot.h new file mode 100644 index 0000000000..c3487c137b --- /dev/null +++ b/plugins/FTPFileYM/curl/lib/dotdot.h @@ -0,0 +1,25 @@ +#ifndef HEADER_CURL_DOTDOT_H +#define HEADER_CURL_DOTDOT_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2013, 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 + * are also available at http://curl.haxx.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ***************************************************************************/ +char *Curl_dedotdotify(char *input); +#endif diff --git a/plugins/FTPFileYM/curl/lib/easy.c b/plugins/FTPFileYM/curl/lib/easy.c index c27deffdae..1e718abcba 100644 --- a/plugins/FTPFileYM/curl/lib/easy.c +++ b/plugins/FTPFileYM/curl/lib/easy.c @@ -22,6 +22,14 @@ #include "curl_setup.h" +/* + * See comment in curl_memory.h for the explanation of this sanity check. + */ + +#ifdef CURLX_NO_MEMORY_CALLBACKS +#error "libcurl shall not ever be built with CURLX_NO_MEMORY_CALLBACKS defined" +#endif + #ifdef HAVE_NETINET_IN_H #include <netinet/in.h> #endif @@ -42,6 +50,11 @@ #include <sys/param.h> #endif +#if defined(HAVE_SIGNAL_H) && defined(HAVE_SIGACTION) && defined(USE_OPENSSL) +#define SIGPIPE_IGNORE 1 +#include <signal.h> +#endif + #include "strequal.h" #include "urldata.h" #include <curl/curl.h> @@ -61,10 +74,10 @@ #include "connect.h" /* for Curl_getconnectinfo */ #include "slist.h" #include "amigaos.h" -#include "curl_rand.h" #include "non-ascii.h" #include "warnless.h" #include "conncache.h" +#include "multiif.h" #define _MPRINTF_REPLACE /* use our functions only */ #include <curl/mprintf.h> @@ -72,6 +85,56 @@ /* The last #include file should be: */ #include "memdebug.h" +#ifdef SIGPIPE_IGNORE +struct sigpipe_ignore { + struct sigaction old_pipe_act; + bool no_signal; +}; + +#define SIGPIPE_VARIABLE(x) struct sigpipe_ignore x + +/* + * sigpipe_ignore() makes sure we ignore SIGPIPE while running libcurl + * internals, and then sigpipe_restore() will restore the situation when we + * return from libcurl again. + */ +static void sigpipe_ignore(struct SessionHandle *data, + struct sigpipe_ignore *ig) +{ + /* get a local copy of no_signal because the SessionHandle might not be + around when we restore */ + ig->no_signal = data->set.no_signal; + if(!data->set.no_signal) { + struct sigaction action; + /* first, extract the existing situation */ + memset(&ig->old_pipe_act, 0, sizeof(struct sigaction)); + sigaction(SIGPIPE, NULL, &ig->old_pipe_act); + action = ig->old_pipe_act; + /* ignore this signal */ + action.sa_handler = SIG_IGN; + sigaction(SIGPIPE, &action, NULL); + } +} + +/* + * sigpipe_restore() puts back the outside world's opinion of signal handler + * and SIGPIPE handling. It MUST only be called after a corresponding + * sigpipe_ignore() was used. + */ +static void sigpipe_restore(struct sigpipe_ignore *ig) +{ + if(!ig->no_signal) + /* restore the outside state */ + sigaction(SIGPIPE, &ig->old_pipe_act, NULL); +} + +#else +/* for systems without sigaction */ +#define sigpipe_ignore(x,y) Curl_nop_stmt +#define sigpipe_restore(x) Curl_nop_stmt +#define SIGPIPE_VARIABLE(x) +#endif + /* win32_cleanup() is for win32 socket cleanup functionality, the opposite of win32_init() */ static void win32_cleanup(void) @@ -189,6 +252,9 @@ curl_free_callback Curl_cfree = (curl_free_callback)free; curl_realloc_callback Curl_crealloc = (curl_realloc_callback)realloc; curl_strdup_callback Curl_cstrdup = (curl_strdup_callback)system_strdup; curl_calloc_callback Curl_ccalloc = (curl_calloc_callback)calloc; +#if defined(WIN32) && defined(UNICODE) +curl_wcsdup_callback Curl_cwcsdup = (curl_wcsdup_callback)_wcsdup; +#endif #else /* * Symbian OS doesn't support initialization to code in writeable static data. @@ -220,6 +286,9 @@ CURLcode curl_global_init(long flags) Curl_crealloc = (curl_realloc_callback)realloc; Curl_cstrdup = (curl_strdup_callback)system_strdup; Curl_ccalloc = (curl_calloc_callback)calloc; +#if defined(WIN32) && defined(UNICODE) + Curl_cwcsdup = (curl_wcsdup_callback)_wcsdup; +#endif if(flags & CURL_GLOBAL_SSL) if(!Curl_ssl_init()) { @@ -262,11 +331,10 @@ CURLcode curl_global_init(long flags) } #endif - init_flags = flags; - - /* Preset pseudo-random number sequence. */ + if(flags & CURL_GLOBAL_ACK_EINTR) + Curl_ack_eintr = 1; - Curl_srand(); + init_flags = flags; return CURLE_OK; } @@ -385,8 +453,328 @@ CURLcode curl_easy_setopt(CURL *curl, CURLoption tag, ...) return ret; } +#ifdef CURLDEBUG + +struct socketmonitor { + struct socketmonitor *next; /* the next node in the list or NULL */ + struct pollfd socket; /* socket info of what to monitor */ +}; + +struct events { + long ms; /* timeout, run the timeout function when reached */ + bool msbump; /* set TRUE when timeout is set by callback */ + int num_sockets; /* number of nodes in the monitor list */ + struct socketmonitor *list; /* list of sockets to monitor */ + int running_handles; /* store the returned number */ +}; + +/* events_timer + * + * Callback that gets called with a new value when the timeout should be + * updated. + */ + +static int events_timer(CURLM *multi, /* multi handle */ + long timeout_ms, /* see above */ + void *userp) /* private callback pointer */ +{ + struct events *ev = userp; + (void)multi; + if(timeout_ms == -1) + /* timeout removed */ + timeout_ms = 0; + else if(timeout_ms == 0) + /* timeout is already reached! */ + timeout_ms = 1; /* trigger asap */ + + ev->ms = timeout_ms; + ev->msbump = TRUE; + return 0; +} + + +/* poll2cselect + * + * convert from poll() bit definitions to libcurl's CURL_CSELECT_* ones + */ +static int poll2cselect(int pollmask) +{ + int omask=0; + if(pollmask & POLLIN) + omask |= CURL_CSELECT_IN; + if(pollmask & POLLOUT) + omask |= CURL_CSELECT_OUT; + if(pollmask & POLLERR) + omask |= CURL_CSELECT_ERR; + return omask; +} + + +/* socketcb2poll + * + * convert from libcurl' CURL_POLL_* bit definitions to poll()'s + */ +static short socketcb2poll(int pollmask) +{ + short omask=0; + if(pollmask & CURL_POLL_IN) + omask |= POLLIN; + if(pollmask & CURL_POLL_OUT) + omask |= POLLOUT; + return omask; +} + +/* events_socket + * + * Callback that gets called with information about socket activity to + * monitor. + */ +static int events_socket(CURL *easy, /* easy handle */ + curl_socket_t s, /* socket */ + int what, /* see above */ + void *userp, /* private callback + pointer */ + void *socketp) /* private socket + pointer */ +{ + struct events *ev = userp; + struct socketmonitor *m; + struct socketmonitor *prev=NULL; + (void)socketp; + + m = ev->list; + while(m) { + if(m->socket.fd == s) { + + if(what == CURL_POLL_REMOVE) { + struct socketmonitor *nxt = m->next; + /* remove this node from the list of monitored sockets */ + if(prev) + prev->next = nxt; + else + ev->list = nxt; + free(m); + m = nxt; + infof(easy, "socket cb: socket %d REMOVED\n", s); + } + else { + /* The socket 's' is already being monitored, update the activity + mask. Convert from libcurl bitmask to the poll one. */ + m->socket.events = socketcb2poll(what); + infof(easy, "socket cb: socket %d UPDATED as %s%s\n", s, + what&CURL_POLL_IN?"IN":"", + what&CURL_POLL_OUT?"OUT":""); + } + break; + } + prev = m; + m = m->next; /* move to next node */ + } + if(!m) { + if(what == CURL_POLL_REMOVE) { + /* this happens a bit too often, libcurl fix perhaps? */ + /* fprintf(stderr, + "%s: socket %d asked to be REMOVED but not present!\n", + __func__, s); */ + } + else { + m = malloc(sizeof(struct socketmonitor)); + m->next = ev->list; + m->socket.fd = s; + m->socket.events = socketcb2poll(what); + m->socket.revents = 0; + ev->list = m; + infof(easy, "socket cb: socket %d ADDED as %s%s\n", s, + what&CURL_POLL_IN?"IN":"", + what&CURL_POLL_OUT?"OUT":""); + } + } + + return 0; +} + + /* - * curl_easy_perform() is the external interface that performs a blocking + * events_setup() + * + * Do the multi handle setups that only event-based transfers need. + */ +static void events_setup(CURLM *multi, struct events *ev) +{ + /* timer callback */ + curl_multi_setopt(multi, CURLMOPT_TIMERFUNCTION, events_timer); + curl_multi_setopt(multi, CURLMOPT_TIMERDATA, ev); + + /* socket callback */ + curl_multi_setopt(multi, CURLMOPT_SOCKETFUNCTION, events_socket); + curl_multi_setopt(multi, CURLMOPT_SOCKETDATA, ev); +} + + +/* wait_or_timeout() + * + * waits for activity on any of the given sockets, or the timeout to trigger. + */ + +static CURLcode wait_or_timeout(struct Curl_multi *multi, struct events *ev) +{ + bool done = FALSE; + CURLMcode mcode; + CURLcode rc = CURLE_OK; + + while(!done) { + CURLMsg *msg; + struct socketmonitor *m; + struct pollfd *f; + struct pollfd fds[4]; + int numfds=0; + int pollrc; + int i; + struct timeval before; + struct timeval after; + + /* populate the fds[] array */ + for(m = ev->list, f=&fds[0]; m; m = m->next) { + f->fd = m->socket.fd; + f->events = m->socket.events; + f->revents = 0; + /* fprintf(stderr, "poll() %d check socket %d\n", numfds, f->fd); */ + f++; + numfds++; + } + + /* get the time stamp to use to figure out how long poll takes */ + before = curlx_tvnow(); + + /* wait for activity or timeout */ + pollrc = Curl_poll(fds, numfds, (int)ev->ms); + + after = curlx_tvnow(); + + ev->msbump = FALSE; /* reset here */ + + if(0 == pollrc) { + /* timeout! */ + ev->ms = 0; + /* fprintf(stderr, "call curl_multi_socket_action( TIMEOUT )\n"); */ + mcode = curl_multi_socket_action(multi, CURL_SOCKET_TIMEOUT, 0, + &ev->running_handles); + } + else if(pollrc > 0) { + /* loop over the monitored sockets to see which ones had activity */ + for(i = 0; i< numfds; i++) { + if(fds[i].revents) { + /* socket activity, tell libcurl */ + int act = poll2cselect(fds[i].revents); /* convert */ + infof(multi->easyp, "call curl_multi_socket_action( socket %d )\n", + fds[i].fd); + mcode = curl_multi_socket_action(multi, fds[i].fd, act, + &ev->running_handles); + } + } + + if(!ev->msbump) + /* If nothing updated the timeout, we decrease it by the spent time. + * If it was updated, it has the new timeout time stored already. + */ + ev->ms += curlx_tvdiff(after, before); + + } + if(mcode) + return CURLE_URL_MALFORMAT; /* TODO: return a proper error! */ + + /* we don't really care about the "msgs_in_queue" value returned in the + second argument */ + msg = curl_multi_info_read(multi, &pollrc); + if(msg) { + rc = msg->data.result; + done = TRUE; + } + } + + return rc; +} + + +/* easy_events() + * + * Runs a transfer in a blocking manner using the events-based API + */ +static CURLcode easy_events(CURLM *multi) +{ + struct events evs= {2, FALSE, 0, NULL, 0}; + + /* if running event-based, do some further multi inits */ + events_setup(multi, &evs); + + return wait_or_timeout(multi, &evs); +} +#else /* CURLDEBUG */ +/* when not built with debug, this function doesn't exist */ +#define easy_events(x) CURLE_NOT_BUILT_IN +#endif + +static CURLcode easy_transfer(CURLM *multi) +{ + bool done = FALSE; + CURLMcode mcode = CURLM_OK; + CURLcode code = CURLE_OK; + struct timeval before; + int without_fds = 0; /* count number of consecutive returns from + curl_multi_wait() without any filedescriptors */ + + while(!done && !mcode) { + int still_running; + int ret; + + before = curlx_tvnow(); + mcode = curl_multi_wait(multi, NULL, 0, 1000, &ret); + + if(mcode == CURLM_OK) { + if(ret == -1) { + /* poll() failed not on EINTR, indicate a network problem */ + code = CURLE_RECV_ERROR; + break; + } + else if(ret == 0) { + struct timeval after = curlx_tvnow(); + /* If it returns without any filedescriptor instantly, we need to + avoid busy-looping during periods where it has nothing particular + to wait for */ + if(curlx_tvdiff(after, before) <= 10) { + without_fds++; + if(without_fds > 2) { + int sleep_ms = without_fds < 10 ? (1 << (without_fds-1)): 1000; + Curl_wait_ms(sleep_ms); + } + } + else + /* it wasn't "instant", restart counter */ + without_fds = 0; + } + else + /* got file descriptor, restart counter */ + without_fds = 0; + + mcode = curl_multi_perform(multi, &still_running); + } + + /* only read 'still_running' if curl_multi_perform() return OK */ + if((mcode == CURLM_OK) && !still_running) { + int rc; + CURLMsg *msg = curl_multi_info_read(multi, &rc); + if(msg) { + code = msg->data.result; + done = TRUE; + } + } + } + return code; +} + + +/* + * easy_perform() is the external interface that performs a blocking * transfer as previously setup. * * CONCEPT: This function creates a multi handle, adds the easy handle to it, @@ -398,18 +786,18 @@ CURLcode curl_easy_setopt(CURL *curl, CURLoption tag, ...) * needs to keep it around since if this easy handle is used again by this * function, the same multi handle must be re-used so that the same pools and * caches can be used. + * + * DEBUG: if 'events' is set TRUE, this function will use a replacement engine + * instead of curl_multi_perform() and use curl_multi_socket_action(). */ -CURLcode curl_easy_perform(CURL *easy) +static CURLcode easy_perform(struct SessionHandle *data, bool events) { CURLM *multi; CURLMcode mcode; CURLcode code = CURLE_OK; - CURLMsg *msg; - bool done = FALSE; - int rc; - struct SessionHandle *data = easy; + SIGPIPE_VARIABLE(pipe_st); - if(!easy) + if(!data) return CURLE_BAD_FUNCTION_ARGUMENT; if(data->multi) { @@ -420,13 +808,18 @@ CURLcode curl_easy_perform(CURL *easy) if(data->multi_easy) multi = data->multi_easy; else { - multi = curl_multi_init(); + /* this multi handle will only ever have a single easy handled attached + to it, so make it use minimal hashes */ + multi = Curl_multi_handle(1, 3); if(!multi) return CURLE_OUT_OF_MEMORY; data->multi_easy = multi; } - mcode = curl_multi_add_handle(multi, easy); + /* Copy the MAXCONNECTS option to the multi handle */ + curl_multi_setopt(multi, CURLMOPT_MAXCONNECTS, data->set.maxconnects); + + mcode = curl_multi_add_handle(multi, data); if(mcode) { curl_multi_cleanup(multi); if(mcode == CURLM_OUT_OF_MEMORY) @@ -435,64 +828,62 @@ CURLcode curl_easy_perform(CURL *easy) return CURLE_FAILED_INIT; } + sigpipe_ignore(data, &pipe_st); + /* assign this after curl_multi_add_handle() since that function checks for it and rejects this handle otherwise */ data->multi = multi; - while(!done && !mcode) { - int still_running; - - mcode = curl_multi_wait(multi, NULL, 0, 1000, NULL); - - if(mcode == CURLM_OK) - mcode = curl_multi_perform(multi, &still_running); - - /* only read 'still_running' if curl_multi_perform() return OK */ - if((mcode == CURLM_OK) && !still_running) { - msg = curl_multi_info_read(multi, &rc); - if(msg) { - code = msg->data.result; - done = TRUE; - } - } - } + /* run the transfer */ + code = events ? easy_events(multi) : easy_transfer(multi); /* ignoring the return code isn't nice, but atm we can't really handle a failure here, room for future improvement! */ - (void)curl_multi_remove_handle(multi, easy); + (void)curl_multi_remove_handle(multi, data); + + sigpipe_restore(&pipe_st); /* The multi handle is kept alive, owned by the easy handle */ return code; } + /* - * curl_easy_cleanup() is the external interface to cleaning/freeing the given - * easy handle. + * curl_easy_perform() is the external interface that performs a blocking + * transfer as previously setup. */ -void curl_easy_cleanup(CURL *curl) +CURLcode curl_easy_perform(CURL *easy) { - struct SessionHandle *data = (struct SessionHandle *)curl; - - if(!data) - return; - - Curl_close(data); + return easy_perform(easy, FALSE); } +#ifdef CURLDEBUG /* - * Store a pointed to the multi handle within the easy handle's data struct. + * curl_easy_perform_ev() is the external interface that performs a blocking + * transfer using the event-based API internally. */ -void Curl_easy_addmulti(struct SessionHandle *data, - void *multi) +CURLcode curl_easy_perform_ev(CURL *easy) { - data->multi = multi; + return easy_perform(easy, TRUE); } -void Curl_easy_initHandleData(struct SessionHandle *data) +#endif + +/* + * curl_easy_cleanup() is the external interface to cleaning/freeing the given + * easy handle. + */ +void curl_easy_cleanup(CURL *curl) { - memset(&data->req, 0, sizeof(struct SingleRequest)); + struct SessionHandle *data = (struct SessionHandle *)curl; + SIGPIPE_VARIABLE(pipe_st); - data->req.maxdownload = -1; + if(!data) + return; + + sigpipe_ignore(data, &pipe_st); + Curl_close(data); + sigpipe_restore(&pipe_st); } /* @@ -504,12 +895,16 @@ CURLcode curl_easy_getinfo(CURL *curl, CURLINFO info, ...) { va_list arg; void *paramp; + CURLcode ret; struct SessionHandle *data = (struct SessionHandle *)curl; va_start(arg, info); paramp = va_arg(arg, void *); - return Curl_getinfo(data, info, paramp); + ret = Curl_getinfo(data, info, paramp); + + va_end(arg); + return ret; } /* @@ -587,8 +982,6 @@ CURL *curl_easy_duphandle(CURL *incurl) Curl_convert_setup(outcurl); - Curl_easy_initHandleData(outcurl); - outcurl->magic = CURLEASY_MAGIC_NUMBER; /* we reach this point and thus we are OK */ @@ -622,7 +1015,7 @@ void curl_easy_reset(CURL *curl) data->state.path = NULL; - Curl_safefree(data->state.proto.generic); + Curl_free_request_state(data); /* zero out UserDefined data: */ Curl_freeset(data); @@ -632,9 +1025,6 @@ void curl_easy_reset(CURL *curl) /* zero out Progress data: */ memset(&data->progress, 0, sizeof(struct Progress)); - /* init Handle data */ - Curl_easy_initHandleData(data); - data->progress.flags |= PGRS_HIDE; data->state.current_speed = -1; /* init to negative == impossible */ } @@ -696,7 +1086,7 @@ CURLcode curl_easy_pause(CURL *curl, int action) do { chunklen = (tempsize > CURL_MAX_WRITE_SIZE)?CURL_MAX_WRITE_SIZE:tempsize; - result = Curl_client_write(data->state.current_conn, + result = Curl_client_write(data->easy_conn, temptype, tempwrite, chunklen); if(result) /* failures abort the loop at once */ @@ -738,6 +1128,13 @@ CURLcode curl_easy_pause(CURL *curl, int action) free(freewrite); /* this is unconditionally no longer used */ } + /* if there's no error and we're not pausing both directions, we want + to have this handle checked soon */ + if(!result && + ((newstate&(KEEP_RECV_PAUSE|KEEP_SEND_PAUSE)) != + (KEEP_RECV_PAUSE|KEEP_SEND_PAUSE)) ) + Curl_expire(data, 1); /* get this handle going again */ + return result; } diff --git a/plugins/FTPFileYM/curl/lib/easyif.h b/plugins/FTPFileYM/curl/lib/easyif.h index 1f521689f6..043ff437d4 100644 --- a/plugins/FTPFileYM/curl/lib/easyif.h +++ b/plugins/FTPFileYM/curl/lib/easyif.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2011, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2013, 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 @@ -25,9 +25,9 @@ /* * Prototypes for library-wide functions provided by easy.c */ -void Curl_easy_addmulti(struct SessionHandle *data, void *multi); - -void Curl_easy_initHandleData(struct SessionHandle *data); +#ifdef CURLDEBUG +CURL_EXTERN CURLcode curl_easy_perform_ev(CURL *easy); +#endif #endif /* HEADER_CURL_EASYIF_H */ diff --git a/plugins/FTPFileYM/curl/lib/escape.c b/plugins/FTPFileYM/curl/lib/escape.c index 6a26cf8ef9..aa7db2c5b9 100644 --- a/plugins/FTPFileYM/curl/lib/escape.c +++ b/plugins/FTPFileYM/curl/lib/escape.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2011, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2013, 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 @@ -159,7 +159,8 @@ CURLcode Curl_urldecode(struct SessionHandle *data, while(--alloc > 0) { in = *string; - if(('%' == in) && ISXDIGIT(string[1]) && ISXDIGIT(string[2])) { + if(('%' == in) && (alloc > 2) && + ISXDIGIT(string[1]) && ISXDIGIT(string[2])) { /* this is two hexadecimal digits following a '%' */ char hexstr[3]; char *ptr; diff --git a/plugins/FTPFileYM/curl/lib/file.c b/plugins/FTPFileYM/curl/lib/file.c index 038bf42e17..e658ada0aa 100644 --- a/plugins/FTPFileYM/curl/lib/file.c +++ b/plugins/FTPFileYM/curl/lib/file.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2012, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2013, 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 @@ -90,7 +90,7 @@ static CURLcode file_done(struct connectdata *conn, static CURLcode file_connect(struct connectdata *conn, bool *done); static CURLcode file_disconnect(struct connectdata *conn, bool dead_connection); - +static CURLcode file_setup_connection(struct connectdata *conn); /* * FILE scheme handler. @@ -98,7 +98,7 @@ static CURLcode file_disconnect(struct connectdata *conn, const struct Curl_handler Curl_handler_file = { "FILE", /* scheme */ - ZERO_NULL, /* setup_connection */ + file_setup_connection, /* setup_connection */ file_do, /* do_it */ file_done, /* done */ ZERO_NULL, /* do_more */ @@ -117,6 +117,16 @@ const struct Curl_handler Curl_handler_file = { }; +static CURLcode file_setup_connection(struct connectdata *conn) +{ + /* allocate the FILE specific struct */ + conn->data->req.protop = calloc(1, sizeof(struct FILEPROTO)); + if(!conn->data->req.protop) + return CURLE_OUT_OF_MEMORY; + + return CURLE_OK; +} + /* Check if this is a range download, and if so, set the internal variables properly. This code is copied from the FTP implementation and might as @@ -179,39 +189,17 @@ static CURLcode file_connect(struct connectdata *conn, bool *done) { struct SessionHandle *data = conn->data; char *real_path; - struct FILEPROTO *file; + struct FILEPROTO *file = data->req.protop; int fd; #ifdef DOS_FILESYSTEM int i; char *actual_path; #endif - /* If there already is a protocol-specific struct allocated for this - sessionhandle, deal with it */ - Curl_reset_reqproto(conn); - real_path = curl_easy_unescape(data, data->state.path, 0, NULL); if(!real_path) return CURLE_OUT_OF_MEMORY; - if(!data->state.proto.file) { - file = calloc(1, sizeof(struct FILEPROTO)); - if(!file) { - free(real_path); - return CURLE_OUT_OF_MEMORY; - } - data->state.proto.file = file; - } - else { - /* file is not a protocol that can deal with "persistancy" */ - file = data->state.proto.file; - Curl_safefree(file->freepath); - file->path = NULL; - if(file->fd != -1) - close(file->fd); - file->fd = -1; - } - #ifdef DOS_FILESYSTEM /* If the first character is a slash, and there's something that looks like a drive at the beginning of @@ -262,7 +250,7 @@ static CURLcode file_connect(struct connectdata *conn, bool *done) static CURLcode file_done(struct connectdata *conn, CURLcode status, bool premature) { - struct FILEPROTO *file = conn->data->state.proto.file; + struct FILEPROTO *file = conn->data->req.protop; (void)status; /* not used */ (void)premature; /* not used */ @@ -280,7 +268,7 @@ static CURLcode file_done(struct connectdata *conn, static CURLcode file_disconnect(struct connectdata *conn, bool dead_connection) { - struct FILEPROTO *file = conn->data->state.proto.file; + struct FILEPROTO *file = conn->data->req.protop; (void)dead_connection; /* not used */ if(file) { @@ -302,7 +290,7 @@ static CURLcode file_disconnect(struct connectdata *conn, static CURLcode file_upload(struct connectdata *conn) { - struct FILEPROTO *file = conn->data->state.proto.file; + struct FILEPROTO *file = conn->data->req.protop; const char *dir = strchr(file->path, DIRSEP); int fd; int mode; @@ -440,6 +428,7 @@ static CURLcode file_do(struct connectdata *conn, bool *done) curl_off_t bytecount = 0; int fd; struct timeval now = Curl_tvnow(); + struct FILEPROTO *file; *done = TRUE; /* unconditionally */ @@ -449,8 +438,10 @@ static CURLcode file_do(struct connectdata *conn, bool *done) if(data->set.upload) return file_upload(conn); + file = conn->data->req.protop; + /* get the fd from the connection phase */ - fd = conn->data->state.proto.file->fd; + fd = file->fd; /* VMS: This only works reliable for STREAMLF files */ if(-1 != fstat(fd, &statbuf)) { diff --git a/plugins/FTPFileYM/curl/lib/formdata.c b/plugins/FTPFileYM/curl/lib/formdata.c index 2ba58dda1c..f718a3e4e9 100644 --- a/plugins/FTPFileYM/curl/lib/formdata.c +++ b/plugins/FTPFileYM/curl/lib/formdata.c @@ -24,9 +24,6 @@ #include <curl/curl.h> -/* Length of the random boundary string. */ -#define BOUNDARY_LENGTH 40 - #if !defined(CURL_DISABLE_HTTP) || defined(USE_SSLEAY) #if defined(HAVE_LIBGEN_H) && defined(HAVE_BASENAME) @@ -35,7 +32,7 @@ #include "urldata.h" /* for struct SessionHandle */ #include "formdata.h" -#include "curl_rand.h" +#include "sslgen.h" #include "strequal.h" #include "curl_memory.h" #include "sendf.h" @@ -56,6 +53,7 @@ static char *Curl_basename(char *path); #endif static size_t readfromfile(struct Form *form, char *buffer, size_t size); +static char *formboundary(struct SessionHandle *data); /* What kind of Content-Type to use on un-specified files with unrecognized extensions. */ @@ -170,8 +168,8 @@ static FormInfo * AddFormInfo(char *value, * Returns some valid contenttype for filename. * ***************************************************************************/ -static const char * ContentTypeForFilename (const char *filename, - const char *prevtype) +static const char *ContentTypeForFilename(const char *filename, + const char *prevtype) { const char *contenttype = NULL; unsigned int i; @@ -180,7 +178,7 @@ static const char * ContentTypeForFilename (const char *filename, * extensions and pick the first we match! */ struct ContentType { - char extension[6]; + const char *extension; const char *type; }; static const struct ContentType ctts[]={ @@ -428,7 +426,7 @@ CURLFORMcode FormAdd(struct curl_httppost **httppost, /* Get contents from a given file name */ case CURLFORM_FILECONTENT: - if(current_form->flags != 0) + if(current_form->flags & (HTTPPOST_PTRCONTENTS|HTTPPOST_READFILE)) return_value = CURL_FORMADD_OPTION_TWICE; else { const char *filename = array_state? @@ -669,9 +667,11 @@ CURLFORMcode FormAdd(struct curl_httppost **httppost, if(((form->flags & HTTPPOST_FILENAME) || (form->flags & HTTPPOST_BUFFER)) && !form->contenttype ) { + char *f = form->flags & HTTPPOST_BUFFER? + form->showfilename : form->value; + /* our contenttype is missing */ - form->contenttype - = strdup(ContentTypeForFilename(form->value, prevtype)); + form->contenttype = strdup(ContentTypeForFilename(f, prevtype)); if(!form->contenttype) { return_value = CURL_FORMADD_MEMORY; break; @@ -776,6 +776,70 @@ CURLFORMcode curl_formadd(struct curl_httppost **httppost, return result; } +#ifdef __VMS +#include <fabdef.h> +/* + * get_vms_file_size does what it takes to get the real size of the file + * + * For fixed files, find out the size of the EOF block and adjust. + * + * For all others, have to read the entire file in, discarding the contents. + * Most posted text files will be small, and binary files like zlib archives + * and CD/DVD images should be either a STREAM_LF format or a fixed format. + * + */ +curl_off_t VmsRealFileSize(const char * name, + const struct_stat * stat_buf) +{ + char buffer[8192]; + curl_off_t count; + int ret_stat; + FILE * file; + + file = fopen(name, "r"); + if(file == NULL) + return 0; + + count = 0; + ret_stat = 1; + while(ret_stat > 0) { + ret_stat = fread(buffer, 1, sizeof(buffer), file); + if(ret_stat != 0) + count += ret_stat; + } + fclose(file); + + return count; +} + +/* + * + * VmsSpecialSize checks to see if the stat st_size can be trusted and + * if not to call a routine to get the correct size. + * + */ +static curl_off_t VmsSpecialSize(const char * name, + const struct_stat * stat_buf) +{ + switch(stat_buf->st_fab_rfm) { + case FAB$C_VAR: + case FAB$C_VFC: + return VmsRealFileSize(name, stat_buf); + break; + default: + return stat_buf->st_size; + } +} + +#endif + +#ifndef __VMS +#define filesize(name, stat_data) (stat_data.st_size) +#else + /* Getting the expected file size needs help on VMS */ +#define filesize(name, stat_data) VmsSpecialSize(name, &stat_data) +#endif + /* * AddFormData() adds a chunk of data to the FormData linked list. * @@ -830,8 +894,8 @@ static CURLcode AddFormData(struct FormData **formp, file */ if(!strequal("-", newform->line)) { struct_stat file; - if(!stat(newform->line, &file) && S_ISREG(file.st_mode)) - *size += file.st_size; + if(!stat(newform->line, &file) && !S_ISDIR(file.st_mode)) + *size += filesize(newform->line, file); else return CURLE_BAD_FUNCTION_ARGUMENT; } @@ -1100,7 +1164,7 @@ CURLcode Curl_getformdata(struct SessionHandle *data, if(!post) return result; /* no input => no output! */ - boundary = Curl_FormBoundary(); + boundary = formboundary(data); if(!boundary) return CURLE_OUT_OF_MEMORY; @@ -1156,7 +1220,7 @@ CURLcode Curl_getformdata(struct SessionHandle *data, the magic to include several files with the same field name */ Curl_safefree(fileboundary); - fileboundary = Curl_FormBoundary(); + fileboundary = formboundary(data); if(!fileboundary) { result = CURLE_OUT_OF_MEMORY; break; @@ -1342,6 +1406,36 @@ int Curl_FormInit(struct Form *form, struct FormData *formdata ) return 0; } +#ifndef __VMS +# define fopen_read fopen +#else + /* + * vmsfopenread + * + * For upload to work as expected on VMS, different optional + * parameters must be added to the fopen command based on + * record format of the file. + * + */ +# define fopen_read vmsfopenread +static FILE * vmsfopenread(const char *file, const char *mode) { + struct_stat statbuf; + int result; + + result = stat(file, &statbuf); + + switch (statbuf.st_fab_rfm) { + case FAB$C_VAR: + case FAB$C_VFC: + case FAB$C_STMCR: + return fopen(file, "r"); + break; + default: + return fopen(file, "r", "rfm=stmlf", "ctx=stm"); + } +} +#endif + /* * readfromfile() * @@ -1364,7 +1458,7 @@ static size_t readfromfile(struct Form *form, char *buffer, else { if(!form->fp) { /* this file hasn't yet been opened */ - form->fp = fopen(form->data->line, "rb"); /* b is for binary */ + form->fp = fopen_read(form->data->line, "rb"); /* b is for binary */ if(!form->fp) return (size_t)-1; /* failure */ } @@ -1459,6 +1553,18 @@ char *Curl_formpostheader(void *formp, size_t *len) return header; } +/* + * formboundary() creates a suitable boundary string and returns an allocated + * one. + */ +static char *formboundary(struct SessionHandle *data) +{ + /* 24 dashes and 16 hexadecimal digits makes 64 bit (18446744073709551615) + combinations */ + return aprintf("------------------------%08x%08x", + Curl_rand(data), Curl_rand(data)); +} + #else /* CURL_DISABLE_HTTP */ CURLFORMcode curl_formadd(struct curl_httppost **httppost, struct curl_httppost **last_post, @@ -1484,37 +1590,5 @@ void curl_formfree(struct curl_httppost *form) /* does nothing HTTP is disabled */ } -#endif /* CURL_DISABLE_HTTP */ -#if !defined(CURL_DISABLE_HTTP) || defined(USE_SSLEAY) - -/* - * Curl_FormBoundary() creates a suitable boundary string and returns an - * allocated one. This is also used by SSL-code so it must be present even - * if HTTP is disabled! - */ -char *Curl_FormBoundary(void) -{ - char *retstring; - size_t i; - - static const char table16[]="0123456789abcdef"; - - retstring = malloc(BOUNDARY_LENGTH+1); - - if(!retstring) - return NULL; /* failed */ - - strcpy(retstring, "----------------------------"); - - for(i=strlen(retstring); i<BOUNDARY_LENGTH; i++) - retstring[i] = table16[Curl_rand()%16]; - - /* 28 dashes and 12 hexadecimal digits makes 12^16 (184884258895036416) - combinations */ - retstring[BOUNDARY_LENGTH]=0; /* zero terminate */ - - return retstring; -} - -#endif /* !defined(CURL_DISABLE_HTTP) || defined(USE_SSLEAY) */ +#endif /* !defined(CURL_DISABLE_HTTP) */ diff --git a/plugins/FTPFileYM/curl/lib/ftp.c b/plugins/FTPFileYM/curl/lib/ftp.c index 469b887496..e818af6c58 100644 --- a/plugins/FTPFileYM/curl/lib/ftp.c +++ b/plugins/FTPFileYM/curl/lib/ftp.c @@ -59,11 +59,7 @@ #include "ftp.h" #include "fileinfo.h" #include "ftplistparser.h" - -#if defined(HAVE_KRB4) || defined(HAVE_GSSAPI) -#include "krb4.h" -#endif - +#include "curl_sec.h" #include "strtoofft.h" #include "strequal.h" #include "sslgen.h" @@ -123,8 +119,8 @@ static void ftp_pasv_verbose(struct connectdata *conn, char *newhost, /* ascii version */ int port); #endif -static CURLcode ftp_state_post_rest(struct connectdata *conn); -static CURLcode ftp_state_post_cwd(struct connectdata *conn); +static CURLcode ftp_state_prepare_transfer(struct connectdata *conn); +static CURLcode ftp_state_mdtm(struct connectdata *conn); static CURLcode ftp_state_quote(struct connectdata *conn, bool init, ftpstate instate); static CURLcode ftp_nb_type(struct connectdata *conn, @@ -136,7 +132,7 @@ static CURLcode ftp_done(struct connectdata *conn, CURLcode, bool premature); static CURLcode ftp_connect(struct connectdata *conn, bool *done); static CURLcode ftp_disconnect(struct connectdata *conn, bool dead_connection); -static CURLcode ftp_do_more(struct connectdata *conn, bool *completed); +static CURLcode ftp_do_more(struct connectdata *conn, int *completed); static CURLcode ftp_multi_statemach(struct connectdata *conn, bool *done); static int ftp_getsock(struct connectdata *conn, curl_socket_t *socks, int numsocks); @@ -151,8 +147,7 @@ static CURLcode wc_statemach(struct connectdata *conn); static void wc_data_dtor(void *ptr); -static CURLcode ftp_state_post_retr_size(struct connectdata *conn, - curl_off_t filesize); +static CURLcode ftp_state_retr(struct connectdata *conn, curl_off_t filesize); static CURLcode ftp_readresp(curl_socket_t sockfd, struct pingpong *pp, @@ -162,8 +157,6 @@ static CURLcode ftp_dophase_done(struct connectdata *conn, bool connected); /* easy-to-use macro: */ -#define FTPSENDF(x,y,z) if((result = Curl_ftpsendf(x,y,z)) != CURLE_OK) \ - return result #define PPSENDF(x,y,z) if((result = Curl_pp_sendf(x,y,z)) != CURLE_OK) \ return result @@ -228,7 +221,7 @@ const struct Curl_handler Curl_handler_ftps = { static const struct Curl_handler Curl_handler_ftp_proxy = { "FTP", /* scheme */ - ZERO_NULL, /* setup_connection */ + Curl_http_setup_conn, /* setup_connection */ Curl_http, /* do_it */ Curl_http_done, /* done */ ZERO_NULL, /* do_more */ @@ -254,7 +247,7 @@ static const struct Curl_handler Curl_handler_ftp_proxy = { static const struct Curl_handler Curl_handler_ftps_proxy = { "FTPS", /* scheme */ - ZERO_NULL, /* setup_connection */ + Curl_http_setup_conn, /* setup_connection */ Curl_http, /* do_it */ Curl_http_done, /* done */ ZERO_NULL, /* do_more */ @@ -496,7 +489,7 @@ static CURLcode ReceivedServerConnect(struct connectdata *conn, bool *received) static CURLcode InitiateTransfer(struct connectdata *conn) { struct SessionHandle *data = conn->data; - struct FTP *ftp = data->state.proto.ftp; + struct FTP *ftp = data->req.protop; CURLcode result = CURLE_OK; if(conn->ssl[SECONDARYSOCKET].use) { @@ -598,17 +591,17 @@ static CURLcode AllowServerConnect(struct connectdata *conn, bool *connected) /* macro to check for the last line in an FTP server response */ #define LASTLINE(line) (STATUSCODE(line) && (' ' == line[3])) -static int ftp_endofresp(struct pingpong *pp, - int *code) +static bool ftp_endofresp(struct connectdata *conn, char *line, size_t len, + int *code) { - char *line = pp->linestart_resp; - size_t len = pp->nread_resp; + (void)conn; if((len > 3) && LASTLINE(line)) { *code = curlx_sltosi(strtol(line, NULL, 10)); - return 1; + return TRUE; } - return 0; + + return FALSE; } static CURLcode ftp_readresp(curl_socket_t sockfd, @@ -618,7 +611,7 @@ static CURLcode ftp_readresp(curl_socket_t sockfd, { struct connectdata *conn = pp->conn; struct SessionHandle *data = conn->data; -#if defined(HAVE_KRB4) || defined(HAVE_GSSAPI) +#ifdef HAVE_GSSAPI char * const buf = data->state.buffer; #endif CURLcode result = CURLE_OK; @@ -626,7 +619,7 @@ static CURLcode ftp_readresp(curl_socket_t sockfd, result = Curl_pp_readresp(sockfd, pp, &code, size); -#if defined(HAVE_KRB4) || defined(HAVE_GSSAPI) +#if defined(HAVE_GSSAPI) /* handle the security-oriented responses 6xx ***/ /* FIXME: some errorchecking perhaps... ***/ switch(code) { @@ -778,6 +771,47 @@ CURLcode Curl_GetFTPResponse(ssize_t *nreadp, /* return number of bytes read */ return result; } +#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS) + /* for debug purposes */ +static const char * const ftp_state_names[]={ + "STOP", + "WAIT220", + "AUTH", + "USER", + "PASS", + "ACCT", + "PBSZ", + "PROT", + "CCC", + "PWD", + "SYST", + "NAMEFMT", + "QUOTE", + "RETR_PREQUOTE", + "STOR_PREQUOTE", + "POSTQUOTE", + "CWD", + "MKD", + "MDTM", + "TYPE", + "LIST_TYPE", + "RETR_TYPE", + "STOR_TYPE", + "SIZE", + "RETR_SIZE", + "STOR_SIZE", + "REST", + "RETR_REST", + "PORT", + "PRET", + "PASV", + "LIST", + "RETR", + "STOR", + "QUIT" +}; +#endif + /* This is the ONLY way to change FTP state! */ static void _state(struct connectdata *conn, ftpstate newstate @@ -786,51 +820,12 @@ static void _state(struct connectdata *conn, #endif ) { -#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS) - /* for debug purposes */ - static const char * const names[]={ - "STOP", - "WAIT220", - "AUTH", - "USER", - "PASS", - "ACCT", - "PBSZ", - "PROT", - "CCC", - "PWD", - "SYST", - "NAMEFMT", - "QUOTE", - "RETR_PREQUOTE", - "STOR_PREQUOTE", - "POSTQUOTE", - "CWD", - "MKD", - "MDTM", - "TYPE", - "LIST_TYPE", - "RETR_TYPE", - "STOR_TYPE", - "SIZE", - "RETR_SIZE", - "STOR_SIZE", - "REST", - "RETR_REST", - "PORT", - "PRET", - "PASV", - "LIST", - "RETR", - "STOR", - "QUIT" - }; -#endif struct ftp_conn *ftpc = &conn->proto.ftpc; #if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS) if(ftpc->state != newstate) infof(conn->data, "FTP %p (line %d) state change from %s to %s\n", - ftpc, lineno, names[ftpc->state], names[newstate]); + (void *)ftpc, lineno, ftp_state_names[ftpc->state], + ftp_state_names[newstate]); #endif ftpc->state = newstate; } @@ -838,7 +833,7 @@ static void _state(struct connectdata *conn, static CURLcode ftp_state_user(struct connectdata *conn) { CURLcode result; - struct FTP *ftp = conn->data->state.proto.ftp; + struct FTP *ftp = conn->data->req.protop; /* send USER */ PPSENDF(&conn->proto.ftpc.pp, "USER %s", ftp->user?ftp->user:""); @@ -853,7 +848,7 @@ static CURLcode ftp_state_pwd(struct connectdata *conn) CURLcode result; /* send PWD to discover our entry point */ - PPSENDF(&conn->proto.ftpc.pp, "PWD", NULL); + PPSENDF(&conn->proto.ftpc.pp, "%s", "PWD"); state(conn, FTP_PWD); return CURLE_OK; @@ -877,29 +872,21 @@ static int ftp_domore_getsock(struct connectdata *conn, curl_socket_t *socks, return GETSOCK_BLANK; /* When in DO_MORE state, we could be either waiting for us to connect to a - remote site, or we could wait for that site to connect to us. Or just - handle ordinary commands. - - When waiting for a connect, we will be in FTP_STOP state and then we wait - for the secondary socket to become writeable. If we're in another state, - we're still handling commands on the control (primary) connection. + * remote site, or we could wait for that site to connect to us. Or just + * handle ordinary commands. + */ - */ + if(FTP_STOP == ftpc->state) { + /* if stopped and still in this state, then we're also waiting for a + connect on the secondary connection */ + socks[0] = conn->sock[FIRSTSOCKET]; + socks[1] = conn->sock[SECONDARYSOCKET]; - switch(ftpc->state) { - case FTP_STOP: - break; - default: - return Curl_pp_getsock(&conn->proto.ftpc.pp, socks, numsocks); - } - - socks[0] = conn->sock[SECONDARYSOCKET]; - if(ftpc->wait_data_conn) { - socks[1] = conn->sock[FIRSTSOCKET]; - return GETSOCK_READSOCK(0) | GETSOCK_READSOCK(1); + return GETSOCK_READSOCK(FIRSTSOCKET) | + GETSOCK_WRITESOCK(SECONDARYSOCKET); } - - return GETSOCK_READSOCK(0); + else + return Curl_pp_getsock(&conn->proto.ftpc.pp, socks, numsocks); } /* This is called after the FTP_QUOTE state is passed. @@ -915,7 +902,7 @@ static CURLcode ftp_state_cwd(struct connectdata *conn) if(ftpc->cwddone) /* already done and fine */ - result = ftp_state_post_cwd(conn); + result = ftp_state_mdtm(conn); else { ftpc->count2 = 0; /* count2 counts failed CWDs */ @@ -943,7 +930,7 @@ static CURLcode ftp_state_cwd(struct connectdata *conn) } else { /* No CWD necessary */ - result = ftp_state_post_cwd(conn); + result = ftp_state_mdtm(conn); } } } @@ -1068,12 +1055,17 @@ static CURLcode ftp_state_use_port(struct connectdata *conn, if(*addr != '\0') { /* attempt to get the address of the given interface name */ - if(!Curl_if2ip(conn->ip_addr->ai_family, addr, - hbuf, sizeof(hbuf))) - /* not an interface, use the given string as host name instead */ - host = addr; - else - host = hbuf; /* use the hbuf for host name */ + switch(Curl_if2ip(conn->ip_addr->ai_family, conn->scope, addr, + hbuf, sizeof(hbuf))) { + case IF2IP_NOT_FOUND: + /* not an interface, use the given string as host name instead */ + host = addr; + break; + case IF2IP_AF_NOT_SUPPORTED: + return CURLE_FTP_PORT_FAILED; + case IF2IP_FOUND: + host = hbuf; /* use the hbuf for host name */ + } } else /* there was only a port(-range) given, default the host */ @@ -1368,13 +1360,17 @@ static CURLcode ftp_state_use_pasv(struct connectdata *conn) return result; } -/* REST is the last command in the chain of commands when a "head"-like - request is made. Thus, if an actual transfer is to be made this is where - we take off for real. */ -static CURLcode ftp_state_post_rest(struct connectdata *conn) +/* + * ftp_state_prepare_transfer() starts PORT, PASV or PRET etc. + * + * REST is the last command in the chain of commands when a "head"-like + * request is made. Thus, if an actual transfer is to be made this is where we + * take off for real. + */ +static CURLcode ftp_state_prepare_transfer(struct connectdata *conn) { CURLcode result = CURLE_OK; - struct FTP *ftp = conn->data->state.proto.ftp; + struct FTP *ftp = conn->data->req.protop; struct SessionHandle *data = conn->data; if(ftp->transfer != FTPTRANSFER_BODY) { @@ -1414,10 +1410,10 @@ static CURLcode ftp_state_post_rest(struct connectdata *conn) return result; } -static CURLcode ftp_state_post_size(struct connectdata *conn) +static CURLcode ftp_state_rest(struct connectdata *conn) { CURLcode result = CURLE_OK; - struct FTP *ftp = conn->data->state.proto.ftp; + struct FTP *ftp = conn->data->req.protop; struct ftp_conn *ftpc = &conn->proto.ftpc; if((ftp->transfer != FTPTRANSFER_BODY) && ftpc->file) { @@ -1430,15 +1426,15 @@ static CURLcode ftp_state_post_size(struct connectdata *conn) state(conn, FTP_REST); } else - result = ftp_state_post_rest(conn); + result = ftp_state_prepare_transfer(conn); return result; } -static CURLcode ftp_state_post_type(struct connectdata *conn) +static CURLcode ftp_state_size(struct connectdata *conn) { CURLcode result = CURLE_OK; - struct FTP *ftp = conn->data->state.proto.ftp; + struct FTP *ftp = conn->data->req.protop; struct ftp_conn *ftpc = &conn->proto.ftpc; if((ftp->transfer == FTPTRANSFER_INFO) && ftpc->file) { @@ -1450,12 +1446,12 @@ static CURLcode ftp_state_post_type(struct connectdata *conn) state(conn, FTP_SIZE); } else - result = ftp_state_post_size(conn); + result = ftp_state_rest(conn); return result; } -static CURLcode ftp_state_post_listtype(struct connectdata *conn) +static CURLcode ftp_state_list(struct connectdata *conn) { CURLcode result = CURLE_OK; struct SessionHandle *data = conn->data; @@ -1524,7 +1520,7 @@ static CURLcode ftp_state_post_listtype(struct connectdata *conn) return result; } -static CURLcode ftp_state_post_retrtype(struct connectdata *conn) +static CURLcode ftp_state_retr_prequote(struct connectdata *conn) { CURLcode result = CURLE_OK; @@ -1535,7 +1531,7 @@ static CURLcode ftp_state_post_retrtype(struct connectdata *conn) return result; } -static CURLcode ftp_state_post_stortype(struct connectdata *conn) +static CURLcode ftp_state_stor_prequote(struct connectdata *conn) { CURLcode result = CURLE_OK; @@ -1546,10 +1542,10 @@ static CURLcode ftp_state_post_stortype(struct connectdata *conn) return result; } -static CURLcode ftp_state_post_mdtm(struct connectdata *conn) +static CURLcode ftp_state_type(struct connectdata *conn) { CURLcode result = CURLE_OK; - struct FTP *ftp = conn->data->state.proto.ftp; + struct FTP *ftp = conn->data->req.protop; struct SessionHandle *data = conn->data; struct ftp_conn *ftpc = &conn->proto.ftpc; @@ -1572,14 +1568,14 @@ static CURLcode ftp_state_post_mdtm(struct connectdata *conn) return result; } else - result = ftp_state_post_type(conn); + result = ftp_state_size(conn); return result; } /* This is called after the CWD commands have been done in the beginning of the DO phase */ -static CURLcode ftp_state_post_cwd(struct connectdata *conn) +static CURLcode ftp_state_mdtm(struct connectdata *conn) { CURLcode result = CURLE_OK; struct SessionHandle *data = conn->data; @@ -1595,7 +1591,7 @@ static CURLcode ftp_state_post_cwd(struct connectdata *conn) state(conn, FTP_MDTM); } else - result = ftp_state_post_mdtm(conn); + result = ftp_state_type(conn); return result; } @@ -1606,7 +1602,7 @@ static CURLcode ftp_state_ul_setup(struct connectdata *conn, bool sizechecked) { CURLcode result = CURLE_OK; - struct FTP *ftp = conn->data->state.proto.ftp; + struct FTP *ftp = conn->data->req.protop; struct SessionHandle *data = conn->data; struct ftp_conn *ftpc = &conn->proto.ftpc; int seekerr = CURL_SEEKFUNC_OK; @@ -1704,7 +1700,7 @@ static CURLcode ftp_state_quote(struct connectdata *conn, { CURLcode result = CURLE_OK; struct SessionHandle *data = conn->data; - struct FTP *ftp = data->state.proto.ftp; + struct FTP *ftp = data->req.protop; struct ftp_conn *ftpc = &conn->proto.ftpc; bool quote=FALSE; struct curl_slist *item; @@ -1770,7 +1766,7 @@ static CURLcode ftp_state_quote(struct connectdata *conn, else { if(ftpc->known_filesize != -1) { Curl_pgrsSetDownloadSize(data, ftpc->known_filesize); - result = ftp_state_post_retr_size(conn, ftpc->known_filesize); + result = ftp_state_retr(conn, ftpc->known_filesize); } else { PPSENDF(&ftpc->pp, "SIZE %s", ftpc->file); @@ -1794,15 +1790,15 @@ static CURLcode ftp_state_quote(struct connectdata *conn, static CURLcode ftp_epsv_disable(struct connectdata *conn) { CURLcode result = CURLE_OK; - infof(conn->data, "got positive EPSV response, but can't connect. " - "Disabling EPSV\n"); + infof(conn->data, "Failed EPSV attempt. Disabling EPSV\n"); /* disable it for next transfer */ conn->bits.ftp_use_epsv = FALSE; conn->data->state.errorbuf = FALSE; /* allow error message to get rewritten */ - PPSENDF(&conn->proto.ftpc.pp, "PASV", NULL); + PPSENDF(&conn->proto.ftpc.pp, "%s", "PASV"); conn->proto.ftpc.count1++; - /* remain in the FTP_PASV state */ + /* remain in/go to the FTP_PASV state */ + state(conn, FTP_PASV); return result; } @@ -1931,28 +1927,18 @@ static CURLcode ftp_state_pasv_resp(struct connectdata *conn, } else if(ftpc->count1 == 0) { /* EPSV failed, move on to PASV */ - - /* disable it for next transfer */ - conn->bits.ftp_use_epsv = FALSE; - infof(data, "disabling EPSV usage\n"); - - PPSENDF(&ftpc->pp, "PASV", NULL); - ftpc->count1++; - /* remain in the FTP_PASV state */ - return result; + return ftp_epsv_disable(conn); } else { failf(data, "Bad PASV/EPSV response: %03d", ftpcode); return CURLE_FTP_WEIRD_PASV_REPLY; } - if(data->set.str[STRING_PROXY] && *data->set.str[STRING_PROXY]) { + if(conn->bits.proxy) { /* - * This is a tunnel through a http proxy and we need to connect to the - * proxy again here. - * - * We don't want to rely on a former host lookup that might've expired - * now, instead we remake the lookup here and now! + * This connection uses a proxy and we need to connect to the proxy again + * here. We don't want to rely on a former host lookup that might've + * expired now, instead we remake the lookup here and now! */ rc = Curl_resolv(conn, conn->proxy.name, (int)conn->port, &addr); if(rc == CURLRESOLV_PENDING) @@ -2018,14 +2004,17 @@ static CURLcode ftp_state_pasv_resp(struct connectdata *conn, case CURLPROXY_SOCKS5_HOSTNAME: result = Curl_SOCKS5(conn->proxyuser, conn->proxypasswd, newhost, newport, SECONDARYSOCKET, conn); + connected = TRUE; break; case CURLPROXY_SOCKS4: result = Curl_SOCKS4(conn->proxyuser, newhost, newport, SECONDARYSOCKET, conn, FALSE); + connected = TRUE; break; case CURLPROXY_SOCKS4A: result = Curl_SOCKS4(conn->proxyuser, newhost, newport, SECONDARYSOCKET, conn, TRUE); + connected = TRUE; break; case CURLPROXY_HTTP: case CURLPROXY_HTTP_1_0: @@ -2057,13 +2046,13 @@ static CURLcode ftp_state_pasv_resp(struct connectdata *conn, * FTP pointer */ struct HTTP http_proxy; - struct FTP *ftp_save = data->state.proto.ftp; + struct FTP *ftp_save = data->req.protop; memset(&http_proxy, 0, sizeof(http_proxy)); - data->state.proto.http = &http_proxy; + data->req.protop = &http_proxy; result = Curl_proxyCONNECT(conn, SECONDARYSOCKET, newhost, newport); - data->state.proto.ftp = ftp_save; + data->req.protop = ftp_save; if(result) return result; @@ -2077,8 +2066,7 @@ static CURLcode ftp_state_pasv_resp(struct connectdata *conn, } } - conn->bits.tcpconnect[SECONDARYSOCKET] = TRUE; - + conn->bits.tcpconnect[SECONDARYSOCKET] = connected; conn->bits.do_more = TRUE; state(conn, FTP_STOP); /* this phase is completed */ @@ -2124,7 +2112,7 @@ static CURLcode ftp_state_mdtm_resp(struct connectdata *conn, { CURLcode result = CURLE_OK; struct SessionHandle *data=conn->data; - struct FTP *ftp = data->state.proto.ftp; + struct FTP *ftp = data->req.protop; struct ftp_conn *ftpc = &conn->proto.ftpc; switch(ftpcode) { @@ -2218,7 +2206,7 @@ static CURLcode ftp_state_mdtm_resp(struct connectdata *conn, } if(!result) - result = ftp_state_post_mdtm(conn); + result = ftp_state_type(conn); return result; } @@ -2242,23 +2230,23 @@ static CURLcode ftp_state_type_resp(struct connectdata *conn, ftpcode); if(instate == FTP_TYPE) - result = ftp_state_post_type(conn); + result = ftp_state_size(conn); else if(instate == FTP_LIST_TYPE) - result = ftp_state_post_listtype(conn); + result = ftp_state_list(conn); else if(instate == FTP_RETR_TYPE) - result = ftp_state_post_retrtype(conn); + result = ftp_state_retr_prequote(conn); else if(instate == FTP_STOR_TYPE) - result = ftp_state_post_stortype(conn); + result = ftp_state_stor_prequote(conn); return result; } -static CURLcode ftp_state_post_retr_size(struct connectdata *conn, +static CURLcode ftp_state_retr(struct connectdata *conn, curl_off_t filesize) { CURLcode result = CURLE_OK; struct SessionHandle *data=conn->data; - struct FTP *ftp = data->state.proto.ftp; + struct FTP *ftp = data->req.protop; struct ftp_conn *ftpc = &conn->proto.ftpc; if(data->set.max_filesize && (filesize > data->set.max_filesize)) { @@ -2358,11 +2346,11 @@ static CURLcode ftp_state_size_resp(struct connectdata *conn, } #endif Curl_pgrsSetDownloadSize(data, filesize); - result = ftp_state_post_size(conn); + result = ftp_state_rest(conn); } else if(instate == FTP_RETR_SIZE) { Curl_pgrsSetDownloadSize(data, filesize); - result = ftp_state_post_retr_size(conn, filesize); + result = ftp_state_retr(conn, filesize); } else if(instate == FTP_STOR_SIZE) { data->state.resume_from = filesize; @@ -2390,7 +2378,7 @@ static CURLcode ftp_state_rest_resp(struct connectdata *conn, return result; } #endif - result = ftp_state_post_rest(conn); + result = ftp_state_prepare_transfer(conn); break; case FTP_RETR_REST: @@ -2427,6 +2415,8 @@ static CURLcode ftp_state_stor_resp(struct connectdata *conn, if(data->set.ftp_use_port) { bool connected; + state(conn, FTP_STOP); /* no longer in STOR state */ + result = AllowServerConnect(conn, &connected); if(result) return result; @@ -2450,7 +2440,7 @@ static CURLcode ftp_state_get_resp(struct connectdata *conn, { CURLcode result = CURLE_OK; struct SessionHandle *data = conn->data; - struct FTP *ftp = data->state.proto.ftp; + struct FTP *ftp = data->req.protop; char *buf = data->state.buffer; if((ftpcode == 150) || (ftpcode == 125)) { @@ -2574,19 +2564,6 @@ static CURLcode ftp_state_loggedin(struct connectdata *conn) { CURLcode result = CURLE_OK; -#ifdef HAVE_KRB4 - if(conn->data->set.krb) { - /* We may need to issue a KAUTH here to have access to the files - * do it if user supplied a password - */ - if(conn->passwd && *conn->passwd) { - /* BLOCKING */ - result = Curl_krb_kauth(conn); - if(result) - return result; - } - } -#endif if(conn->ssl[FIRSTSOCKET].use) { /* PBSZ = PROTECTION BUFFER SIZE. @@ -2618,7 +2595,7 @@ static CURLcode ftp_state_user_resp(struct connectdata *conn, { CURLcode result = CURLE_OK; struct SessionHandle *data = conn->data; - struct FTP *ftp = data->state.proto.ftp; + struct FTP *ftp = data->req.protop; struct ftp_conn *ftpc = &conn->proto.ftpc; (void)instate; /* no use for this yet */ @@ -2706,14 +2683,17 @@ static CURLcode ftp_statemach_act(struct connectdata *conn) /* we have now received a full FTP server response */ switch(ftpc->state) { case FTP_WAIT220: - if(ftpcode != 220) { + if(ftpcode == 230) + /* 230 User logged in - already! */ + return ftp_state_user_resp(conn, ftpcode, ftpc->state); + else if(ftpcode != 220) { failf(data, "Got a %03d ftp-server response when 220 was expected", ftpcode); return CURLE_FTP_WEIRD_SERVER_REPLY; } /* We have received a 220 response fine, now we proceed. */ -#if defined(HAVE_KRB4) || defined(HAVE_GSSAPI) +#ifdef HAVE_GSSAPI if(data->set.krb) { /* If not anonymous login, try a secure login. Note that this procedure is still BLOCKING. */ @@ -2828,7 +2808,7 @@ static CURLcode ftp_statemach_act(struct connectdata *conn) if(data->set.ftp_ccc) { /* CCC - Clear Command Channel */ - PPSENDF(&ftpc->pp, "CCC", NULL); + PPSENDF(&ftpc->pp, "%s", "CCC"); state(conn, FTP_CCC); } else { @@ -2866,13 +2846,19 @@ static CURLcode ftp_statemach_act(struct connectdata *conn) return CURLE_OUT_OF_MEMORY; /* Reply format is like - 257<space>"<directory-name>"<space><commentary> and the RFC959 - says + 257<space>[rubbish]"<directory-name>"<space><commentary> and the + RFC959 says The directory name can contain any character; embedded double-quotes should be escaped by double-quotes (the "quote-doubling" convention). */ + + /* scan for the first double-quote for non-standard responses */ + while(ptr < &data->state.buffer[sizeof(data->state.buffer)] + && *ptr != '\n' && *ptr != '\0' && *ptr != '"') + ptr++; + if('\"' == *ptr) { /* it started good */ ptr++; @@ -2909,7 +2895,7 @@ static CURLcode ftp_statemach_act(struct connectdata *conn) if(!ftpc->server_os && dir[0] != '/') { - result = Curl_pp_sendf(&ftpc->pp, "SYST", NULL); + result = Curl_pp_sendf(&ftpc->pp, "%s", "SYST"); if(result != CURLE_OK) { free(dir); return result; @@ -2962,7 +2948,7 @@ static CURLcode ftp_statemach_act(struct connectdata *conn) if(strequal(os, "OS/400")) { /* Force OS400 name format 1. */ - result = Curl_pp_sendf(&ftpc->pp, "SITE NAMEFMT 1", NULL); + result = Curl_pp_sendf(&ftpc->pp, "%s", "SITE NAMEFMT 1"); if(result != CURLE_OK) { free(os); return result; @@ -3040,7 +3026,7 @@ static CURLcode ftp_statemach_act(struct connectdata *conn) PPSENDF(&ftpc->pp, "CWD %s", ftpc->dirs[ftpc->count1 - 1]); } else { - result = ftp_state_post_cwd(conn); + result = ftp_state_mdtm(conn); if(result) return result; } @@ -3124,7 +3110,7 @@ static CURLcode ftp_multi_statemach(struct connectdata *conn, bool *done) { struct ftp_conn *ftpc = &conn->proto.ftpc; - CURLcode result = Curl_pp_multi_statemach(&ftpc->pp); + CURLcode result = Curl_pp_statemach(&ftpc->pp, FALSE); /* Check for the state outside of the Curl_socket_ready() return code checks since at times we are in fact already in this state when this function @@ -3134,14 +3120,14 @@ static CURLcode ftp_multi_statemach(struct connectdata *conn, return result; } -static CURLcode ftp_easy_statemach(struct connectdata *conn) +static CURLcode ftp_block_statemach(struct connectdata *conn) { struct ftp_conn *ftpc = &conn->proto.ftpc; struct pingpong *pp = &ftpc->pp; CURLcode result = CURLE_OK; while(ftpc->state != FTP_STOP) { - result = Curl_pp_easy_statemach(pp); + result = Curl_pp_statemach(pp, TRUE); if(result) break; } @@ -3150,62 +3136,12 @@ static CURLcode ftp_easy_statemach(struct connectdata *conn) } /* - * Allocate and initialize the struct FTP for the current SessionHandle. If - * need be. - */ - -#if defined(__INTEL_COMPILER) && (__INTEL_COMPILER == 910) && \ - defined(__OPTIMIZE__) && defined(__unix__) && defined(__i386__) - /* workaround icc 9.1 optimizer issue */ -#pragma optimize("", off) -#endif - -static CURLcode ftp_init(struct connectdata *conn) -{ - struct FTP *ftp; - - if(NULL == conn->data->state.proto.ftp) { - conn->data->state.proto.ftp = malloc(sizeof(struct FTP)); - if(NULL == conn->data->state.proto.ftp) - return CURLE_OUT_OF_MEMORY; - } - - ftp = conn->data->state.proto.ftp; - - /* get some initial data into the ftp struct */ - ftp->bytecountp = &conn->data->req.bytecount; - ftp->transfer = FTPTRANSFER_BODY; - ftp->downloadsize = 0; - - /* No need to duplicate user+password, the connectdata struct won't change - during a session, but we re-init them here since on subsequent inits - since the conn struct may have changed or been replaced. - */ - ftp->user = conn->user; - ftp->passwd = conn->passwd; - if(isBadFtpString(ftp->user)) - return CURLE_URL_MALFORMAT; - if(isBadFtpString(ftp->passwd)) - return CURLE_URL_MALFORMAT; - - conn->proto.ftpc.known_filesize = -1; /* unknown size for now */ - - return CURLE_OK; -} - -#if defined(__INTEL_COMPILER) && (__INTEL_COMPILER == 910) && \ - defined(__OPTIMIZE__) && defined(__unix__) && defined(__i386__) - /* workaround icc 9.1 optimizer issue */ -#pragma optimize("", on) -#endif - -/* * ftp_connect() should do everything that is to be considered a part of * the connection phase. * * The variable 'done' points to will be TRUE if the protocol-layer connect - * phase is done when this function returns, or FALSE is not. When called as - * a part of the easy interface, it will always be TRUE. + * phase is done when this function returns, or FALSE if not. + * */ static CURLcode ftp_connect(struct connectdata *conn, bool *done) /* see description above */ @@ -3216,14 +3152,6 @@ static CURLcode ftp_connect(struct connectdata *conn, *done = FALSE; /* default to not done yet */ - /* If there already is a protocol-specific struct allocated for this - sessionhandle, deal with it */ - Curl_reset_reqproto(conn); - - result = ftp_init(conn); - if(CURLE_OK != result) - return result; - /* We always support persistent connections on ftp */ conn->bits.close = FALSE; @@ -3263,7 +3191,7 @@ static CURLcode ftp_done(struct connectdata *conn, CURLcode status, bool premature) { struct SessionHandle *data = conn->data; - struct FTP *ftp = data->state.proto.ftp; + struct FTP *ftp = data->req.protop; struct ftp_conn *ftpc = &conn->proto.ftpc; struct pingpong *pp = &ftpc->pp; ssize_t nread; @@ -3369,15 +3297,16 @@ static CURLcode ftp_done(struct connectdata *conn, CURLcode status, #endif if(conn->sock[SECONDARYSOCKET] != CURL_SOCKET_BAD) { - if(!result && ftpc->dont_check && data->req.maxdownload > 0) + if(!result && ftpc->dont_check && data->req.maxdownload > 0) { /* partial download completed */ - result = Curl_pp_sendf(pp, "ABOR"); + result = Curl_pp_sendf(pp, "%s", "ABOR"); if(result) { failf(data, "Failure sending ABOR command: %s", curl_easy_strerror(result)); ftpc->ctl_valid = FALSE; /* mark control connection as bad */ conn->bits.close = TRUE; /* mark for connection closure */ } + } if(conn->ssl[SECONDARYSOCKET].use) { /* The secondary socket is using SSL so we must close down that part @@ -3523,7 +3452,7 @@ CURLcode ftp_sendquote(struct connectdata *conn, struct curl_slist *quote) acceptfail = TRUE; } - FTPSENDF(conn, "%s", cmd); + PPSENDF(&conn->proto.ftpc.pp, "%s", cmd); pp->response = Curl_tvnow(); /* timeout relative now */ @@ -3664,19 +3593,22 @@ static CURLcode ftp_range(struct connectdata *conn) * * This function shall be called when the second FTP (data) connection is * connected. + * + * 'complete' can return 0 for incomplete, 1 for done and -1 for go back + * (which basically is only for when PASV is being sent to retry a failed + * EPSV). */ -static CURLcode ftp_do_more(struct connectdata *conn, bool *complete) +static CURLcode ftp_do_more(struct connectdata *conn, int *completep) { struct SessionHandle *data=conn->data; struct ftp_conn *ftpc = &conn->proto.ftpc; CURLcode result = CURLE_OK; bool connected = FALSE; + bool complete = FALSE; /* the ftp struct is inited in ftp_connect() */ - struct FTP *ftp = data->state.proto.ftp; - - *complete = FALSE; + struct FTP *ftp = data->req.protop; /* if the second connection isn't done yet, wait for it */ if(!conn->bits.tcpconnect[SECONDARYSOCKET]) { @@ -3694,14 +3626,22 @@ static CURLcode ftp_do_more(struct connectdata *conn, bool *complete) if(connected) { DEBUGF(infof(data, "DO-MORE connected phase starts\n")); } - else + else { + if(result && (ftpc->count1 == 0)) { + *completep = -1; /* go back to DOING please */ + /* this is a EPSV connect failing, try PASV instead */ + return ftp_epsv_disable(conn); + } return result; + } } if(ftpc->state) { /* already in a state so skip the intial commands. They are only done to kickstart the do_more state */ - result = ftp_multi_statemach(conn, complete); + result = ftp_multi_statemach(conn, &complete); + + *completep = (int)complete; /* if we got an error or if we don't wait for a data connection return immediately */ @@ -3712,7 +3652,7 @@ static CURLcode ftp_do_more(struct connectdata *conn, bool *complete) /* if we reach the end of the FTP state machine here, *complete will be TRUE but so is ftpc->wait_data_conn, which says we need to wait for the data connection and therefore we're not actually complete */ - *complete = FALSE; + *completep = 0; } if(ftp->transfer <= FTPTRANSFER_INFO) { @@ -3735,6 +3675,9 @@ static CURLcode ftp_do_more(struct connectdata *conn, bool *complete) if(result) return result; + + *completep = 1; /* this state is now complete when the server has + connected back to us */ } } else if(data->set.upload) { @@ -3742,7 +3685,8 @@ static CURLcode ftp_do_more(struct connectdata *conn, bool *complete) if(result) return result; - result = ftp_multi_statemach(conn, complete); + result = ftp_multi_statemach(conn, &complete); + *completep = (int)complete; } else { /* download */ @@ -3770,7 +3714,8 @@ static CURLcode ftp_do_more(struct connectdata *conn, bool *complete) return result; } - result = ftp_multi_statemach(conn, complete); + result = ftp_multi_statemach(conn, &complete); + *completep = (int)complete; } return result; } @@ -3782,7 +3727,7 @@ static CURLcode ftp_do_more(struct connectdata *conn, bool *complete) if(!ftpc->wait_data_conn) { /* no waiting for the data connection so this is now complete */ - *complete = TRUE; + *completep = 1; DEBUGF(infof(data, "DO-MORE phase ends with %d\n", (int)result)); } @@ -3811,7 +3756,7 @@ CURLcode ftp_perform(struct connectdata *conn, if(conn->data->set.opt_no_body) { /* requested no body means no transfer... */ - struct FTP *ftp = conn->data->state.proto.ftp; + struct FTP *ftp = conn->data->req.protop; ftp->transfer = FTPTRANSFER_INFO; } @@ -3825,7 +3770,9 @@ CURLcode ftp_perform(struct connectdata *conn, /* run the state-machine */ result = ftp_multi_statemach(conn, dophase_done); - *connected = conn->bits.tcpconnect[FIRSTSOCKET]; + *connected = conn->bits.tcpconnect[SECONDARYSOCKET]; + + infof(conn->data, "ftp_perform ends with SECONDARY: %d\n", *connected); if(*dophase_done) DEBUGF(infof(conn->data, "DO phase is complete1\n")); @@ -3978,16 +3925,11 @@ static CURLcode wc_statemach(struct connectdata *conn) /* filelist has at least one file, lets get first one */ struct ftp_conn *ftpc = &conn->proto.ftpc; struct curl_fileinfo *finfo = wildcard->filelist->head->ptr; - char *tmp_path = malloc(strlen(conn->data->state.path) + - strlen(finfo->filename) + 1); - if(!tmp_path) { + + char *tmp_path = aprintf("%s%s", wildcard->path, finfo->filename); + if(!tmp_path) return CURLE_OUT_OF_MEMORY; - } - tmp_path[0] = 0; - /* make full path to matched file */ - strcat(tmp_path, wildcard->path); - strcat(tmp_path, finfo->filename); /* switch default "state.pathbuffer" and tmp_path, good to see ftp_parse_url_path function to understand this trick */ Curl_safefree(conn->data->state.pathbuffer); @@ -4076,17 +4018,6 @@ static CURLcode ftp_do(struct connectdata *conn, bool *done) *done = FALSE; /* default to false */ ftpc->wait_data_conn = FALSE; /* default to no such wait */ - /* - Since connections can be re-used between SessionHandles, this might be a - connection already existing but on a fresh SessionHandle struct so we must - make sure we have a good 'struct FTP' to play with. For new connections, - the struct FTP is allocated and setup in the ftp_connect() function. - */ - Curl_reset_reqproto(conn); - retcode = ftp_init(conn); - if(retcode) - return retcode; - if(conn->data->set.wildcardmatch) { retcode = wc_statemach(conn); if(conn->data->wildcard.state == CURLWC_SKIP || @@ -4118,19 +4049,19 @@ CURLcode Curl_ftpsendf(struct connectdata *conn, size_t write_len; char *sptr=s; CURLcode res = CURLE_OK; -#if defined(HAVE_KRB4) || defined(HAVE_GSSAPI) +#ifdef HAVE_GSSAPI enum protection_level data_sec = conn->data_prot; #endif va_list ap; va_start(ap, fmt); - vsnprintf(s, SBUF_SIZE-3, fmt, ap); + write_len = vsnprintf(s, SBUF_SIZE-3, fmt, ap); va_end(ap); - strcat(s, "\r\n"); /* append a trailing CRLF */ + strcpy(&s[write_len], "\r\n"); /* append a trailing CRLF */ + write_len +=2; bytes_written=0; - write_len = strlen(s); res = Curl_convert_to_network(conn->data, s, write_len); /* Curl_convert_to_network calls failf if unsuccessful */ @@ -4138,12 +4069,12 @@ CURLcode Curl_ftpsendf(struct connectdata *conn, return(res); for(;;) { -#if defined(HAVE_KRB4) || defined(HAVE_GSSAPI) +#ifdef HAVE_GSSAPI conn->data_prot = PROT_CMD; #endif res = Curl_write(conn, conn->sock[FIRSTSOCKET], sptr, write_len, &bytes_written); -#if defined(HAVE_KRB4) || defined(HAVE_GSSAPI) +#ifdef HAVE_GSSAPI DEBUGASSERT(data_sec > PROT_NONE && data_sec < PROT_LAST); conn->data_prot = data_sec; #endif @@ -4181,7 +4112,7 @@ static CURLcode ftp_quit(struct connectdata *conn) CURLcode result = CURLE_OK; if(conn->proto.ftpc.ctl_valid) { - result = Curl_pp_sendf(&conn->proto.ftpc.pp, "QUIT", NULL); + result = Curl_pp_sendf(&conn->proto.ftpc.pp, "%s", "QUIT"); if(result) { failf(conn->data, "Failure sending QUIT command: %s", curl_easy_strerror(result)); @@ -4193,7 +4124,7 @@ static CURLcode ftp_quit(struct connectdata *conn) state(conn, FTP_QUIT); - result = ftp_easy_statemach(conn); + result = ftp_block_statemach(conn); } return result; @@ -4245,7 +4176,7 @@ static CURLcode ftp_disconnect(struct connectdata *conn, bool dead_connection) Curl_pp_disconnect(pp); -#if defined(HAVE_KRB4) || defined(HAVE_GSSAPI) +#ifdef HAVE_GSSAPI Curl_sec_end(conn); #endif @@ -4264,7 +4195,7 @@ CURLcode ftp_parse_url_path(struct connectdata *conn) { struct SessionHandle *data = conn->data; /* the ftp struct is already inited in ftp_connect() */ - struct FTP *ftp = data->state.proto.ftp; + struct FTP *ftp = data->req.protop; struct ftp_conn *ftpc = &conn->proto.ftpc; const char *slash_pos; /* position of the first '/' char in curpos */ const char *path_to_use = data->state.path; @@ -4309,13 +4240,17 @@ CURLcode ftp_parse_url_path(struct connectdata *conn) } slash_pos=strrchr(cur_pos, '/'); if(slash_pos || !*cur_pos) { + size_t dirlen = slash_pos-cur_pos; + ftpc->dirs = calloc(1, sizeof(ftpc->dirs[0])); if(!ftpc->dirs) return CURLE_OUT_OF_MEMORY; + if(!dirlen) + dirlen++; + ftpc->dirs[0] = curl_easy_unescape(conn->data, slash_pos ? cur_pos : "/", - slash_pos ? - curlx_sztosi(slash_pos-cur_pos) : 1, + slash_pos ? curlx_uztosi(dirlen) : 1, NULL); if(!ftpc->dirs[0]) { freedirs(ftpc); @@ -4370,6 +4305,15 @@ CURLcode ftp_parse_url_path(struct connectdata *conn) } else { cur_pos = slash_pos + 1; /* jump to the rest of the string */ + if(!ftpc->dirdepth) { + /* path starts with a slash, add that as a directory */ + ftpc->dirs[ftpc->dirdepth] = strdup("/"); + if(!ftpc->dirs[ftpc->dirdepth++]) { /* run out of memory ... */ + failf(data, "no memory"); + freedirs(ftpc); + return CURLE_OUT_OF_MEMORY; + } + } continue; } @@ -4441,11 +4385,11 @@ CURLcode ftp_parse_url_path(struct connectdata *conn) static CURLcode ftp_dophase_done(struct connectdata *conn, bool connected) { - struct FTP *ftp = conn->data->state.proto.ftp; + struct FTP *ftp = conn->data->req.protop; struct ftp_conn *ftpc = &conn->proto.ftpc; if(connected) { - bool completed; + int completed; CURLcode result = ftp_do_more(conn, &completed); if(result) { @@ -4536,11 +4480,12 @@ CURLcode ftp_regular_transfer(struct connectdata *conn, return result; } -static CURLcode ftp_setup_connection(struct connectdata * conn) +static CURLcode ftp_setup_connection(struct connectdata *conn) { struct SessionHandle *data = conn->data; - char * type; + char *type; char command; + struct FTP *ftp; if(conn->bits.httpproxy && !data->set.tunnel_thru_httpproxy) { /* Unless we have asked to tunnel ftp operations through the proxy, we @@ -4556,18 +4501,18 @@ static CURLcode ftp_setup_connection(struct connectdata * conn) return CURLE_UNSUPPORTED_PROTOCOL; #endif } - /* - * We explicitly mark this connection as persistent here as we're doing - * FTP over HTTP and thus we accidentally avoid setting this value - * otherwise. - */ - conn->bits.close = FALSE; + /* set it up as a HTTP connection instead */ + return conn->handler->setup_connection(conn); #else failf(data, "FTP over http proxy requires HTTP support built-in!"); return CURLE_UNSUPPORTED_PROTOCOL; #endif } + conn->data->req.protop = ftp = malloc(sizeof(struct FTP)); + if(NULL == ftp) + return CURLE_OUT_OF_MEMORY; + data->state.path++; /* don't include the initial slash */ data->state.slash_removed = TRUE; /* we've skipped the slash */ @@ -4600,6 +4545,24 @@ static CURLcode ftp_setup_connection(struct connectdata * conn) } } + /* get some initial data into the ftp struct */ + ftp->bytecountp = &conn->data->req.bytecount; + ftp->transfer = FTPTRANSFER_BODY; + ftp->downloadsize = 0; + + /* No need to duplicate user+password, the connectdata struct won't change + during a session, but we re-init them here since on subsequent inits + since the conn struct may have changed or been replaced. + */ + ftp->user = conn->user; + ftp->passwd = conn->passwd; + if(isBadFtpString(ftp->user)) + return CURLE_URL_MALFORMAT; + if(isBadFtpString(ftp->passwd)) + return CURLE_URL_MALFORMAT; + + conn->proto.ftpc.known_filesize = -1; /* unknown size for now */ + return CURLE_OK; } diff --git a/plugins/FTPFileYM/curl/lib/ftp.h b/plugins/FTPFileYM/curl/lib/ftp.h index d359f28f35..bdd59c93da 100644 --- a/plugins/FTPFileYM/curl/lib/ftp.h +++ b/plugins/FTPFileYM/curl/lib/ftp.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2011, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2013, 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 @@ -97,13 +97,6 @@ typedef enum { file */ } curl_ftpfile; -typedef enum { - FTPTRANSFER_BODY, /* yes do transfer a body */ - FTPTRANSFER_INFO, /* do still go through to get info/headers */ - FTPTRANSFER_NONE, /* don't get anything and don't get info */ - FTPTRANSFER_LAST /* end of list marker, never used */ -} curl_ftptransfer; - /* This FTP struct is used in the SessionHandle. All FTP data that is connection-oriented must be in FTP_conn to properly deal with the fact that perhaps the SessionHandle is changed between the times the connection is @@ -115,7 +108,7 @@ struct FTP { /* transfer a file/body or not, done as a typedefed enum just to make debuggers display the full symbol and not just the numerical value */ - curl_ftptransfer transfer; + curl_pp_transfer transfer; curl_off_t downloadsize; }; diff --git a/plugins/FTPFileYM/curl/lib/getinfo.c b/plugins/FTPFileYM/curl/lib/getinfo.c index 74e5b0893d..3d09dc6846 100644 --- a/plugins/FTPFileYM/curl/lib/getinfo.c +++ b/plugins/FTPFileYM/curl/lib/getinfo.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2012, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2013, 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 @@ -53,8 +53,9 @@ CURLcode Curl_initinfo(struct SessionHandle *data) pro->t_redirect = 0; info->httpcode = 0; - info->httpversion=0; - info->filetime=-1; /* -1 is an illegal time and thus means unknown */ + info->httpversion = 0; + info->filetime = -1; /* -1 is an illegal time and thus means unknown */ + info->timecond = FALSE; if(info->contenttype) free(info->contenttype); @@ -185,7 +186,7 @@ static CURLcode getinfo_long(struct SessionHandle *data, CURLINFO info, break; case CURLINFO_CONDITION_UNMET: /* return if the condition prevented the document to get transferred */ - *param_longp = data->info.timecond; + *param_longp = data->info.timecond ? 1L : 0L; break; case CURLINFO_RTSP_CLIENT_CSEQ: *param_longp = data->state.rtsp_next_client_CSeq; diff --git a/plugins/FTPFileYM/curl/lib/gskit.c b/plugins/FTPFileYM/curl/lib/gskit.c new file mode 100644 index 0000000000..5cda85b9b8 --- /dev/null +++ b/plugins/FTPFileYM/curl/lib/gskit.c @@ -0,0 +1,906 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2013, 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 + * are also available at http://curl.haxx.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ***************************************************************************/ + +#include "curl_setup.h" + +#ifdef USE_GSKIT + +#include <gskssl.h> +#include <qsoasync.h> + +/* Some symbols are undefined/unsupported on OS400 versions < V7R1. */ +#ifndef GSK_SSL_EXTN_SERVERNAME_REQUEST +#define GSK_SSL_EXTN_SERVERNAME_REQUEST 230 +#endif + +#ifdef HAVE_LIMITS_H +# include <limits.h> +#endif + +#include <curl/curl.h> +#include "urldata.h" +#include "sendf.h" +#include "gskit.h" +#include "sslgen.h" +#include "connect.h" /* for the connect timeout */ +#include "select.h" +#include "strequal.h" +#include "x509asn1.h" + +#define _MPRINTF_REPLACE /* use our functions only */ +#include <curl/mprintf.h> + +#include "curl_memory.h" +/* The last #include file should be: */ +#include "memdebug.h" + + +/* Supported ciphers. */ +typedef struct { + const char * name; /* Cipher name. */ + const char * gsktoken; /* Corresponding token for GSKit String. */ + int sslver; /* SSL version. */ +} gskit_cipher; + +static const gskit_cipher ciphertable[] = { + { "null-md5", "01", CURL_SSLVERSION_SSLv3 }, + { "null-sha", "02", CURL_SSLVERSION_SSLv3 }, + { "exp-rc4-md5", "03", CURL_SSLVERSION_SSLv3 }, + { "rc4-md5", "04", CURL_SSLVERSION_SSLv3 }, + { "rc4-sha", "05", CURL_SSLVERSION_SSLv3 }, + { "exp-rc2-cbc-md5", "06", CURL_SSLVERSION_SSLv3 }, + { "exp-des-cbc-sha", "09", CURL_SSLVERSION_SSLv3 }, + { "des-cbc3-sha", "0A", CURL_SSLVERSION_SSLv3 }, + { "aes128-sha", "2F", CURL_SSLVERSION_TLSv1 }, + { "aes256-sha", "35", CURL_SSLVERSION_TLSv1 }, + { "rc4-md5", "1", CURL_SSLVERSION_SSLv2 }, + { "exp-rc4-md5", "2", CURL_SSLVERSION_SSLv2 }, + { "rc2-md5", "3", CURL_SSLVERSION_SSLv2 }, + { "exp-rc2-md5", "4", CURL_SSLVERSION_SSLv2 }, + { "des-cbc-md5", "6", CURL_SSLVERSION_SSLv2 }, + { "des-cbc3-md5", "7", CURL_SSLVERSION_SSLv2 }, + { (const char *) NULL, (const char *) NULL, 0 } +}; + + +static bool is_separator(char c) +{ + /* Return whether character is a cipher list separator. */ + switch (c) { + case ' ': + case '\t': + case ':': + case ',': + case ';': + return true; + } + return false; +} + + +static CURLcode gskit_status(struct SessionHandle * data, int rc, + const char * procname, CURLcode defcode) +{ + CURLcode cc; + + /* Process GSKit status and map it to a CURLcode. */ + switch (rc) { + case GSK_OK: + case GSK_OS400_ASYNCHRONOUS_SOC_INIT: + return CURLE_OK; + case GSK_KEYRING_OPEN_ERROR: + case GSK_OS400_ERROR_NO_ACCESS: + return CURLE_SSL_CACERT_BADFILE; + case GSK_INSUFFICIENT_STORAGE: + return CURLE_OUT_OF_MEMORY; + case GSK_ERROR_BAD_V2_CIPHER: + case GSK_ERROR_BAD_V3_CIPHER: + case GSK_ERROR_NO_CIPHERS: + return CURLE_SSL_CIPHER; + case GSK_OS400_ERROR_NOT_TRUSTED_ROOT: + case GSK_ERROR_CERT_VALIDATION: + return CURLE_PEER_FAILED_VERIFICATION; + case GSK_OS400_ERROR_TIMED_OUT: + return CURLE_OPERATION_TIMEDOUT; + case GSK_WOULD_BLOCK: + return CURLE_AGAIN; + case GSK_OS400_ERROR_NOT_REGISTERED: + break; + case GSK_ERROR_IO: + switch (errno) { + case ENOMEM: + return CURLE_OUT_OF_MEMORY; + default: + failf(data, "%s I/O error: %s", procname, strerror(errno)); + break; + } + break; + default: + failf(data, "%s: %s", procname, gsk_strerror(rc)); + break; + } + return defcode; +} + + +static CURLcode set_enum(struct SessionHandle * data, + gsk_handle h, GSK_ENUM_ID id, GSK_ENUM_VALUE value) +{ + int rc = gsk_attribute_set_enum(h, id, value); + + switch (rc) { + case GSK_OK: + return CURLE_OK; + case GSK_ERROR_IO: + failf(data, "gsk_attribute_set_enum() I/O error: %s", strerror(errno)); + break; + default: + failf(data, "gsk_attribute_set_enum(): %s", gsk_strerror(rc)); + break; + } + return CURLE_SSL_CONNECT_ERROR; +} + + +static CURLcode set_buffer(struct SessionHandle * data, + gsk_handle h, GSK_BUF_ID id, const char * buffer) +{ + int rc = gsk_attribute_set_buffer(h, id, buffer, 0); + + switch (rc) { + case GSK_OK: + return CURLE_OK; + case GSK_ERROR_IO: + failf(data, "gsk_attribute_set_buffer() I/O error: %s", strerror(errno)); + break; + default: + failf(data, "gsk_attribute_set_buffer(): %s", gsk_strerror(rc)); + break; + } + return CURLE_SSL_CONNECT_ERROR; +} + + +static CURLcode set_numeric(struct SessionHandle * data, + gsk_handle h, GSK_NUM_ID id, int value) +{ + int rc = gsk_attribute_set_numeric_value(h, id, value); + + switch (rc) { + case GSK_OK: + return CURLE_OK; + case GSK_ERROR_IO: + failf(data, "gsk_attribute_set_numeric_value() I/O error: %s", + strerror(errno)); + break; + default: + failf(data, "gsk_attribute_set_numeric_value(): %s", gsk_strerror(rc)); + break; + } + return CURLE_SSL_CONNECT_ERROR; +} + + +static CURLcode set_callback(struct SessionHandle * data, + gsk_handle h, GSK_CALLBACK_ID id, void * info) +{ + int rc = gsk_attribute_set_callback(h, id, info); + + switch (rc) { + case GSK_OK: + return CURLE_OK; + case GSK_ERROR_IO: + failf(data, "gsk_attribute_set_callback() I/O error: %s", strerror(errno)); + break; + default: + failf(data, "gsk_attribute_set_callback(): %s", gsk_strerror(rc)); + break; + } + return CURLE_SSL_CONNECT_ERROR; +} + + +static CURLcode set_ciphers(struct SessionHandle * data, gsk_handle h) +{ + const char * cipherlist = data->set.str[STRING_SSL_CIPHER_LIST]; + char * sslv2ciphers; + char * sslv3ciphers; + const char * clp; + const gskit_cipher * ctp; + char * v2p; + char * v3p; + int i; + CURLcode cc; + + /* Compile cipher list into GSKit-compatible cipher lists. */ + + if(!cipherlist) + return CURLE_OK; + while(is_separator(*cipherlist)) /* Skip initial separators. */ + cipherlist++; + if(!*cipherlist) + return CURLE_OK; + + /* We allocate GSKit buffers of the same size as the input string: since + GSKit tokens are always shorter than their cipher names, allocated buffers + will always be large enough to accomodate the result. */ + i = strlen(cipherlist) + 1; + v2p = malloc(i); + if(!v2p) + return CURLE_OUT_OF_MEMORY; + v3p = malloc(i); + if(!v3p) { + free(v2p); + return CURLE_OUT_OF_MEMORY; + } + sslv2ciphers = v2p; + sslv3ciphers = v3p; + + /* Process each cipher in input string. */ + for(;;) { + for(clp = cipherlist; *cipherlist && !is_separator(*cipherlist);) + cipherlist++; + i = cipherlist - clp; + if(!i) + break; + /* Search the cipher in our table. */ + for(ctp = ciphertable; ctp->name; ctp++) + if(strnequal(ctp->name, clp, i) && !ctp->name[i]) + break; + if(!ctp->name) + failf(data, "Unknown cipher %.*s: ignored", i, clp); + else { + switch (ctp->sslver) { + case CURL_SSLVERSION_SSLv2: + strcpy(v2p, ctp->gsktoken); + v2p += strlen(v2p); + break; + default: + /* GSKit wants TLSv1 ciphers with SSLv3 ciphers. */ + strcpy(v3p, ctp->gsktoken); + v3p += strlen(v3p); + break; + } + } + + /* Advance to next cipher name or end of string. */ + while(is_separator(*cipherlist)) + cipherlist++; + } + *v2p = '\0'; + *v3p = '\0'; + cc = set_buffer(data, h, GSK_V2_CIPHER_SPECS, sslv2ciphers); + if(cc == CURLE_OK) + cc = set_buffer(data, h, GSK_V3_CIPHER_SPECS, sslv3ciphers); + free(sslv2ciphers); + free(sslv3ciphers); + return cc; +} + + +int Curl_gskit_init(void) +{ + /* No initialisation needed. */ + + return 1; +} + + +void Curl_gskit_cleanup(void) +{ + /* Nothing to do. */ +} + + +static CURLcode init_environment(struct SessionHandle * data, + gsk_handle * envir, const char * appid, + const char * file, const char * label, + const char * password) +{ + int rc; + CURLcode c; + gsk_handle h; + + /* Creates the GSKit environment. */ + + rc = gsk_environment_open(&h); + switch (rc) { + case GSK_OK: + break; + case GSK_INSUFFICIENT_STORAGE: + return CURLE_OUT_OF_MEMORY; + default: + failf(data, "gsk_environment_open(): %s", gsk_strerror(rc)); + return CURLE_SSL_CONNECT_ERROR; + } + + c = set_enum(data, h, GSK_SESSION_TYPE, GSK_CLIENT_SESSION); + if(c == CURLE_OK && appid) + c = set_buffer(data, h, GSK_OS400_APPLICATION_ID, appid); + if(c == CURLE_OK && file) + c = set_buffer(data, h, GSK_KEYRING_FILE, file); + if(c == CURLE_OK && label) + c = set_buffer(data, h, GSK_KEYRING_LABEL, label); + if(c == CURLE_OK && password) + c = set_buffer(data, h, GSK_KEYRING_PW, password); + + if(c == CURLE_OK) { + /* Locate CAs, Client certificate and key according to our settings. + Note: this call may be blocking for some tenths of seconds. */ + c = gskit_status(data, gsk_environment_init(h), + "gsk_environment_init()", CURLE_SSL_CERTPROBLEM); + if(c == CURLE_OK) { + *envir = h; + return c; + } + } + /* Error: rollback. */ + gsk_environment_close(&h); + return c; +} + + +static void cancel_async_handshake(struct connectdata * conn, int sockindex) +{ + struct ssl_connect_data * connssl = &conn->ssl[sockindex]; + Qso_OverlappedIO_t cstat; + + if(QsoCancelOperation(conn->sock[sockindex], 0) > 0) + QsoWaitForIOCompletion(connssl->iocport, &cstat, (struct timeval *) NULL); +} + + +static void close_async_handshake(struct ssl_connect_data * connssl) +{ + QsoDestroyIOCompletionPort(connssl->iocport); + connssl->iocport = -1; +} + + +static void close_one(struct ssl_connect_data * conn, + struct SessionHandle * data) +{ + if(conn->handle) { + gskit_status(data, gsk_secure_soc_close(&conn->handle), + "gsk_secure_soc_close()", 0); + conn->handle = (gsk_handle) NULL; + } + if(conn->iocport >= 0) + close_async_handshake(conn); +} + + +static ssize_t gskit_send(struct connectdata * conn, int sockindex, + const void * mem, size_t len, CURLcode * curlcode) +{ + struct SessionHandle * data = conn->data; + CURLcode cc; + int written; + + cc = gskit_status(data, + gsk_secure_soc_write(conn->ssl[sockindex].handle, + (char *) mem, (int) len, &written), + "gsk_secure_soc_write()", CURLE_SEND_ERROR); + if(cc != CURLE_OK) { + *curlcode = cc; + written = -1; + } + return (ssize_t) written; /* number of bytes */ +} + + +static ssize_t gskit_recv(struct connectdata * conn, int num, char * buf, + size_t buffersize, CURLcode * curlcode) +{ + struct SessionHandle * data = conn->data; + int buffsize; + int nread; + CURLcode cc; + + buffsize = buffersize > (size_t) INT_MAX? INT_MAX: (int) buffersize; + cc = gskit_status(data, gsk_secure_soc_read(conn->ssl[num].handle, + buf, buffsize, &nread), + "gsk_secure_soc_read()", CURLE_RECV_ERROR); + if(cc != CURLE_OK) { + *curlcode = cc; + nread = -1; + } + return (ssize_t) nread; +} + + +static CURLcode gskit_connect_step1(struct connectdata * conn, int sockindex) +{ + struct SessionHandle * data = conn->data; + struct ssl_connect_data * connssl = &conn->ssl[sockindex]; + gsk_handle envir; + CURLcode cc; + int rc; + char * keyringfile; + char * keyringpwd; + char * keyringlabel; + char * v2ciphers; + char * v3ciphers; + char * sni; + bool sslv2enable, sslv3enable, tlsv1enable; + long timeout; + Qso_OverlappedIO_t commarea; + + /* Create SSL environment, start (preferably asynchronous) handshake. */ + + connssl->handle = (gsk_handle) NULL; + connssl->iocport = -1; + + /* GSKit supports two ways of specifying an SSL context: either by + * application identifier (that should have been defined at the system + * level) or by keyring file, password and certificate label. + * Local certificate name (CURLOPT_SSLCERT) is used to hold either the + * application identifier of the certificate label. + * Key password (CURLOPT_KEYPASSWD) holds the keyring password. + * It is not possible to have different keyrings for the CAs and the + * local certificate. We thus use the CA file (CURLOPT_CAINFO) to identify + * the keyring file. + * If no key password is given and the keyring is the system keyring, + * application identifier mode is tried first, as recommended in IBM doc. + */ + + keyringfile = data->set.str[STRING_SSL_CAFILE]; + keyringpwd = data->set.str[STRING_KEY_PASSWD]; + keyringlabel = data->set.str[STRING_CERT]; + envir = (gsk_handle) NULL; + + if(keyringlabel && *keyringlabel && !keyringpwd && + !strcmp(keyringfile, CURL_CA_BUNDLE)) { + /* Try application identifier mode. */ + init_environment(data, &envir, keyringlabel, (const char *) NULL, + (const char *) NULL, (const char *) NULL); + } + + if(!envir) { + /* Use keyring mode. */ + cc = init_environment(data, &envir, (const char *) NULL, + keyringfile, keyringlabel, keyringpwd); + if(cc != CURLE_OK) + return cc; + } + + /* Create secure session. */ + cc = gskit_status(data, gsk_secure_soc_open(envir, &connssl->handle), + "gsk_secure_soc_open()", CURLE_SSL_CONNECT_ERROR); + gsk_environment_close(&envir); + if(cc != CURLE_OK) + return cc; + + /* Determine which SSL/TLS version should be enabled. */ + sslv2enable = sslv3enable = tlsv1enable = false; + sni = conn->host.name; + switch (data->set.ssl.version) { + case CURL_SSLVERSION_SSLv2: + sslv2enable = true; + sni = (char *) NULL; + break; + case CURL_SSLVERSION_SSLv3: + sslv3enable = true; + sni = (char *) NULL; + break; + case CURL_SSLVERSION_TLSv1: + tlsv1enable = true; + break; + default: /* CURL_SSLVERSION_DEFAULT. */ + sslv3enable = true; + tlsv1enable = true; + break; + } + + /* Process SNI. Ignore if not supported (on OS400 < V7R1). */ + if(sni) { + rc = gsk_attribute_set_buffer(connssl->handle, + GSK_SSL_EXTN_SERVERNAME_REQUEST, sni, 0); + switch (rc) { + case GSK_OK: + case GSK_ATTRIBUTE_INVALID_ID: + break; + case GSK_ERROR_IO: + failf(data, "gsk_attribute_set_buffer() I/O error: %s", strerror(errno)); + cc = CURLE_SSL_CONNECT_ERROR; + break; + default: + failf(data, "gsk_attribute_set_buffer(): %s", gsk_strerror(rc)); + cc = CURLE_SSL_CONNECT_ERROR; + break; + } + } + + /* Set session parameters. */ + if(cc == CURLE_OK) { + /* Compute the handshake timeout. Since GSKit granularity is 1 second, + we round up the required value. */ + timeout = Curl_timeleft(data, NULL, TRUE); + if(timeout < 0) + cc = CURLE_OPERATION_TIMEDOUT; + else + cc = set_numeric(data, connssl->handle, GSK_HANDSHAKE_TIMEOUT, + (timeout + 999) / 1000); + } + if(cc == CURLE_OK) + cc = set_numeric(data, connssl->handle, GSK_FD, conn->sock[sockindex]); + if(cc == CURLE_OK) + cc = set_ciphers(data, connssl->handle); + if(cc == CURLE_OK) + cc = set_enum(data, connssl->handle, GSK_PROTOCOL_SSLV2, + sslv2enable? GSK_PROTOCOL_SSLV2_ON: + GSK_PROTOCOL_SSLV2_OFF); + if(cc == CURLE_OK) + cc = set_enum(data, connssl->handle, GSK_PROTOCOL_SSLV3, + sslv3enable? GSK_PROTOCOL_SSLV3_ON: + GSK_PROTOCOL_SSLV3_OFF); + if(cc == CURLE_OK) + cc = set_enum(data, connssl->handle, GSK_PROTOCOL_TLSV1, + sslv3enable? GSK_PROTOCOL_TLSV1_ON: + GSK_PROTOCOL_TLSV1_OFF); + if(cc == CURLE_OK) + cc = set_enum(data, connssl->handle, GSK_SERVER_AUTH_TYPE, + data->set.ssl.verifypeer? GSK_SERVER_AUTH_FULL: + GSK_SERVER_AUTH_PASSTHRU); + + if(cc == CURLE_OK) { + /* Start handshake. Try asynchronous first. */ + memset(&commarea, 0, sizeof commarea); + connssl->iocport = QsoCreateIOCompletionPort(); + if(connssl->iocport != -1) { + cc = gskit_status(data, gsk_secure_soc_startInit(connssl->handle, + connssl->iocport, &commarea), + "gsk_secure_soc_startInit()", CURLE_SSL_CONNECT_ERROR); + if(cc == CURLE_OK) { + connssl->connecting_state = ssl_connect_2; + return CURLE_OK; + } + else + close_async_handshake(connssl); + } + else if(errno != ENOBUFS) + cc = gskit_status(data, GSK_ERROR_IO, "QsoCreateIOCompletionPort()", 0); + else { + /* No more completion port available. Use synchronous IO. */ + cc = gskit_status(data, gsk_secure_soc_init(connssl->handle), + "gsk_secure_soc_init()", CURLE_SSL_CONNECT_ERROR); + if(cc == CURLE_OK) { + connssl->connecting_state = ssl_connect_3; + return CURLE_OK; + } + } + } + + /* Error: rollback. */ + close_one(connssl, data); + return cc; +} + + +static CURLcode gskit_connect_step2(struct connectdata * conn, int sockindex, + bool nonblocking) +{ + struct SessionHandle * data = conn->data; + struct ssl_connect_data * connssl = &conn->ssl[sockindex]; + Qso_OverlappedIO_t cstat; + long timeout_ms; + struct timeval stmv; + CURLcode cc; + + /* Poll or wait for end of SSL asynchronous handshake. */ + + for(;;) { + timeout_ms = nonblocking? 0: Curl_timeleft(data, NULL, TRUE); + if(timeout_ms < 0) + timeout_ms = 0; + stmv.tv_sec = timeout_ms / 1000; + stmv.tv_usec = (timeout_ms - stmv.tv_sec * 1000) * 1000; + switch (QsoWaitForIOCompletion(connssl->iocport, &cstat, &stmv)) { + case 1: /* Operation complete. */ + break; + case -1: /* An error occurred: handshake still in progress. */ + if(errno == EINTR) { + if(nonblocking) + return CURLE_OK; + continue; /* Retry. */ + } + if(errno != ETIME) { + failf(data, "QsoWaitForIOCompletion() I/O error: %s", strerror(errno)); + cancel_async_handshake(conn, sockindex); + close_async_handshake(connssl); + return CURLE_SSL_CONNECT_ERROR; + } + /* FALL INTO... */ + case 0: /* Handshake in progress, timeout occurred. */ + if(nonblocking) + return CURLE_OK; + cancel_async_handshake(conn, sockindex); + close_async_handshake(connssl); + return CURLE_OPERATION_TIMEDOUT; + } + break; + } + cc = gskit_status(data, cstat.returnValue, "SSL handshake", + CURLE_SSL_CONNECT_ERROR); + if(cc == CURLE_OK) + connssl->connecting_state = ssl_connect_3; + close_async_handshake(connssl); + return cc; +} + + +static CURLcode gskit_connect_step3(struct connectdata * conn, int sockindex) +{ + struct SessionHandle * data = conn->data; + struct ssl_connect_data * connssl = &conn->ssl[sockindex]; + const gsk_cert_data_elem * cdev; + int cdec; + const gsk_cert_data_elem * p; + const char * cert = (const char *) NULL; + const char * certend; + int i; + CURLcode cc; + + /* SSL handshake done: gather certificate info and verify host. */ + + if(gskit_status(data, gsk_attribute_get_cert_info(connssl->handle, + GSK_PARTNER_CERT_INFO, + &cdev, &cdec), + "gsk_attribute_get_cert_info()", CURLE_SSL_CONNECT_ERROR) == + CURLE_OK) { + infof(data, "Server certificate:\n"); + p = cdev; + for(i = 0; i++ < cdec; p++) + switch (p->cert_data_id) { + case CERT_BODY_DER: + cert = p->cert_data_p; + certend = cert + cdev->cert_data_l; + break; + case CERT_DN_PRINTABLE: + infof(data, "\t subject: %.*s\n", p->cert_data_l, p->cert_data_p); + break; + case CERT_ISSUER_DN_PRINTABLE: + infof(data, "\t issuer: %.*s\n", p->cert_data_l, p->cert_data_p); + break; + case CERT_VALID_FROM: + infof(data, "\t start date: %.*s\n", p->cert_data_l, p->cert_data_p); + break; + case CERT_VALID_TO: + infof(data, "\t expire date: %.*s\n", p->cert_data_l, p->cert_data_p); + break; + } + } + + /* Verify host. */ + cc = Curl_verifyhost(conn, cert, certend); + if(cc != CURLE_OK) + return cc; + + /* The only place GSKit can get the whole CA chain is a validation + callback where no user data pointer is available. Therefore it's not + possible to copy this chain into our structures for CAINFO. + However the server certificate may be available, thus we can return + info about it. */ + if(data->set.ssl.certinfo) { + if(Curl_ssl_init_certinfo(data, 1)) + return CURLE_OUT_OF_MEMORY; + if(cert) { + cc = Curl_extract_certinfo(conn, 0, cert, certend); + if(cc != CURLE_OK) + return cc; + } + } + + connssl->connecting_state = ssl_connect_done; + return CURLE_OK; +} + + +static CURLcode gskit_connect_common(struct connectdata * conn, int sockindex, + bool nonblocking, bool * done) +{ + struct SessionHandle * data = conn->data; + struct ssl_connect_data * connssl = &conn->ssl[sockindex]; + long timeout_ms; + Qso_OverlappedIO_t cstat; + CURLcode cc = CURLE_OK; + + *done = connssl->state == ssl_connection_complete; + if(*done) + return CURLE_OK; + + /* Step 1: create session, start handshake. */ + if(connssl->connecting_state == ssl_connect_1) { + /* check allowed time left */ + timeout_ms = Curl_timeleft(data, NULL, TRUE); + + if(timeout_ms < 0) { + /* no need to continue if time already is up */ + failf(data, "SSL connection timeout"); + cc = CURLE_OPERATION_TIMEDOUT; + } + else + cc = gskit_connect_step1(conn, sockindex); + } + + /* Step 2: check if handshake is over. */ + if(cc == CURLE_OK && connssl->connecting_state == ssl_connect_2) { + /* check allowed time left */ + timeout_ms = Curl_timeleft(data, NULL, TRUE); + + if(timeout_ms < 0) { + /* no need to continue if time already is up */ + failf(data, "SSL connection timeout"); + cc = CURLE_OPERATION_TIMEDOUT; + } + else + cc = gskit_connect_step2(conn, sockindex, nonblocking); + } + + /* Step 3: gather certificate info, verify host. */ + if(cc == CURLE_OK && connssl->connecting_state == ssl_connect_3) + cc = gskit_connect_step3(conn, sockindex); + + if(cc != CURLE_OK) + close_one(connssl, data); + else if(connssl->connecting_state == ssl_connect_done) { + connssl->state = ssl_connection_complete; + connssl->connecting_state = ssl_connect_1; + conn->recv[sockindex] = gskit_recv; + conn->send[sockindex] = gskit_send; + *done = TRUE; + } + + return cc; +} + + +CURLcode Curl_gskit_connect_nonblocking(struct connectdata * conn, + int sockindex, + bool * done) +{ + CURLcode cc; + + cc = gskit_connect_common(conn, sockindex, TRUE, done); + if(*done || cc != CURLE_OK) + conn->ssl[sockindex].connecting_state = ssl_connect_1; + return cc; +} + + +CURLcode Curl_gskit_connect(struct connectdata * conn, int sockindex) +{ + CURLcode retcode; + bool done; + + conn->ssl[sockindex].connecting_state = ssl_connect_1; + retcode = gskit_connect_common(conn, sockindex, FALSE, &done); + if(retcode) + return retcode; + + DEBUGASSERT(done); + + return CURLE_OK; +} + + +void Curl_gskit_close(struct connectdata * conn, int sockindex) +{ + struct SessionHandle * data = conn->data; + struct ssl_connect_data * connssl = &conn->ssl[sockindex]; + + if(connssl->use) + close_one(connssl, data); +} + + +int Curl_gskit_close_all(struct SessionHandle * data) +{ + /* Unimplemented. */ + (void) data; + return 0; +} + + +int Curl_gskit_shutdown(struct connectdata * conn, int sockindex) +{ + struct ssl_connect_data * connssl = &conn->ssl[sockindex]; + struct SessionHandle * data = conn->data; + ssize_t nread; + int what; + int rc; + char buf[120]; + + if(!connssl->handle) + return 0; + + if(data->set.ftp_ccc != CURLFTPSSL_CCC_ACTIVE) + return 0; + + close_one(connssl, data); + rc = 0; + what = Curl_socket_ready(conn->sock[sockindex], + CURL_SOCKET_BAD, SSL_SHUTDOWN_TIMEOUT); + + for(;;) { + if(what < 0) { + /* anything that gets here is fatally bad */ + failf(data, "select/poll on SSL socket, errno: %d", SOCKERRNO); + rc = -1; + break; + } + + if(!what) { /* timeout */ + failf(data, "SSL shutdown timeout"); + break; + } + + /* Something to read, let's do it and hope that it is the close + notify alert from the server. No way to gsk_secure_soc_read() now, so + use read(). */ + + nread = read(conn->sock[sockindex], buf, sizeof(buf)); + + if(nread < 0) { + failf(data, "read: %s", strerror(errno)); + rc = -1; + } + + if(nread <= 0) + break; + + what = Curl_socket_ready(conn->sock[sockindex], CURL_SOCKET_BAD, 0); + } + + return rc; +} + + +size_t Curl_gskit_version(char * buffer, size_t size) +{ + strncpy(buffer, "GSKit", size); + return strlen(buffer); +} + + +int Curl_gskit_check_cxn(struct connectdata * cxn) +{ + int err; + int errlen; + + /* The only thing that can be tested here is at the socket level. */ + + if(!cxn->ssl[FIRSTSOCKET].handle) + return 0; /* connection has been closed */ + + err = 0; + errlen = sizeof err; + + if(getsockopt(cxn->sock[FIRSTSOCKET], SOL_SOCKET, SO_ERROR, + (unsigned char *) &err, &errlen) || + errlen != sizeof err || err) + return 0; /* connection has been closed */ + + return -1; /* connection status unknown */ +} + +#endif /* USE_GSKIT */ diff --git a/plugins/FTPFileYM/curl/lib/gskit.h b/plugins/FTPFileYM/curl/lib/gskit.h new file mode 100644 index 0000000000..0d59aa7096 --- /dev/null +++ b/plugins/FTPFileYM/curl/lib/gskit.h @@ -0,0 +1,64 @@ +#ifndef HEADER_CURL_GSKIT_H +#define HEADER_CURL_GSKIT_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2013, 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 + * are also available at http://curl.haxx.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ***************************************************************************/ +#include "curl_setup.h" + +/* + * This header should only be needed to get included by sslgen.c and gskit.c + */ + +#include "urldata.h" + +#ifdef USE_GSKIT +int Curl_gskit_init(void); +void Curl_gskit_cleanup(void); +CURLcode Curl_gskit_connect(struct connectdata * conn, int sockindex); +CURLcode Curl_gskit_connect_nonblocking(struct connectdata * conn, + int sockindex, bool * done); +void Curl_gskit_close(struct connectdata *conn, int sockindex); +int Curl_gskit_close_all(struct SessionHandle * data); +int Curl_gskit_shutdown(struct connectdata * conn, int sockindex); + +size_t Curl_gskit_version(char * buffer, size_t size); +int Curl_gskit_check_cxn(struct connectdata * cxn); + +/* API setup for GSKit */ +#define curlssl_init Curl_gskit_init +#define curlssl_cleanup Curl_gskit_cleanup +#define curlssl_connect Curl_gskit_connect +#define curlssl_connect_nonblocking Curl_gskit_connect_nonblocking + +/* No session handling for GSKit */ +#define curlssl_session_free(x) Curl_nop_stmt +#define curlssl_close_all Curl_gskit_close_all +#define curlssl_close Curl_gskit_close +#define curlssl_shutdown(x,y) Curl_gskit_shutdown(x,y) +#define curlssl_set_engine(x,y) CURLE_NOT_BUILT_IN +#define curlssl_set_engine_default(x) CURLE_NOT_BUILT_IN +#define curlssl_engines_list(x) NULL +#define curlssl_version Curl_gskit_version +#define curlssl_check_cxn(x) Curl_gskit_check_cxn(x) +#define curlssl_data_pending(x,y) 0 +#endif /* USE_GSKIT */ + +#endif /* HEADER_CURL_GSKIT_H */ diff --git a/plugins/FTPFileYM/curl/lib/gtls.h b/plugins/FTPFileYM/curl/lib/gtls.h index 84e1396b56..453542e1ec 100644 --- a/plugins/FTPFileYM/curl/lib/gtls.h +++ b/plugins/FTPFileYM/curl/lib/gtls.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2012, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2013, 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 @@ -55,6 +55,10 @@ void Curl_gtls_md5sum(unsigned char *tmp, /* input */ unsigned char *md5sum, /* output */ size_t md5len); +/* this backend provides these functions: */ +#define have_curlssl_random 1 +#define have_curlssl_md5sum 1 + /* API setup for GnuTLS */ #define curlssl_init Curl_gtls_init #define curlssl_cleanup Curl_gtls_cleanup diff --git a/plugins/FTPFileYM/curl/lib/hash.c b/plugins/FTPFileYM/curl/lib/hash.c index 0a8e99ab20..4a12e1a7b9 100644 --- a/plugins/FTPFileYM/curl/lib/hash.c +++ b/plugins/FTPFileYM/curl/lib/hash.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2012, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2013, 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 @@ -391,7 +391,7 @@ void Curl_hash_print(struct curl_hash *h, if(func) func(he->ptr); else - fprintf(stderr, " [%p]", he->ptr); + fprintf(stderr, " [%p]", (void *)he->ptr); he = Curl_hash_next_element(&iter); } diff --git a/plugins/FTPFileYM/curl/lib/hash.h b/plugins/FTPFileYM/curl/lib/hash.h index 99a627405b..aa935d4eb9 100644 --- a/plugins/FTPFileYM/curl/lib/hash.h +++ b/plugins/FTPFileYM/curl/lib/hash.h @@ -104,4 +104,3 @@ void Curl_hash_print(struct curl_hash *h, #endif /* HEADER_CURL_HASH_H */ - diff --git a/plugins/FTPFileYM/curl/lib/hostcheck.c b/plugins/FTPFileYM/curl/lib/hostcheck.c index 44ad822f07..abd1fa0c3c 100644 --- a/plugins/FTPFileYM/curl/lib/hostcheck.c +++ b/plugins/FTPFileYM/curl/lib/hostcheck.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2012, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2013, 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 @@ -22,8 +22,9 @@ #include "curl_setup.h" -#if defined(USE_SSLEAY) || defined(USE_AXTLS) -/* these two backends use functions from this file */ +#if defined(USE_SSLEAY) || defined(USE_AXTLS) || defined(USE_QSOSSL) || \ + defined(USE_GSKIT) +/* these backends use functions from this file */ #include "hostcheck.h" #include "rawstr.h" @@ -93,4 +94,4 @@ int Curl_cert_hostcheck(const char *match_pattern, const char *hostname) return 0; } -#endif /* SSLEAY or AXTLS */ +#endif /* SSLEAY or AXTLS or QSOSSL or GSKIT */ diff --git a/plugins/FTPFileYM/curl/lib/hostip.c b/plugins/FTPFileYM/curl/lib/hostip.c index 3765ca1cde..f37b4925bb 100644 --- a/plugins/FTPFileYM/curl/lib/hostip.c +++ b/plugins/FTPFileYM/curl/lib/hostip.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2012, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2013, 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 @@ -140,6 +140,10 @@ struct curl_hash *Curl_global_host_cache_init(void) void Curl_global_host_cache_dtor(void) { if(host_cache_initialized) { + /* first make sure that any custom "CURLOPT_RESOLVE" names are + cleared off */ + Curl_hostcache_clean(NULL, &hostname_cache); + /* then free the remaining hash completely */ Curl_hash_clean(&hostname_cache); host_cache_initialized = 0; } @@ -681,12 +685,14 @@ clean_up: * Curl_resolv_unlock() unlocks the given cached DNS entry. When this has been * made, the struct may be destroyed due to pruning. It is important that only * one unlock is made for each Curl_resolv() call. + * + * May be called with 'data' == NULL for global cache. */ void Curl_resolv_unlock(struct SessionHandle *data, struct Curl_dns_entry *dns) { DEBUGASSERT(dns && (dns->inuse>0)); - if(data->share) + if(data && data->share) Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE); dns->inuse--; @@ -697,7 +703,7 @@ void Curl_resolv_unlock(struct SessionHandle *data, struct Curl_dns_entry *dns) free(dns); } - if(data->share) + if(data && data->share) Curl_share_unlock(data, CURL_LOCK_DATA_DNS); } @@ -734,22 +740,23 @@ static int hostcache_inuse(void *data, void *hc) return 1; /* free all entries */ } -void Curl_hostcache_clean(struct SessionHandle *data) +/* + * Curl_hostcache_clean() + * + * This _can_ be called with 'data' == NULL but then of course no locking + * can be done! + */ + +void Curl_hostcache_clean(struct SessionHandle *data, + struct curl_hash *hash) { /* Entries added to the hostcache with the CURLOPT_RESOLVE function are * still present in the cache with the inuse counter set to 1. Detect them * and cleanup! */ - Curl_hash_clean_with_criterium(data->dns.hostcache, data, hostcache_inuse); + Curl_hash_clean_with_criterium(hash, data, hostcache_inuse); } -void Curl_hostcache_destroy(struct SessionHandle *data) -{ - Curl_hostcache_clean(data); - Curl_hash_destroy(data->dns.hostcache); - data->dns.hostcachetype = HCACHE_NONE; - data->dns.hostcache = NULL; -} CURLcode Curl_loadhostpairs(struct SessionHandle *data) { diff --git a/plugins/FTPFileYM/curl/lib/hostip.h b/plugins/FTPFileYM/curl/lib/hostip.h index 9dd47baa66..42ed7d3208 100644 --- a/plugins/FTPFileYM/curl/lib/hostip.h +++ b/plugins/FTPFileYM/curl/lib/hostip.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2012, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2013, 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 @@ -201,9 +201,30 @@ extern sigjmp_buf curl_jmpenv; CURLcode Curl_set_dns_servers(struct SessionHandle *data, char *servers); /* + * Function provided by the resolver backend to set + * outgoing interface to use for DNS requests + */ +CURLcode Curl_set_dns_interface(struct SessionHandle *data, + const char *interf); + +/* + * Function provided by the resolver backend to set + * local IPv4 address to use as source address for DNS requests + */ +CURLcode Curl_set_dns_local_ip4(struct SessionHandle *data, + const char *local_ip4); + +/* + * Function provided by the resolver backend to set + * local IPv6 address to use as source address for DNS requests + */ +CURLcode Curl_set_dns_local_ip6(struct SessionHandle *data, + const char *local_ip6); + +/* * Clean off entries from the cache */ -void Curl_hostcache_clean(struct SessionHandle *data); +void Curl_hostcache_clean(struct SessionHandle *data, struct curl_hash *hash); /* * Destroy the hostcache of this handle. diff --git a/plugins/FTPFileYM/curl/lib/hostip4.c b/plugins/FTPFileYM/curl/lib/hostip4.c index 3a38b321f4..1e39f4a987 100644 --- a/plugins/FTPFileYM/curl/lib/hostip4.c +++ b/plugins/FTPFileYM/curl/lib/hostip4.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2012, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2013, 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 @@ -140,7 +140,7 @@ Curl_addrinfo *Curl_ipv4_resolve_r(const char *hostname, #if defined(HAVE_GETADDRINFO_THREADSAFE) else { struct addrinfo hints; - char sbuf[NI_MAXSERV]; + char sbuf[12]; char *sbufptr = NULL; memset(&hints, 0, sizeof(hints)); diff --git a/plugins/FTPFileYM/curl/lib/hostip6.c b/plugins/FTPFileYM/curl/lib/hostip6.c index c42760a640..8327004cd3 100644 --- a/plugins/FTPFileYM/curl/lib/hostip6.c +++ b/plugins/FTPFileYM/curl/lib/hostip6.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2012, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2013, 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 @@ -168,7 +168,7 @@ Curl_addrinfo *Curl_getaddrinfo(struct connectdata *conn, struct addrinfo hints; Curl_addrinfo *res; int error; - char sbuf[NI_MAXSERV]; + char sbuf[12]; char *sbufptr = NULL; char addrbuf[128]; int pf; diff --git a/plugins/FTPFileYM/curl/lib/hostsyn.c b/plugins/FTPFileYM/curl/lib/hostsyn.c index 65a403545a..4ad3c63da4 100644 --- a/plugins/FTPFileYM/curl/lib/hostsyn.c +++ b/plugins/FTPFileYM/curl/lib/hostsyn.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2012, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2013, 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 @@ -72,4 +72,40 @@ CURLcode Curl_set_dns_servers(struct SessionHandle *data, } +/* + * Function provided by the resolver backend to set + * outgoing interface to use for DNS requests + */ +CURLcode Curl_set_dns_interface(struct SessionHandle *data, + const char *interf) +{ + (void)data; + (void)interf; + return CURLE_NOT_BUILT_IN; +} + +/* + * Function provided by the resolver backend to set + * local IPv4 address to use as source address for DNS requests + */ +CURLcode Curl_set_dns_local_ip4(struct SessionHandle *data, + const char *local_ip4) +{ + (void)data; + (void)local_ip4; + return CURLE_NOT_BUILT_IN; +} + +/* + * Function provided by the resolver backend to set + * local IPv6 address to use as source address for DNS requests + */ +CURLcode Curl_set_dns_local_ip6(struct SessionHandle *data, + const char *local_ip6) +{ + (void)data; + (void)local_ip6; + return CURLE_NOT_BUILT_IN; +} + #endif /* truly sync */ diff --git a/plugins/FTPFileYM/curl/lib/http.c b/plugins/FTPFileYM/curl/lib/http.c index daaafe317e..3b18c63115 100644 --- a/plugins/FTPFileYM/curl/lib/http.c +++ b/plugins/FTPFileYM/curl/lib/http.c @@ -73,6 +73,9 @@ #include "http_proxy.h" #include "warnless.h" #include "non-ascii.h" +#include "bundles.h" +#include "pipeline.h" +#include "http2.h" #define _MPRINTF_REPLACE /* use our functions only */ #include <curl/mprintf.h> @@ -103,7 +106,7 @@ static int https_getsock(struct connectdata *conn, */ const struct Curl_handler Curl_handler_http = { "HTTP", /* scheme */ - ZERO_NULL, /* setup_connection */ + Curl_http_setup_conn, /* setup_connection */ Curl_http, /* do_it */ Curl_http_done, /* done */ ZERO_NULL, /* do_more */ @@ -127,7 +130,7 @@ const struct Curl_handler Curl_handler_http = { */ const struct Curl_handler Curl_handler_https = { "HTTPS", /* scheme */ - ZERO_NULL, /* setup_connection */ + Curl_http_setup_conn, /* setup_connection */ Curl_http, /* do_it */ Curl_http_done, /* done */ ZERO_NULL, /* do_more */ @@ -147,6 +150,19 @@ const struct Curl_handler Curl_handler_https = { #endif +CURLcode Curl_http_setup_conn(struct connectdata *conn) +{ + /* allocate the HTTP-specific struct for the SessionHandle, only to survive + during this request */ + DEBUGASSERT(conn->data->req.protop == NULL); + + conn->data->req.protop = calloc(1, sizeof(struct HTTP)); + if(!conn->data->req.protop) + return CURLE_OUT_OF_MEMORY; + + return CURLE_OK; +} + /* * checkheaders() checks the linked list of custom HTTP headers for a * particular header (prefix). @@ -328,7 +344,7 @@ static bool pickoneauth(struct auth *pick) static CURLcode http_perhapsrewind(struct connectdata *conn) { struct SessionHandle *data = conn->data; - struct HTTP *http = data->state.proto.http; + struct HTTP *http = data->req.protop; curl_off_t bytessent; curl_off_t expectsend = -1; /* default is unknown */ @@ -946,7 +962,7 @@ static size_t readmoredata(char *buffer, void *userp) { struct connectdata *conn = (struct connectdata *)userp; - struct HTTP *http = conn->data->state.proto.http; + struct HTTP *http = conn->data->req.protop; size_t fullsize = size * nitems; if(0 == http->postsize) @@ -1017,7 +1033,7 @@ CURLcode Curl_add_buffer_send(Curl_send_buffer *in, CURLcode res; char *ptr; size_t size; - struct HTTP *http = conn->data->state.proto.http; + struct HTTP *http = conn->data->req.protop; size_t sendsize; curl_socket_t sockfd; size_t headersize; @@ -1400,7 +1416,7 @@ CURLcode Curl_http_done(struct connectdata *conn, CURLcode status, bool premature) { struct SessionHandle *data = conn->data; - struct HTTP *http =data->state.proto.http; + struct HTTP *http =data->req.protop; Curl_unencode_cleanup(conn); @@ -1440,6 +1456,7 @@ CURLcode Curl_http_done(struct connectdata *conn, if(!premature && /* this check is pointless when DONE is called before the entire operation is complete */ !conn->bits.retry && + !data->set.connect_only && ((http->readbytecount + data->req.headerbytecount - data->req.deductheadercount)) <= 0) { @@ -1454,14 +1471,19 @@ CURLcode Curl_http_done(struct connectdata *conn, } -/* Determine if we should use HTTP 1.1 for this request. Reasons to avoid it - are if the user specifically requested HTTP 1.0, if the server we are - connected to only supports 1.0, or if any server previously contacted to - handle this request only supports 1.0. */ -static bool use_http_1_1(const struct SessionHandle *data, - const struct connectdata *conn) +/* + * Determine if we should use HTTP 1.1 (OR BETTER) for this request. Reasons + * to avoid it include: + * + * - if the user specifically requested HTTP 1.0 + * - if the server we are connected to only supports 1.0 + * - if any server previously contacted to handle this request only supports + * 1.0. + */ +static bool use_http_1_1plus(const struct SessionHandle *data, + const struct connectdata *conn) { - return ((data->set.httpversion == CURL_HTTP_VERSION_1_1) || + return ((data->set.httpversion >= CURL_HTTP_VERSION_1_1) || ((data->set.httpversion != CURL_HTTP_VERSION_1_0) && ((conn->httpversion == 11) || ((conn->httpversion != 10) && @@ -1477,7 +1499,7 @@ static CURLcode expect100(struct SessionHandle *data, const char *ptr; data->state.expect100header = FALSE; /* default to false unless it is set to TRUE below */ - if(use_http_1_1(data, conn)) { + if(use_http_1_1plus(data, conn)) { /* if not doing HTTP 1.0 or disabled explicitly, we add a Expect: 100-continue to the headers which actually speeds up post operations (as there is one packet coming back from the web server) */ @@ -1653,20 +1675,7 @@ CURLcode Curl_http(struct connectdata *conn, bool *done) the rest of the request in the PERFORM phase. */ *done = TRUE; - /* If there already is a protocol-specific struct allocated for this - sessionhandle, deal with it */ - Curl_reset_reqproto(conn); - - if(!data->state.proto.http) { - /* Only allocate this struct if we don't already have it! */ - - http = calloc(1, sizeof(struct HTTP)); - if(!http) - return CURLE_OUT_OF_MEMORY; - data->state.proto.http = http; - } - else - http = data->state.proto.http; + http = data->req.protop; if(!data->state.this_is_a_follow) { /* this is not a followed location, get the original host name */ @@ -1737,8 +1746,11 @@ CURLcode Curl_http(struct connectdata *conn, bool *done) conn->bits.authneg = FALSE; Curl_safefree(conn->allocptr.ref); - if(data->change.referer && !Curl_checkheaders(data, "Referer:")) + if(data->change.referer && !Curl_checkheaders(data, "Referer:")) { conn->allocptr.ref = aprintf("Referer: %s\r\n", data->change.referer); + if(!conn->allocptr.ref) + return CURLE_OUT_OF_MEMORY; + } else conn->allocptr.ref = NULL; @@ -1790,7 +1802,7 @@ CURLcode Curl_http(struct connectdata *conn, bool *done) if(conn->bits.authneg) /* don't enable chunked during auth neg */ ; - else if(use_http_1_1(data, conn)) { + else if(use_http_1_1plus(data, conn)) { /* HTTP, upload, unknown file size and not HTTP 1.0 */ data->req.upload_chunky = TRUE; } @@ -2090,7 +2102,7 @@ CURLcode Curl_http(struct connectdata *conn, bool *done) /* Use 1.1 unless the user specifically asked for 1.0 or the server only supports 1.0 */ - httpstring= use_http_1_1(data, conn)?"1.1":"1.0"; + httpstring= use_http_1_1plus(data, conn)?"1.1":"1.0"; /* initialize a dynamic send-buffer */ req_buffer = Curl_add_buffer_init(); @@ -2168,6 +2180,15 @@ CURLcode Curl_http(struct connectdata *conn, bool *done) if(result) return result; + if(!(conn->handler->flags&PROTOPT_SSL) && + (data->set.httpversion == CURL_HTTP_VERSION_2_0)) { + /* append HTTP2 updrade magic stuff to the HTTP request if it isn't done + over SSL */ + result = Curl_http2_request(req_buffer, conn); + if(result) + return result; + } + #if !defined(CURL_DISABLE_COOKIES) if(data->cookies || addcookies) { struct Cookie *co=NULL; /* no cookies from start */ @@ -3148,13 +3169,19 @@ CURLcode Curl_http_readwrite_headers(struct SessionHandle *data, } else if(conn->httpversion >= 11 && !conn->bits.close) { + struct connectbundle *cb_ptr; /* If HTTP version is >= 1.1 and connection is persistent server supports pipelining. */ DEBUGF(infof(data, "HTTP 1.1 or later with persistent connection, " "pipelining supported\n")); - conn->server_supports_pipelining = TRUE; + /* Activate pipelining if needed */ + cb_ptr = conn->bundle; + if(cb_ptr) { + if(!Curl_pipeline_site_blacklisted(data, conn)) + cb_ptr->server_supports_pipelining = TRUE; + } } switch(k->httpcode) { @@ -3231,6 +3258,16 @@ CURLcode Curl_http_readwrite_headers(struct SessionHandle *data, data->info.contenttype = contenttype; } } + else if(checkprefix("Server:", k->p)) { + char *server_name = copy_header_value(k->p); + + /* Turn off pipelining if the server version is blacklisted */ + if(conn->bundle && conn->bundle->server_supports_pipelining) { + if(Curl_pipeline_server_blacklisted(data, server_name)) + conn->bundle->server_supports_pipelining = FALSE; + } + Curl_safefree(server_name); + } else if((conn->httpversion == 10) && conn->bits.httpproxy && Curl_compareheader(k->p, diff --git a/plugins/FTPFileYM/curl/lib/http.h b/plugins/FTPFileYM/curl/lib/http.h index 7236dd88ca..a506238a66 100644 --- a/plugins/FTPFileYM/curl/lib/http.h +++ b/plugins/FTPFileYM/curl/lib/http.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2011, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2013, 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 @@ -21,8 +21,14 @@ * KIND, either express or implied. * ***************************************************************************/ +#include "curl_setup.h" + #ifndef CURL_DISABLE_HTTP +#ifdef USE_NGHTTP2 +#include <nghttp2/nghttp2.h> +#endif + extern const struct Curl_handler Curl_handler_http; #ifdef USE_SSL @@ -66,6 +72,7 @@ CURLcode Curl_add_custom_headers(struct connectdata *conn, CURLcode Curl_http(struct connectdata *conn, bool *done); CURLcode Curl_http_done(struct connectdata *, CURLcode, bool premature); CURLcode Curl_http_connect(struct connectdata *conn, bool *done); +CURLcode Curl_http_setup_conn(struct connectdata *conn); /* The following functions are defined in http_chunks.c */ void Curl_httpchunk_init(struct connectdata *conn); @@ -141,6 +148,14 @@ struct HTTP { points to an allocated send_buffer struct */ }; +struct http_conn { +#ifdef USE_NGHTTP2 + nghttp2_session *h2; +#else + int unused; /* prevent a compiler warning */ +#endif +}; + CURLcode Curl_http_readwrite_headers(struct SessionHandle *data, struct connectdata *conn, ssize_t *nread, diff --git a/plugins/FTPFileYM/curl/lib/http2.c b/plugins/FTPFileYM/curl/lib/http2.c new file mode 100644 index 0000000000..b876436e1a --- /dev/null +++ b/plugins/FTPFileYM/curl/lib/http2.c @@ -0,0 +1,182 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2013, 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 + * are also available at http://curl.haxx.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ***************************************************************************/ + +#include "curl_setup.h" + +#ifdef USE_NGHTTP2 +#define _MPRINTF_REPLACE +#include <curl/mprintf.h> + +#include <nghttp2/nghttp2.h> +#include "urldata.h" +#include "http2.h" +#include "http.h" +#include "sendf.h" +#include "curl_base64.h" +#include "curl_memory.h" + +/* include memdebug.h last */ +#include "memdebug.h" + +/* + * Store nghttp2 version info in this buffer, Prefix with a space. Return + * total length written. + */ +int Curl_http2_ver(char *p, size_t len) +{ + nghttp2_info *h2 = nghttp2_version(0); + return snprintf(p, len, " nghttp2/%s", h2->version_str); +} + +/* + * The implementation of nghttp2_send_callback type. Here we write |data| with + * size |length| to the network and return the number of bytes actually + * written. See the documentation of nghttp2_send_callback for the details. + */ +static ssize_t send_callback(nghttp2_session *h2, + const uint8_t *data, size_t length, int flags, + void *userp) +{ + struct connectdata *conn = (struct connectdata *)userp; + ssize_t written; + CURLcode rc = + Curl_write(conn, conn->sock[0], data, length, &written); + (void)h2; + (void)flags; + + if(rc) { + failf(conn->data, "Failed sending HTTP2 data"); + return NGHTTP2_ERR_CALLBACK_FAILURE; + } + else if(!written) + return NGHTTP2_ERR_WOULDBLOCK; + + return written; +} + +/* + * The implementation of nghttp2_recv_callback type. Here we read data from + * the network and write them in |buf|. The capacity of |buf| is |length| + * bytes. Returns the number of bytes stored in |buf|. See the documentation + * of nghttp2_recv_callback for the details. + */ +static ssize_t recv_callback(nghttp2_session *h2, + uint8_t *buf, size_t length, int flags, + void *userp) +{ + struct connectdata *conn = (struct connectdata *)userp; + ssize_t nread; + CURLcode rc = Curl_read(conn, conn->sock[0], (char *)buf, length, &nread); + (void)h2; + (void)flags; + + if(rc) { + failf(conn->data, "Failed recving HTTP2 data"); + return NGHTTP2_ERR_CALLBACK_FAILURE; + } + if(!nread) + return NGHTTP2_ERR_WOULDBLOCK; + + return nread; +} + +/* + * This is all callbacks nghttp2 calls + */ +static const nghttp2_session_callbacks callbacks = { + send_callback, + recv_callback, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL +}; + +/* + * The HTTP2 settings we send in the Upgrade request + */ +static nghttp2_settings_entry settings[] = { + { NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 100 }, + { NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE, NGHTTP2_INITIAL_WINDOW_SIZE }, +}; + +/* + * Append headers to ask for a HTTP1.1 to HTTP2 upgrade. + */ +CURLcode Curl_http2_request(Curl_send_buffer *req, + struct connectdata *conn) +{ + uint8_t binsettings[80]; + CURLcode result; + ssize_t binlen; + char *base64; + size_t blen; + + if(!conn->proto.httpc.h2) { + /* The nghttp2 session is not yet setup, do it */ + int rc = nghttp2_session_client_new(&conn->proto.httpc.h2, + &callbacks, &conn); + if(rc) { + failf(conn->data, "Couldn't initialize nghttp2!"); + return CURLE_OUT_OF_MEMORY; /* most likely at least */ + } + } + + /* As long as we have a fixed set of settings, we don't have to dynamically + * figure out the base64 strings since it'll always be the same. However, + * the settings will likely not be fixed every time in the future. + */ + + /* this returns number of bytes it wrote */ + binlen = nghttp2_pack_settings_payload(binsettings, + sizeof(binsettings), + settings, + sizeof(settings)/sizeof(settings[0])); + if(!binlen) { + failf(conn->data, "nghttp2 unexpectedly failed on pack_settings_payload"); + return CURLE_FAILED_INIT; + } + + result = Curl_base64_encode(conn->data, (const char *)binsettings, binlen, + &base64, &blen); + if(result) + return result; + + result = Curl_add_bufferf(req, + "Connection: Upgrade, HTTP2-Settings\r\n" + "Upgrade: %s\r\n" + "HTTP2-Settings: %s\r\n", + NGHTTP2_PROTO_VERSION_ID, base64); + free(base64); + + return result; +} + +#endif diff --git a/plugins/FTPFileYM/curl/lib/http2.h b/plugins/FTPFileYM/curl/lib/http2.h new file mode 100644 index 0000000000..09b91d1122 --- /dev/null +++ b/plugins/FTPFileYM/curl/lib/http2.h @@ -0,0 +1,42 @@ +#ifndef HEADER_CURL_HTTP2_H +#define HEADER_CURL_HTTP2_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2013, 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 + * are also available at http://curl.haxx.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ***************************************************************************/ + +#include "curl_setup.h" + +#ifdef USE_NGHTTP2 +#include "http.h" +/* + * Store nghttp2 version info in this buffer, Prefix with a space. Return + * total length written. + */ +int Curl_http2_ver(char *p, size_t len); + +CURLcode Curl_http2_request(Curl_send_buffer *req, + struct connectdata *conn); +#else /* USE_NGHTTP2 */ +#define Curl_http2_request(x,y) CURLE_OK +#endif + +#endif /* HEADER_CURL_HTTP2_H */ + diff --git a/plugins/FTPFileYM/curl/lib/http_digest.c b/plugins/FTPFileYM/curl/lib/http_digest.c index f9f20d487f..f50f8033f9 100644 --- a/plugins/FTPFileYM/curl/lib/http_digest.c +++ b/plugins/FTPFileYM/curl/lib/http_digest.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2012, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2013, 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 @@ -25,14 +25,13 @@ #if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_CRYPTO_AUTH) #include "urldata.h" -#include "sendf.h" #include "rawstr.h" #include "curl_base64.h" #include "curl_md5.h" #include "http_digest.h" #include "strtok.h" -#include "url.h" /* for Curl_safefree() */ #include "curl_memory.h" +#include "sslgen.h" /* for Curl_rand() */ #include "non-ascii.h" /* included for Curl_convert_... prototypes */ #include "warnless.h" @@ -267,6 +266,38 @@ static void md5_to_ascii(unsigned char *source, /* 16 bytes */ snprintf((char *)&dest[i*2], 3, "%02x", source[i]); } +/* Perform quoted-string escaping as described in RFC2616 and its errata */ +static char *string_quoted(const char *source) +{ + char *dest, *d; + const char *s = source; + size_t n = 1; /* null terminator */ + + /* Calculate size needed */ + while(*s) { + ++n; + if(*s == '"' || *s == '\\') { + ++n; + } + ++s; + } + + dest = malloc(n); + if(dest) { + s = source; + d = dest; + while(*s) { + if(*s == '"' || *s == '\\') { + *d++ = '\\'; + } + *d++ = *s++; + } + *d = 0; + } + + return dest; +} + CURLcode Curl_output_digest(struct connectdata *conn, bool proxy, const unsigned char *request, @@ -278,16 +309,16 @@ CURLcode Curl_output_digest(struct connectdata *conn, unsigned char md5buf[16]; /* 16 bytes/128 bits */ unsigned char request_digest[33]; unsigned char *md5this; - unsigned char *ha1; + unsigned char ha1[33];/* 32 digits and 1 zero byte */ unsigned char ha2[33];/* 32 digits and 1 zero byte */ char cnoncebuf[33]; char *cnonce = NULL; size_t cnonce_sz = 0; char *tmp = NULL; - struct timeval now; - char **allocuserpwd; + size_t userlen; const char *userp; + char *userp_quoted; const char *passwdp; struct auth *authp; @@ -320,10 +351,7 @@ CURLcode Curl_output_digest(struct connectdata *conn, authp = &data->state.authhost; } - if(*allocuserpwd) { - Curl_safefree(*allocuserpwd); - *allocuserpwd = NULL; - } + Curl_safefree(*allocuserpwd); /* not set means empty */ if(!userp) @@ -342,10 +370,11 @@ CURLcode Curl_output_digest(struct connectdata *conn, d->nc = 1; if(!d->cnonce) { - /* Generate a cnonce */ - now = Curl_tvnow(); - snprintf(cnoncebuf, sizeof(cnoncebuf), "%32ld", - (long)now.tv_sec + now.tv_usec); + struct timeval now = Curl_tvnow(); + snprintf(cnoncebuf, sizeof(cnoncebuf), "%08x%08x%08x%08x", + Curl_rand(data), Curl_rand(data), + (unsigned int)now.tv_sec, + (unsigned int)now.tv_usec); rc = Curl_base64_encode(data, cnoncebuf, strlen(cnoncebuf), &cnonce, &cnonce_sz); @@ -372,12 +401,7 @@ CURLcode Curl_output_digest(struct connectdata *conn, CURL_OUTPUT_DIGEST_CONV(data, md5this); /* convert on non-ASCII machines */ Curl_md5it(md5buf, md5this); - free(md5this); /* free this again */ - - ha1 = malloc(33); /* 32 digits and 1 zero byte */ - if(!ha1) - return CURLE_OUT_OF_MEMORY; - + Curl_safefree(md5this); md5_to_ascii(md5buf, ha1); if(d->algo == CURLDIGESTALGO_MD5SESS) { @@ -387,7 +411,7 @@ CURLcode Curl_output_digest(struct connectdata *conn, return CURLE_OUT_OF_MEMORY; CURL_OUTPUT_DIGEST_CONV(data, tmp); /* convert on non-ASCII machines */ Curl_md5it(md5buf, (unsigned char *)tmp); - free(tmp); /* free this again */ + Curl_safefree(tmp); md5_to_ascii(md5buf, ha1); } @@ -424,19 +448,21 @@ CURLcode Curl_output_digest(struct connectdata *conn, else md5this = (unsigned char *)aprintf("%s:%s", request, uripath); - if(!md5this) { - free(ha1); - return CURLE_OUT_OF_MEMORY; - } - if(d->qop && Curl_raw_equal(d->qop, "auth-int")) { - /* We don't support auth-int at the moment. I can't see a easy way to get - entity-body here */ - /* TODO: Append H(entity-body)*/ + /* We don't support auth-int for PUT or POST at the moment. + TODO: replace md5 of empty string with entity-body for PUT/POST */ + unsigned char *md5this2 = (unsigned char *) + aprintf("%s:%s", md5this, "d41d8cd98f00b204e9800998ecf8427e"); + Curl_safefree(md5this); + md5this = md5this2; } + + if(!md5this) + return CURLE_OUT_OF_MEMORY; + CURL_OUTPUT_DIGEST_CONV(data, md5this); /* convert on non-ASCII machines */ Curl_md5it(md5buf, md5this); - free(md5this); /* free this again */ + Curl_safefree(md5this); md5_to_ascii(md5buf, ha2); if(d->qop) { @@ -454,20 +480,30 @@ CURLcode Curl_output_digest(struct connectdata *conn, d->nonce, ha2); } - free(ha1); if(!md5this) return CURLE_OUT_OF_MEMORY; CURL_OUTPUT_DIGEST_CONV(data, md5this); /* convert on non-ASCII machines */ Curl_md5it(md5buf, md5this); - free(md5this); /* free this again */ + Curl_safefree(md5this); md5_to_ascii(md5buf, request_digest); /* for test case 64 (snooped from a Mozilla 1.3a request) Authorization: Digest username="testuser", realm="testrealm", \ nonce="1053604145", uri="/64", response="c55f7f30d83d774a3d2dcacf725abaca" + + Digest parameters are all quoted strings. Username which is provided by + the user will need double quotes and backslashes within it escaped. For + the other fields, this shouldn't be an issue. realm, nonce, and opaque + are copied as is from the server, escapes and all. cnonce is generated + with web-safe characters. uri is already percent encoded. nc is 8 hex + characters. algorithm and qop with standard values only contain web-safe + chracters. */ + userp_quoted = string_quoted(userp); + if(!userp_quoted) + return CURLE_OUT_OF_MEMORY; if(d->qop) { *allocuserpwd = @@ -481,7 +517,7 @@ CURLcode Curl_output_digest(struct connectdata *conn, "qop=%s, " "response=\"%s\"", proxy?"Proxy-":"", - userp, + userp_quoted, d->realm, d->nonce, uripath, /* this is the PATH part of the URL */ @@ -504,12 +540,13 @@ CURLcode Curl_output_digest(struct connectdata *conn, "uri=\"%s\", " "response=\"%s\"", proxy?"Proxy-":"", - userp, + userp_quoted, d->realm, d->nonce, uripath, /* this is the PATH part of the URL */ request_digest); } + Curl_safefree(userp_quoted); if(!*allocuserpwd) return CURLE_OUT_OF_MEMORY; @@ -533,10 +570,11 @@ CURLcode Curl_output_digest(struct connectdata *conn, } /* append CRLF + zero (3 bytes) to the userpwd header */ - tmp = realloc(*allocuserpwd, strlen(*allocuserpwd) + 3); + userlen = strlen(*allocuserpwd); + tmp = realloc(*allocuserpwd, userlen + 3); if(!tmp) return CURLE_OUT_OF_MEMORY; - strcat(tmp, "\r\n"); + strcpy(&tmp[userlen], "\r\n"); /* append the data */ *allocuserpwd = tmp; return CURLE_OK; @@ -544,29 +582,12 @@ CURLcode Curl_output_digest(struct connectdata *conn, static void digest_cleanup_one(struct digestdata *d) { - if(d->nonce) - free(d->nonce); - d->nonce = NULL; - - if(d->cnonce) - free(d->cnonce); - d->cnonce = NULL; - - if(d->realm) - free(d->realm); - d->realm = NULL; - - if(d->opaque) - free(d->opaque); - d->opaque = NULL; - - if(d->qop) - free(d->qop); - d->qop = NULL; - - if(d->algorithm) - free(d->algorithm); - d->algorithm = NULL; + Curl_safefree(d->nonce); + Curl_safefree(d->cnonce); + Curl_safefree(d->realm); + Curl_safefree(d->opaque); + Curl_safefree(d->qop); + Curl_safefree(d->algorithm); d->nc = 0; d->algo = CURLDIGESTALGO_MD5; /* default algorithm */ diff --git a/plugins/FTPFileYM/curl/lib/http_negotiate.c b/plugins/FTPFileYM/curl/lib/http_negotiate.c index 535a4279e2..9b981b3327 100644 --- a/plugins/FTPFileYM/curl/lib/http_negotiate.c +++ b/plugins/FTPFileYM/curl/lib/http_negotiate.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2012, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2013, 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 @@ -135,7 +135,7 @@ int Curl_input_negotiate(struct connectdata *conn, bool proxy, struct SessionHandle *data = conn->data; struct negotiatedata *neg_ctx = proxy?&data->state.proxyneg: &data->state.negotiate; - OM_uint32 major_status, minor_status, minor_status2; + OM_uint32 major_status, minor_status, discard_st; gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER; gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER; int ret; @@ -192,46 +192,50 @@ int Curl_input_negotiate(struct connectdata *conn, bool proxy, return -1; input_token.length = rawlen; + DEBUGASSERT(input_token.value != NULL); + #ifdef HAVE_SPNEGO /* Handle SPNEGO */ if(checkprefix("Negotiate", header)) { - ASN1_OBJECT * object = NULL; - unsigned char * spnegoToken = NULL; + unsigned char *spnegoToken = NULL; size_t spnegoTokenLength = 0; - unsigned char * mechToken = NULL; - size_t mechTokenLength = 0; - - if(input_token.value == NULL) - return CURLE_OUT_OF_MEMORY; + gss_buffer_desc mechToken = GSS_C_EMPTY_BUFFER; spnegoToken = malloc(input_token.length); - if(spnegoToken == NULL) + if(spnegoToken == NULL) { + Curl_safefree(input_token.value); return CURLE_OUT_OF_MEMORY; - + } + memcpy(spnegoToken, input_token.value, input_token.length); spnegoTokenLength = input_token.length; - object = OBJ_txt2obj ("1.2.840.113554.1.2.2", 1); if(!parseSpnegoTargetToken(spnegoToken, spnegoTokenLength, NULL, NULL, - &mechToken, - &mechTokenLength, + (unsigned char**)&mechToken.value, + &mechToken.length, NULL, NULL)) { - free(spnegoToken); - spnegoToken = NULL; + Curl_safefree(spnegoToken); infof(data, "Parse SPNEGO Target Token failed\n"); } + else if(!mechToken.value || !mechToken.length) { + Curl_safefree(spnegoToken); + if(mechToken.value) + gss_release_buffer(&discard_st, &mechToken); + infof(data, "Parse SPNEGO Target Token succeeded (NULL token)\n"); + } else { - free(input_token.value); - input_token.value = malloc(mechTokenLength); - if(input_token.value == NULL) + Curl_safefree(spnegoToken); + Curl_safefree(input_token.value); + input_token.value = malloc(mechToken.length); + if(input_token.value == NULL) { + gss_release_buffer(&discard_st, &mechToken); return CURLE_OUT_OF_MEMORY; - - memcpy(input_token.value, mechToken,mechTokenLength); - input_token.length = mechTokenLength; - free(mechToken); - mechToken = NULL; + } + memcpy(input_token.value, mechToken.value, mechToken.length); + input_token.length = mechToken.length; + gss_release_buffer(&discard_st, &mechToken); infof(data, "Parse SPNEGO Target Token succeeded\n"); } } @@ -246,23 +250,23 @@ int Curl_input_negotiate(struct connectdata *conn, bool proxy, &input_token, &output_token, NULL); - if(input_token.length > 0) - gss_release_buffer(&minor_status2, &input_token); + Curl_safefree(input_token.value); + neg_ctx->status = major_status; if(GSS_ERROR(major_status)) { - /* Curl_cleanup_negotiate(data) ??? */ - log_gss_error(conn, minor_status, - "gss_init_sec_context() failed: "); + if(output_token.value) + gss_release_buffer(&discard_st, &output_token); + log_gss_error(conn, minor_status, "gss_init_sec_context() failed: "); return -1; } - if(output_token.length == 0) { + if(!output_token.value || !output_token.length) { + if(output_token.value) + gss_release_buffer(&discard_st, &output_token); return -1; } neg_ctx->output_token = output_token; - /* conn->bits.close = FALSE; */ - return 0; } @@ -275,14 +279,14 @@ CURLcode Curl_output_negotiate(struct connectdata *conn, bool proxy) size_t len = 0; char *userp; CURLcode error; + OM_uint32 discard_st; #ifdef HAVE_SPNEGO /* Handle SPNEGO */ if(checkprefix("Negotiate", neg_ctx->protocol)) { - ASN1_OBJECT * object = NULL; - unsigned char * spnegoToken = NULL; - size_t spnegoTokenLength = 0; - unsigned char * responseToken = NULL; + ASN1_OBJECT *object = NULL; + unsigned char *responseToken = NULL; size_t responseTokenLength = 0; + gss_buffer_desc spnegoToken = GSS_C_EMPTY_BUFFER; responseToken = malloc(neg_ctx->output_token.length); if(responseToken == NULL) @@ -291,30 +295,34 @@ CURLcode Curl_output_negotiate(struct connectdata *conn, bool proxy) neg_ctx->output_token.length); responseTokenLength = neg_ctx->output_token.length; - object=OBJ_txt2obj ("1.2.840.113554.1.2.2", 1); - if(!makeSpnegoInitialToken (object, - responseToken, - responseTokenLength, - &spnegoToken, - &spnegoTokenLength)) { - free(responseToken); - responseToken = NULL; + object = OBJ_txt2obj("1.2.840.113554.1.2.2", 1); + if(!object) { + Curl_safefree(responseToken); + return CURLE_OUT_OF_MEMORY; + } + + if(!makeSpnegoInitialToken(object, + responseToken, + responseTokenLength, + (unsigned char**)&spnegoToken.value, + &spnegoToken.length)) { + Curl_safefree(responseToken); + ASN1_OBJECT_free(object); infof(conn->data, "Make SPNEGO Initial Token failed\n"); } + else if(!spnegoToken.value || !spnegoToken.length) { + Curl_safefree(responseToken); + ASN1_OBJECT_free(object); + if(spnegoToken.value) + gss_release_buffer(&discard_st, &spnegoToken); + infof(conn->data, "Make SPNEGO Initial Token succeeded (NULL token)\n"); + } else { - free(responseToken); - responseToken = NULL; - free(neg_ctx->output_token.value); - neg_ctx->output_token.value = malloc(spnegoTokenLength); - if(neg_ctx->output_token.value == NULL) { - free(spnegoToken); - spnegoToken = NULL; - return CURLE_OUT_OF_MEMORY; - } - memcpy(neg_ctx->output_token.value, spnegoToken,spnegoTokenLength); - neg_ctx->output_token.length = spnegoTokenLength; - free(spnegoToken); - spnegoToken = NULL; + Curl_safefree(responseToken); + ASN1_OBJECT_free(object); + gss_release_buffer(&discard_st, &neg_ctx->output_token); + neg_ctx->output_token.value = spnegoToken.value; + neg_ctx->output_token.length = spnegoToken.length; infof(conn->data, "Make SPNEGO Initial Token succeeded\n"); } } @@ -324,26 +332,33 @@ CURLcode Curl_output_negotiate(struct connectdata *conn, bool proxy) neg_ctx->output_token.length, &encoded, &len); if(error) { - Curl_safefree(neg_ctx->output_token.value); + gss_release_buffer(&discard_st, &neg_ctx->output_token); neg_ctx->output_token.value = NULL; + neg_ctx->output_token.length = 0; return error; } - if(len == 0) { - Curl_safefree(neg_ctx->output_token.value); + if(!encoded || !len) { + gss_release_buffer(&discard_st, &neg_ctx->output_token); neg_ctx->output_token.value = NULL; + neg_ctx->output_token.length = 0; return CURLE_REMOTE_ACCESS_DENIED; } userp = aprintf("%sAuthorization: %s %s\r\n", proxy ? "Proxy-" : "", neg_ctx->protocol, encoded); - - if(proxy) + if(proxy) { + Curl_safefree(conn->allocptr.proxyuserpwd); conn->allocptr.proxyuserpwd = userp; - else + } + else { + Curl_safefree(conn->allocptr.userpwd); conn->allocptr.userpwd = userp; - free(encoded); - Curl_cleanup_negotiate (conn->data); + } + + Curl_safefree(encoded); + Curl_cleanup_negotiate(conn->data); + return (userp == NULL) ? CURLE_OUT_OF_MEMORY : CURLE_OK; } @@ -353,7 +368,7 @@ static void cleanup(struct negotiatedata *neg_ctx) if(neg_ctx->context != GSS_C_NO_CONTEXT) gss_delete_sec_context(&minor_status, &neg_ctx->context, GSS_C_NO_BUFFER); - if(neg_ctx->output_token.length != 0) + if(neg_ctx->output_token.value) gss_release_buffer(&minor_status, &neg_ctx->output_token); if(neg_ctx->server_name != GSS_C_NO_NAME) diff --git a/plugins/FTPFileYM/curl/lib/http_proxy.c b/plugins/FTPFileYM/curl/lib/http_proxy.c index 4f17ce2f60..6a555525d1 100644 --- a/plugins/FTPFileYM/curl/lib/http_proxy.c +++ b/plugins/FTPFileYM/curl/lib/http_proxy.c @@ -66,13 +66,13 @@ CURLcode Curl_proxy_connect(struct connectdata *conn) * This function might be called several times in the multi interface case * if the proxy's CONNTECT response is not instant. */ - prot_save = conn->data->state.proto.generic; + prot_save = conn->data->req.protop; memset(&http_proxy, 0, sizeof(http_proxy)); - conn->data->state.proto.http = &http_proxy; + conn->data->req.protop = &http_proxy; conn->bits.close = FALSE; result = Curl_proxyCONNECT(conn, FIRSTSOCKET, conn->host.name, conn->remote_port); - conn->data->state.proto.generic = prot_save; + conn->data->req.protop = prot_save; if(CURLE_OK != result) return result; #else @@ -356,6 +356,10 @@ CURLcode Curl_proxyCONNECT(struct connectdata *conn, result = Curl_client_write(conn, writetype, line_start, perline); + + data->info.header_size += (long)perline; + data->req.headerbytecount += (long)perline; + if(result) return result; @@ -560,6 +564,8 @@ CURLcode Curl_proxyCONNECT(struct connectdata *conn, infof (data, "Proxy replied OK to CONNECT request\n"); data->req.ignorebody = FALSE; /* put it (back) to non-ignore state */ + conn->bits.rewindaftersend = FALSE; /* make sure this isn't set for the + document request */ return CURLE_OK; } #endif /* CURL_DISABLE_PROXY */ diff --git a/plugins/FTPFileYM/curl/lib/if2ip.c b/plugins/FTPFileYM/curl/lib/if2ip.c index 558e30f15f..05ae7d6f87 100644 --- a/plugins/FTPFileYM/curl/lib/if2ip.c +++ b/plugins/FTPFileYM/curl/lib/if2ip.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2012, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2013, 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 @@ -83,40 +83,63 @@ bool Curl_if_is_interface_name(const char *interf) return result; } -char *Curl_if2ip(int af, const char *interf, char *buf, int buf_size) +if2ip_result_t Curl_if2ip(int af, unsigned int remote_scope, + const char *interf, char *buf, int buf_size) { struct ifaddrs *iface, *head; - char *ip = NULL; + if2ip_result_t res = IF2IP_NOT_FOUND; + +#ifndef ENABLE_IPV6 + (void) remote_scope; +#endif if(getifaddrs(&head) >= 0) { for(iface=head; iface != NULL; iface=iface->ifa_next) { - if((iface->ifa_addr != NULL) && - (iface->ifa_addr->sa_family == af) && - curl_strequal(iface->ifa_name, interf)) { - void *addr; - char scope[12]=""; + if(iface->ifa_addr != NULL) { + if(iface->ifa_addr->sa_family == af) { + if(curl_strequal(iface->ifa_name, interf)) { + void *addr; + char *ip; + char scope[12]=""; + char ipstr[64]; #ifdef ENABLE_IPV6 - if(af == AF_INET6) { - unsigned int scopeid = 0; - addr = &((struct sockaddr_in6 *)iface->ifa_addr)->sin6_addr; + if(af == AF_INET6) { + unsigned int scopeid = 0; + addr = &((struct sockaddr_in6 *)iface->ifa_addr)->sin6_addr; #ifdef HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID - /* Include the scope of this interface as part of the address */ - scopeid = ((struct sockaddr_in6 *)iface->ifa_addr)->sin6_scope_id; + /* Include the scope of this interface as part of the address */ + scopeid = + ((struct sockaddr_in6 *)iface->ifa_addr)->sin6_scope_id; #endif - if(scopeid) - snprintf(scope, sizeof(scope), "%%%u", scopeid); - } - else + if(scopeid != remote_scope) { + /* We are interested only in interface addresses whose + scope ID matches the remote address we want to + connect to: global (0) for global, link-local for + link-local, etc... */ + if(res == IF2IP_NOT_FOUND) res = IF2IP_AF_NOT_SUPPORTED; + continue; + } + if(scopeid) + snprintf(scope, sizeof(scope), "%%%u", scopeid); + } + else #endif - addr = &((struct sockaddr_in *)iface->ifa_addr)->sin_addr; - ip = (char *) Curl_inet_ntop(af, addr, buf, buf_size); - strlcat(buf, scope, buf_size); - break; + addr = &((struct sockaddr_in *)iface->ifa_addr)->sin_addr; + res = IF2IP_FOUND; + ip = (char *) Curl_inet_ntop(af, addr, ipstr, sizeof(ipstr)); + snprintf(buf, buf_size, "%s%s", ip, scope); + break; + } + } + else if((res == IF2IP_NOT_FOUND) && + curl_strequal(iface->ifa_name, interf)) { + res = IF2IP_AF_NOT_SUPPORTED; + } } } freeifaddrs(head); } - return ip; + return res; } #elif defined(HAVE_IOCTL_SIOCGIFADDR) @@ -126,30 +149,31 @@ bool Curl_if_is_interface_name(const char *interf) /* This is here just to support the old interfaces */ char buf[256]; - char *ip = Curl_if2ip(AF_INET, interf, buf, sizeof(buf)); - - return (ip != NULL) ? TRUE : FALSE; + return (Curl_if2ip(AF_INET, 0, interf, buf, sizeof(buf)) == + IF2IP_NOT_FOUND) ? FALSE : TRUE; } -char *Curl_if2ip(int af, const char *interf, char *buf, int buf_size) +if2ip_result_t Curl_if2ip(int af, unsigned int remote_scope, + const char *interf, char *buf, int buf_size) { struct ifreq req; struct in_addr in; struct sockaddr_in *s; curl_socket_t dummy; size_t len; - char *ip; + + (void)remote_scope; if(!interf || (af != AF_INET)) - return NULL; + return IF2IP_NOT_FOUND; len = strlen(interf); if(len >= sizeof(req.ifr_name)) - return NULL; + return IF2IP_NOT_FOUND; dummy = socket(AF_INET, SOCK_STREAM, 0); if(CURL_SOCKET_BAD == dummy) - return NULL; + return IF2IP_NOT_FOUND; memset(&req, 0, sizeof(req)); memcpy(req.ifr_name, interf, len+1); @@ -157,15 +181,18 @@ char *Curl_if2ip(int af, const char *interf, char *buf, int buf_size) if(ioctl(dummy, SIOCGIFADDR, &req) < 0) { sclose(dummy); - return NULL; + /* With SIOCGIFADDR, we cannot tell the difference between an interface + that does not exist and an interface that has no address of the + correct family. Assume the interface does not exist */ + return IF2IP_NOT_FOUND; } s = (struct sockaddr_in *)&req.ifr_addr; memcpy(&in, &s->sin_addr, sizeof(in)); - ip = (char *) Curl_inet_ntop(s->sin_family, &in, buf, buf_size); + Curl_inet_ntop(s->sin_family, &in, buf, buf_size); sclose(dummy); - return ip; + return IF2IP_FOUND; } #else @@ -177,13 +204,15 @@ bool Curl_if_is_interface_name(const char *interf) return FALSE; } -char *Curl_if2ip(int af, const char *interf, char *buf, int buf_size) +if2ip_result_t Curl_if2ip(int af, unsigned int remote_scope, + const char *interf, char *buf, int buf_size) { (void) af; + (void) remote_scope; (void) interf; (void) buf; (void) buf_size; - return NULL; + return IF2IP_NOT_FOUND; } #endif diff --git a/plugins/FTPFileYM/curl/lib/if2ip.h b/plugins/FTPFileYM/curl/lib/if2ip.h index 858ec2f5b3..ac58752378 100644 --- a/plugins/FTPFileYM/curl/lib/if2ip.h +++ b/plugins/FTPFileYM/curl/lib/if2ip.h @@ -24,7 +24,15 @@ #include "curl_setup.h" bool Curl_if_is_interface_name(const char *interf); -char *Curl_if2ip(int af, const char *interf, char *buf, int buf_size); + +typedef enum { + IF2IP_NOT_FOUND = 0, /* Interface not found */ + IF2IP_AF_NOT_SUPPORTED = 1, /* Int. exists but has no address for this af */ + IF2IP_FOUND = 2 /* The address has been stored in "buf" */ +} if2ip_result_t; + +if2ip_result_t Curl_if2ip(int af, unsigned int remote_scope, + const char *interf, char *buf, int buf_size); #ifdef __INTERIX diff --git a/plugins/FTPFileYM/curl/lib/imap.c b/plugins/FTPFileYM/curl/lib/imap.c index 72ec871f92..9a845102ad 100644 --- a/plugins/FTPFileYM/curl/lib/imap.c +++ b/plugins/FTPFileYM/curl/lib/imap.c @@ -24,7 +24,9 @@ * RFC3501 IMAPv4 protocol * RFC4422 Simple Authentication and Security Layer (SASL) * RFC4616 PLAIN authentication + * RFC4959 IMAP Extension for SASL Initial Client Response * RFC5092 IMAP URL Scheme + * RFC6749 OAuth 2.0 Authorization Framework * ***************************************************************************/ @@ -76,6 +78,7 @@ #include "url.h" #include "rawstr.h" #include "curl_sasl.h" +#include "warnless.h" #define _MPRINTF_REPLACE /* use our functions only */ #include <curl/mprintf.h> @@ -85,7 +88,6 @@ #include "memdebug.h" /* Local API functions */ -static CURLcode imap_parse_url_path(struct connectdata *conn); static CURLcode imap_regular_transfer(struct connectdata *conn, bool *done); static CURLcode imap_do(struct connectdata *conn, bool *done); static CURLcode imap_done(struct connectdata *conn, CURLcode status, @@ -97,7 +99,11 @@ static int imap_getsock(struct connectdata *conn, curl_socket_t *socks, int numsocks); static CURLcode imap_doing(struct connectdata *conn, bool *dophase_done); static CURLcode imap_setup_connection(struct connectdata *conn); -static CURLcode imap_state_upgrade_tls(struct connectdata *conn); +static char *imap_atom(const char *str); +static CURLcode imap_sendf(struct connectdata *conn, const char *fmt, ...); +static CURLcode imap_parse_url_options(struct connectdata *conn); +static CURLcode imap_parse_url_path(struct connectdata *conn); +static CURLcode imap_parse_custom_request(struct connectdata *conn); /* * IMAP protocol handler. @@ -158,7 +164,7 @@ const struct Curl_handler Curl_handler_imaps = { static const struct Curl_handler Curl_handler_imap_proxy = { "IMAP", /* scheme */ - ZERO_NULL, /* setup_connection */ + Curl_http_setup_conn, /* setup_connection */ Curl_http, /* do_it */ Curl_http_done, /* done */ ZERO_NULL, /* do_more */ @@ -183,7 +189,7 @@ static const struct Curl_handler Curl_handler_imap_proxy = { static const struct Curl_handler Curl_handler_imaps_proxy = { "IMAPS", /* scheme */ - ZERO_NULL, /* setup_connection */ + Curl_http_setup_conn, /* setup_connection */ Curl_http, /* do_it */ Curl_http_done, /* done */ ZERO_NULL, /* do_more */ @@ -203,227 +209,173 @@ static const struct Curl_handler Curl_handler_imaps_proxy = { #endif #endif +#ifdef USE_SSL +static void imap_to_imaps(struct connectdata *conn) +{ + conn->handler = &Curl_handler_imaps; +} +#else +#define imap_to_imaps(x) Curl_nop_stmt +#endif + /*********************************************************************** * - * imap_sendf() + * imap_matchresp() * - * Sends the formated string as an IMAP command to the server. + * Determines whether the untagged response is related to the specified + * command by checking if it is in format "* <command-name> ..." or + * "* <number> <command-name> ...". * - * Designed to never block. + * The "* " marker is assumed to have already been checked by the caller. */ -static CURLcode imap_sendf(struct connectdata *conn, - const char *idstr, /* command id to wait for */ - const char *fmt, ...) +static bool imap_matchresp(const char *line, size_t len, const char *cmd) { - CURLcode res; - struct imap_conn *imapc = &conn->proto.imapc; - va_list ap; - va_start(ap, fmt); - - imapc->idstr = idstr; + const char *end = line + len; + size_t cmd_len = strlen(cmd); - res = Curl_pp_vsendf(&imapc->pp, fmt, ap); + /* Skip the untagged response marker */ + line += 2; - va_end(ap); - - return res; -} + /* Do we have a number after the marker? */ + if(line < end && ISDIGIT(*line)) { + /* Skip the number */ + do + line++; + while(line < end && ISDIGIT(*line)); -static const char *getcmdid(struct connectdata *conn) -{ - static const char * const ids[]= { - "A", - "B", - "C", - "D" - }; + /* Do we have the space character? */ + if(line == end || *line != ' ') + return FALSE; - struct imap_conn *imapc = &conn->proto.imapc; + line++; + } - /* Get the next id, but wrap at end of table */ - imapc->cmdid = (int)((imapc->cmdid + 1) % (sizeof(ids) / sizeof(ids[0]))); + /* Does the command name match and is it followed by a space character or at + the end of line? */ + if(line + cmd_len <= end && Curl_raw_nequal(line, cmd, cmd_len) && + (line[cmd_len] == ' ' || line + cmd_len == end)) + return TRUE; - return ids[imapc->cmdid]; + return FALSE; } /*********************************************************************** * - * imap_atom() - * - * Checks the input string for characters that need escaping and returns an - * atom ready for sending to the server. - * - * The returned string needs to be freed. + * imap_endofresp() * + * Checks whether the given string is a valid tagged, untagged or continuation + * response which can be processed by the response handler. */ -static char* imap_atom(const char* str) +static bool imap_endofresp(struct connectdata *conn, char *line, size_t len, + int *resp) { - const char *p1; - char *p2; - size_t backsp_count = 0; - size_t quote_count = 0; - bool space_exists = FALSE; - size_t newlen = 0; - char *newstr = NULL; - - if(!str) - return NULL; + struct IMAP *imap = conn->data->req.protop; + struct imap_conn *imapc = &conn->proto.imapc; + const char *id = imapc->resptag; + size_t id_len = strlen(id); - /* Count any unescapped characters */ - p1 = str; - while(*p1) { - if(*p1 == '\\') - backsp_count++; - else if(*p1 == '"') - quote_count++; - else if(*p1 == ' ') - space_exists = TRUE; + /* Do we have a tagged command response? */ + if(len >= id_len + 1 && !memcmp(id, line, id_len) && line[id_len] == ' ') { + line += id_len + 1; + len -= id_len + 1; + + if(len >= 2 && !memcmp(line, "OK", 2)) + *resp = 'O'; + else if(len >= 2 && !memcmp(line, "NO", 2)) + *resp = 'N'; + else if(len >= 3 && !memcmp(line, "BAD", 3)) + *resp = 'B'; + else { + failf(conn->data, "Bad tagged response"); + *resp = -1; + } - p1++; + return TRUE; } - /* Does the input contain any unescapped characters? */ - if(!backsp_count && !quote_count && !space_exists) - return strdup(str); + /* Do we have an untagged command response? */ + if(len >= 2 && !memcmp("* ", line, 2)) { + switch(imapc->state) { + /* States which are interested in untagged responses */ + case IMAP_CAPABILITY: + if(!imap_matchresp(line, len, "CAPABILITY")) + return FALSE; + break; - /* Calculate the new string length */ - newlen = strlen(str) + backsp_count + quote_count + (space_exists ? 2 : 0); + case IMAP_LIST: + if((!imap->custom && !imap_matchresp(line, len, "LIST")) || + (imap->custom && !imap_matchresp(line, len, imap->custom) && + (strcmp(imap->custom, "STORE") || + !imap_matchresp(line, len, "FETCH")) && + strcmp(imap->custom, "SELECT") && + strcmp(imap->custom, "EXAMINE") && + strcmp(imap->custom, "SEARCH") && + strcmp(imap->custom, "EXPUNGE") && + strcmp(imap->custom, "LSUB") && + strcmp(imap->custom, "UID") && + strcmp(imap->custom, "NOOP"))) + return FALSE; + break; - /* Allocate the new string */ - newstr = (char *) malloc((newlen + 1) * sizeof(char)); - if(!newstr) - return NULL; + case IMAP_SELECT: + /* SELECT is special in that its untagged responses do not have a + common prefix so accept anything! */ + break; - /* Surround the string in quotes if necessary */ - p2 = newstr; - if(space_exists) { - newstr[0] = '"'; - newstr[newlen - 1] = '"'; - p2++; - } + case IMAP_FETCH: + if(!imap_matchresp(line, len, "FETCH")) + return FALSE; + break; - /* Copy the string, escaping backslash and quote characters along the way */ - p1 = str; - while(*p1) { - if(*p1 == '\\' || *p1 == '"') { - *p2 = '\\'; - p2++; + /* Ignore other untagged responses */ + default: + return FALSE; } - *p2 = *p1; - - p1++; - p2++; - } - - /* Terminate the string */ - newstr[newlen] = '\0'; - - return newstr; -} - -/* Function that checks for an ending imap status code at the start of the - given string but also detects the supported authentication mechanisms from - the CAPABILITY response. */ -static int imap_endofresp(struct pingpong *pp, int *resp) -{ - char *line = pp->linestart_resp; - size_t len = pp->nread_resp; - struct imap_conn *imapc = &pp->conn->proto.imapc; - const char *id = imapc->idstr; - size_t id_len = strlen(id); - size_t wordlen; - - /* Do we have a generic command response? */ - if(len >= id_len + 3) { - if(!memcmp(id, line, id_len) && line[id_len] == ' ') { - *resp = line[id_len + 1]; /* O, N or B */ - return TRUE; - } + *resp = '*'; + return TRUE; } - /* Do we have a generic continuation response? */ + /* Do we have a continuation response? This should be a + symbol followed by + a space and optionally some text as per RFC-3501 for the AUTHENTICATE and + APPEND commands and as outlined in Section 4. Examples of RFC-4959 but + some e-mail servers ignore this and only send a single + instead. */ if((len == 3 && !memcmp("+", line, 1)) || (len >= 2 && !memcmp("+ ", line, 2))) { - *resp = '+'; - return TRUE; - } + switch(imapc->state) { + /* States which are interested in continuation responses */ + case IMAP_AUTHENTICATE_PLAIN: + case IMAP_AUTHENTICATE_LOGIN: + case IMAP_AUTHENTICATE_LOGIN_PASSWD: + case IMAP_AUTHENTICATE_CRAMMD5: + case IMAP_AUTHENTICATE_DIGESTMD5: + case IMAP_AUTHENTICATE_DIGESTMD5_RESP: + case IMAP_AUTHENTICATE_NTLM: + case IMAP_AUTHENTICATE_NTLM_TYPE2MSG: + case IMAP_AUTHENTICATE_XOAUTH2: + case IMAP_AUTHENTICATE_FINAL: + case IMAP_APPEND: + *resp = '+'; + break; - /* Are we processing CAPABILITY command responses? */ - if(imapc->state == IMAP_CAPABILITY) { - /* Do we have a valid response? */ - if(len >= 2 && !memcmp("* ", line, 2)) { - line += 2; - len -= 2; - - /* Loop through the data line */ - for(;;) { - while(len && - (*line == ' ' || *line == '\t' || - *line == '\r' || *line == '\n')) { - - if(*line == '\n') - return FALSE; - - line++; - len--; - } - - if(!len) - break; - - /* Extract the word */ - for(wordlen = 0; wordlen < len && line[wordlen] != ' ' && - line[wordlen] != '\t' && line[wordlen] != '\r' && - line[wordlen] != '\n';) - wordlen++; - - /* Has the server explicitly disabled clear text authentication? */ - if(wordlen == 13 && !memcmp(line, "LOGINDISABLED", 13)) - imapc->login_disabled = TRUE; - - /* Do we have a SASL based authentication mechanism? */ - else if(wordlen > 5 && !memcmp(line, "AUTH=", 5)) { - line += 5; - len -= 5; - wordlen -= 5; - - /* Test the word for a matching authentication mechanism */ - if(wordlen == 5 && !memcmp(line, "LOGIN", 5)) - imapc->authmechs |= SASL_MECH_LOGIN; - if(wordlen == 5 && !memcmp(line, "PLAIN", 5)) - imapc->authmechs |= SASL_MECH_PLAIN; - else if(wordlen == 8 && !memcmp(line, "CRAM-MD5", 8)) - imapc->authmechs |= SASL_MECH_CRAM_MD5; - else if(wordlen == 10 && !memcmp(line, "DIGEST-MD5", 10)) - imapc->authmechs |= SASL_MECH_DIGEST_MD5; - else if(wordlen == 6 && !memcmp(line, "GSSAPI", 6)) - imapc->authmechs |= SASL_MECH_GSSAPI; - else if(wordlen == 8 && !memcmp(line, "EXTERNAL", 8)) - imapc->authmechs |= SASL_MECH_EXTERNAL; - else if(wordlen == 4 && !memcmp(line, "NTLM", 4)) - imapc->authmechs |= SASL_MECH_NTLM; - } - - line += wordlen; - len -= wordlen; - } + default: + failf(conn->data, "Unexpected continuation response"); + *resp = -1; + break; } - } - /* Are we processing FETCH command responses? */ - if(imapc->state == IMAP_FETCH) { - /* Do we have a valid response? */ - if(len >= 2 && !memcmp("* ", line, 2)) { - *resp = '*'; - return TRUE; - } + return TRUE; } return FALSE; /* Nothing for us */ } -/* This is the ONLY way to change IMAP state! */ +/*********************************************************************** + * + * state() + * + * This is the ONLY way to change IMAP state! + */ static void state(struct connectdata *conn, imapstate newstate) { struct imap_conn *imapc = &conn->proto.imapc; @@ -432,9 +384,9 @@ static void state(struct connectdata *conn, imapstate newstate) static const char * const names[]={ "STOP", "SERVERGREET", + "CAPABILITY", "STARTTLS", "UPGRADETLS", - "CAPABILITY", "AUTHENTICATE_PLAIN", "AUTHENTICATE_LOGIN", "AUTHENTICATE_LOGIN_PASSWD", @@ -443,152 +395,425 @@ static void state(struct connectdata *conn, imapstate newstate) "AUTHENTICATE_DIGESTMD5_RESP", "AUTHENTICATE_NTLM", "AUTHENTICATE_NTLM_TYPE2MSG", - "AUTHENTICATE", + "AUTHENTICATE_XOAUTH2", + "AUTHENTICATE_FINAL", "LOGIN", + "LIST", "SELECT", "FETCH", + "FETCH_FINAL", + "APPEND", + "APPEND_FINAL", "LOGOUT", /* LAST */ }; if(imapc->state != newstate) infof(conn->data, "IMAP %p state change from %s to %s\n", - imapc, names[imapc->state], names[newstate]); + (void *)imapc, names[imapc->state], names[newstate]); #endif imapc->state = newstate; } -static CURLcode imap_state_capability(struct connectdata *conn) +/*********************************************************************** + * + * imap_perform_capability() + * + * Sends the CAPABILITY command in order to obtain a list of server side + * supported capabilities. + */ +static CURLcode imap_perform_capability(struct connectdata *conn) { CURLcode result = CURLE_OK; struct imap_conn *imapc = &conn->proto.imapc; - const char *str; imapc->authmechs = 0; /* No known authentication mechanisms yet */ imapc->authused = 0; /* Clear the authentication mechanism used */ + imapc->tls_supported = FALSE; /* Clear the TLS capability */ - /* Check we have a username and password to authenticate with and end the - connect phase if we don't */ - if(!conn->bits.user_passwd) { - state(conn, IMAP_STOP); + /* Send the CAPABILITY command */ + result = imap_sendf(conn, "CAPABILITY"); - return result; - } + if(!result) + state(conn, IMAP_CAPABILITY); - str = getcmdid(conn); + return result; +} - /* Send the CAPABILITY command */ - result = imap_sendf(conn, str, "%s CAPABILITY", str); +/*********************************************************************** + * + * imap_perform_starttls() + * + * Sends the STARTTLS command to start the upgrade to TLS. + */ +static CURLcode imap_perform_starttls(struct connectdata *conn) +{ + CURLcode result = CURLE_OK; - if(result) - return result; + /* Send the STARTTLS command */ + result = imap_sendf(conn, "STARTTLS"); - state(conn, IMAP_CAPABILITY); + if(!result) + state(conn, IMAP_STARTTLS); - return CURLE_OK; + return result; +} + +/*********************************************************************** + * + * imap_perform_upgrade_tls() + * + * Performs the upgrade to TLS. + */ +static CURLcode imap_perform_upgrade_tls(struct connectdata *conn) +{ + CURLcode result = CURLE_OK; + struct imap_conn *imapc = &conn->proto.imapc; + + /* Start the SSL connection */ + result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, &imapc->ssldone); + + if(!result) { + if(imapc->state != IMAP_UPGRADETLS) + state(conn, IMAP_UPGRADETLS); + + if(imapc->ssldone) { + imap_to_imaps(conn); + result = imap_perform_capability(conn); + } + } + + return result; } -static CURLcode imap_state_login(struct connectdata *conn) +/*********************************************************************** + * + * imap_perform_login() + * + * Sends a clear text LOGIN command to authenticate with. + */ +static CURLcode imap_perform_login(struct connectdata *conn) { - CURLcode result; - struct FTP *imap = conn->data->state.proto.imap; - const char *str = getcmdid(conn); - char *user = imap_atom(imap->user); - char *passwd = imap_atom(imap->passwd); + CURLcode result = CURLE_OK; + char *user; + char *passwd; - /* send USER and password */ - result = imap_sendf(conn, str, "%s LOGIN %s %s", str, - user ? user : "", passwd ? passwd : ""); + /* Check we have a username and password to authenticate with and end the + connect phase if we don't */ + if(!conn->bits.user_passwd) { + state(conn, IMAP_STOP); + + return result; + } + + /* Make sure the username and password are in the correct atom format */ + user = imap_atom(conn->user); + passwd = imap_atom(conn->passwd); + + /* Send the LOGIN command */ + result = imap_sendf(conn, "LOGIN %s %s", user ? user : "", + passwd ? passwd : ""); Curl_safefree(user); Curl_safefree(passwd); - if(result) - return result; - - state(conn, IMAP_LOGIN); + if(!result) + state(conn, IMAP_LOGIN); - return CURLE_OK; + return result; } -static CURLcode imap_authenticate(struct connectdata *conn) +/*********************************************************************** + * + * imap_perform_authenticate() + * + * Sends an AUTHENTICATE command allowing the client to login with the + * appropriate SASL authentication mechanism. + * + * Additionally, the function will perform fallback to the LOGIN command + * should a common mechanism not be available between the client and server. + */ +static CURLcode imap_perform_authenticate(struct connectdata *conn) { CURLcode result = CURLE_OK; + struct SessionHandle *data = conn->data; struct imap_conn *imapc = &conn->proto.imapc; const char *mech = NULL; - imapstate authstate = IMAP_STOP; + char *initresp = NULL; + size_t len = 0; + imapstate state1 = IMAP_STOP; + imapstate state2 = IMAP_STOP; + + /* Check we have a username and password to authenticate with and end the + connect phase if we don't */ + if(!conn->bits.user_passwd) { + state(conn, IMAP_STOP); + + return result; + } /* Calculate the supported authentication mechanism by decreasing order of security */ #ifndef CURL_DISABLE_CRYPTO_AUTH - if(imapc->authmechs & SASL_MECH_DIGEST_MD5) { - mech = "DIGEST-MD5"; - authstate = IMAP_AUTHENTICATE_DIGESTMD5; + if((imapc->authmechs & SASL_MECH_DIGEST_MD5) && + (imapc->prefmech & SASL_MECH_DIGEST_MD5)) { + mech = SASL_MECH_STRING_DIGEST_MD5; + state1 = IMAP_AUTHENTICATE_DIGESTMD5; imapc->authused = SASL_MECH_DIGEST_MD5; } - else if(imapc->authmechs & SASL_MECH_CRAM_MD5) { - mech = "CRAM-MD5"; - authstate = IMAP_AUTHENTICATE_CRAMMD5; + else if((imapc->authmechs & SASL_MECH_CRAM_MD5) && + (imapc->prefmech & SASL_MECH_CRAM_MD5)) { + mech = SASL_MECH_STRING_CRAM_MD5; + state1 = IMAP_AUTHENTICATE_CRAMMD5; imapc->authused = SASL_MECH_CRAM_MD5; } else #endif #ifdef USE_NTLM - if(imapc->authmechs & SASL_MECH_NTLM) { - mech = "NTLM"; - authstate = IMAP_AUTHENTICATE_NTLM; + if((imapc->authmechs & SASL_MECH_NTLM) && + (imapc->prefmech & SASL_MECH_NTLM)) { + mech = SASL_MECH_STRING_NTLM; + state1 = IMAP_AUTHENTICATE_NTLM; + state2 = IMAP_AUTHENTICATE_NTLM_TYPE2MSG; imapc->authused = SASL_MECH_NTLM; + + if(imapc->ir_supported || data->set.sasl_ir) + result = Curl_sasl_create_ntlm_type1_message(conn->user, conn->passwd, + &conn->ntlm, + &initresp, &len); } else #endif - if(imapc->authmechs & SASL_MECH_LOGIN) { - mech = "LOGIN"; - authstate = IMAP_AUTHENTICATE_LOGIN; + if(((imapc->authmechs & SASL_MECH_XOAUTH2) && + (imapc->prefmech & SASL_MECH_XOAUTH2) && + (imapc->prefmech != SASL_AUTH_ANY)) || conn->xoauth2_bearer) { + mech = SASL_MECH_STRING_XOAUTH2; + state1 = IMAP_AUTHENTICATE_XOAUTH2; + state2 = IMAP_AUTHENTICATE_FINAL; + imapc->authused = SASL_MECH_XOAUTH2; + + if(imapc->ir_supported || data->set.sasl_ir) + result = Curl_sasl_create_xoauth2_message(conn->data, conn->user, + conn->xoauth2_bearer, + &initresp, &len); + } + else if((imapc->authmechs & SASL_MECH_LOGIN) && + (imapc->prefmech & SASL_MECH_LOGIN)) { + mech = SASL_MECH_STRING_LOGIN; + state1 = IMAP_AUTHENTICATE_LOGIN; + state2 = IMAP_AUTHENTICATE_LOGIN_PASSWD; imapc->authused = SASL_MECH_LOGIN; + + if(imapc->ir_supported || data->set.sasl_ir) + result = Curl_sasl_create_login_message(conn->data, conn->user, + &initresp, &len); } - else if(imapc->authmechs & SASL_MECH_PLAIN) { - mech = "PLAIN"; - authstate = IMAP_AUTHENTICATE_PLAIN; + else if((imapc->authmechs & SASL_MECH_PLAIN) && + (imapc->prefmech & SASL_MECH_PLAIN)) { + mech = SASL_MECH_STRING_PLAIN; + state1 = IMAP_AUTHENTICATE_PLAIN; + state2 = IMAP_AUTHENTICATE_FINAL; imapc->authused = SASL_MECH_PLAIN; + + if(imapc->ir_supported || data->set.sasl_ir) + result = Curl_sasl_create_plain_message(conn->data, conn->user, + conn->passwd, &initresp, &len); } - if(mech) { - /* Perform SASL based authentication */ - const char *str = getcmdid(conn); + if(!result) { + if(mech) { + /* Perform SASL based authentication */ + if(initresp) { + result = imap_sendf(conn, "AUTHENTICATE %s %s", mech, initresp); + + if(!result) + state(conn, state2); + } + else { + result = imap_sendf(conn, "AUTHENTICATE %s", mech); - result = imap_sendf(conn, str, "%s AUTHENTICATE %s", str, mech); + if(!result) + state(conn, state1); + } - if(!result) - state(conn, authstate); + Curl_safefree(initresp); + } + else if(!imapc->login_disabled) + /* Perform clear text authentication */ + result = imap_perform_login(conn); + else { + /* Other mechanisms not supported */ + infof(conn->data, "No known authentication mechanisms supported!\n"); + result = CURLE_LOGIN_DENIED; + } } - else if(!imapc->login_disabled) - /* Perform clear text authentication */ - result = imap_state_login(conn); + + return result; +} + +/*********************************************************************** + * + * imap_perform_list() + * + * Sends a LIST command or an alternative custom request. + */ +static CURLcode imap_perform_list(struct connectdata *conn) +{ + CURLcode result = CURLE_OK; + struct SessionHandle *data = conn->data; + struct IMAP *imap = data->req.protop; + char *mailbox; + + if(imap->custom) + /* Send the custom request */ + result = imap_sendf(conn, "%s%s", imap->custom, + imap->custom_params ? imap->custom_params : ""); else { - /* Other mechanisms not supported */ - infof(conn->data, "No known authentication mechanisms supported!\n"); - result = CURLE_LOGIN_DENIED; + /* Make sure the mailbox is in the correct atom format */ + mailbox = imap_atom(imap->mailbox ? imap->mailbox : ""); + if(!mailbox) + return CURLE_OUT_OF_MEMORY; + + /* Send the LIST command */ + result = imap_sendf(conn, "LIST \"%s\" *", mailbox); + + Curl_safefree(mailbox); } + if(!result) + state(conn, IMAP_LIST); + return result; } -/* For the IMAP "protocol connect" and "doing" phases only */ -static int imap_getsock(struct connectdata *conn, curl_socket_t *socks, - int numsocks) +/*********************************************************************** + * + * imap_perform_select() + * + * Sends a SELECT command to ask the server to change the selected mailbox. + */ +static CURLcode imap_perform_select(struct connectdata *conn) { - return Curl_pp_getsock(&conn->proto.imapc.pp, socks, numsocks); + CURLcode result = CURLE_OK; + struct SessionHandle *data = conn->data; + struct IMAP *imap = data->req.protop; + struct imap_conn *imapc = &conn->proto.imapc; + char *mailbox; + + /* Invalidate old information as we are switching mailboxes */ + Curl_safefree(imapc->mailbox); + Curl_safefree(imapc->mailbox_uidvalidity); + + /* Check we have a mailbox */ + if(!imap->mailbox) { + failf(conn->data, "Cannot SELECT without a mailbox."); + return CURLE_URL_MALFORMAT; + } + + /* Make sure the mailbox is in the correct atom format */ + mailbox = imap_atom(imap->mailbox); + if(!mailbox) + return CURLE_OUT_OF_MEMORY; + + /* Send the SELECT command */ + result = imap_sendf(conn, "SELECT %s", mailbox); + + Curl_safefree(mailbox); + + if(!result) + state(conn, IMAP_SELECT); + + return result; } -#ifdef USE_SSL -static void imap_to_imaps(struct connectdata *conn) +/*********************************************************************** + * + * imap_perform_fetch() + * + * Sends a FETCH command to initiate the download of a message. + */ +static CURLcode imap_perform_fetch(struct connectdata *conn) { - conn->handler = &Curl_handler_imaps; + CURLcode result = CURLE_OK; + struct IMAP *imap = conn->data->req.protop; + + /* Check we have a UID */ + if(!imap->uid) { + failf(conn->data, "Cannot FETCH without a UID."); + return CURLE_URL_MALFORMAT; + } + + /* Send the FETCH command */ + result = imap_sendf(conn, "FETCH %s BODY[%s]", + imap->uid, + imap->section ? imap->section : ""); + + if(!result) + state(conn, IMAP_FETCH); + + return result; +} + +/*********************************************************************** + * + * imap_perform_append() + * + * Sends an APPEND command to initiate the upload of a message. + */ +static CURLcode imap_perform_append(struct connectdata *conn) +{ + CURLcode result = CURLE_OK; + struct IMAP *imap = conn->data->req.protop; + char *mailbox; + + /* Check we have a mailbox */ + if(!imap->mailbox) { + failf(conn->data, "Cannot APPEND without a mailbox."); + return CURLE_URL_MALFORMAT; + } + + /* Check we know the size of the upload */ + if(conn->data->set.infilesize < 0) { + failf(conn->data, "Cannot APPEND with unknown input file size\n"); + return CURLE_UPLOAD_FAILED; + } + + /* Make sure the mailbox is in the correct atom format */ + mailbox = imap_atom(imap->mailbox); + if(!mailbox) + return CURLE_OUT_OF_MEMORY; + + /* Send the APPEND command */ + result = imap_sendf(conn, "APPEND %s (\\Seen) {%" FORMAT_OFF_T "}", + mailbox, conn->data->set.infilesize); + + Curl_safefree(mailbox); + + if(!result) + state(conn, IMAP_APPEND); + + return result; +} + +/*********************************************************************** + * + * imap_perform_logout() + * + * Performs the logout action prior to sclose() being called. + */ +static CURLcode imap_perform_logout(struct connectdata *conn) +{ + CURLcode result = CURLE_OK; + + /* Send the LOGOUT command */ + result = imap_sendf(conn, "LOGOUT"); + + if(!result) + state(conn, IMAP_LOGOUT); + + return result; } -#else -#define imap_to_imaps(x) Curl_nop_stmt -#endif /* For the initial server greeting */ static CURLcode imap_state_servergreet_resp(struct connectdata *conn, @@ -602,85 +827,136 @@ static CURLcode imap_state_servergreet_resp(struct connectdata *conn, if(imapcode != 'O') { failf(data, "Got unexpected imap-server response"); - return CURLE_FTP_WEIRD_SERVER_REPLY; - } - - if(data->set.use_ssl && !conn->ssl[FIRSTSOCKET].use) { - /* We don't have a SSL/TLS connection yet, but SSL is requested. Switch - to TLS connection now */ - const char *str = getcmdid(conn); - result = imap_sendf(conn, str, "%s STARTTLS", str); - if(!result) - state(conn, IMAP_STARTTLS); + result = CURLE_FTP_WEIRD_SERVER_REPLY; /* TODO: fix this code */ } else - result = imap_state_capability(conn); + result = imap_perform_capability(conn); return result; } -/* For STARTTLS responses */ -static CURLcode imap_state_starttls_resp(struct connectdata *conn, - int imapcode, - imapstate instate) +/* For CAPABILITY responses */ +static CURLcode imap_state_capability_resp(struct connectdata *conn, + int imapcode, + imapstate instate) { CURLcode result = CURLE_OK; struct SessionHandle *data = conn->data; + struct imap_conn *imapc = &conn->proto.imapc; + const char *line = data->state.buffer; + size_t wordlen; (void)instate; /* no use for this yet */ - if(imapcode != 'O') { - if(data->set.use_ssl != CURLUSESSL_TRY) { - failf(data, "STARTTLS denied. %c", imapcode); - result = CURLE_USE_SSL_FAILED; - } - else - result = imap_state_capability(conn); - } - else - result = imap_state_upgrade_tls(conn); + /* Do we have a untagged response? */ + if(imapcode == '*') { + line += 2; - return result; -} + /* Loop through the data line */ + for(;;) { + while(*line && + (*line == ' ' || *line == '\t' || + *line == '\r' || *line == '\n')) { -static CURLcode imap_state_upgrade_tls(struct connectdata *conn) -{ - struct imap_conn *imapc = &conn->proto.imapc; - CURLcode result; + line++; + } - result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, &imapc->ssldone); + if(!*line) + break; - if(!result) { - if(imapc->state != IMAP_UPGRADETLS) - state(conn, IMAP_UPGRADETLS); + /* Extract the word */ + for(wordlen = 0; line[wordlen] && line[wordlen] != ' ' && + line[wordlen] != '\t' && line[wordlen] != '\r' && + line[wordlen] != '\n';) + wordlen++; + + /* Does the server support the STARTTLS capability? */ + if(wordlen == 8 && !memcmp(line, "STARTTLS", 8)) + imapc->tls_supported = TRUE; + + /* Has the server explicitly disabled clear text authentication? */ + else if(wordlen == 13 && !memcmp(line, "LOGINDISABLED", 13)) + imapc->login_disabled = TRUE; + + /* Does the server support the SASL-IR capability? */ + else if(wordlen == 7 && !memcmp(line, "SASL-IR", 7)) + imapc->ir_supported = TRUE; + + /* Do we have a SASL based authentication mechanism? */ + else if(wordlen > 5 && !memcmp(line, "AUTH=", 5)) { + line += 5; + wordlen -= 5; + + /* Test the word for a matching authentication mechanism */ + if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_LOGIN)) + imapc->authmechs |= SASL_MECH_LOGIN; + else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_PLAIN)) + imapc->authmechs |= SASL_MECH_PLAIN; + else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_CRAM_MD5)) + imapc->authmechs |= SASL_MECH_CRAM_MD5; + else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_DIGEST_MD5)) + imapc->authmechs |= SASL_MECH_DIGEST_MD5; + else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_GSSAPI)) + imapc->authmechs |= SASL_MECH_GSSAPI; + else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_EXTERNAL)) + imapc->authmechs |= SASL_MECH_EXTERNAL; + else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_NTLM)) + imapc->authmechs |= SASL_MECH_NTLM; + else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_XOAUTH2)) + imapc->authmechs |= SASL_MECH_XOAUTH2; + } - if(imapc->ssldone) { - imap_to_imaps(conn); - result = imap_state_capability(conn); + line += wordlen; } } + else if(imapcode == 'O') { + if(data->set.use_ssl && !conn->ssl[FIRSTSOCKET].use) { + /* We don't have a SSL/TLS connection yet, but SSL is requested */ + if(imapc->tls_supported) + /* Switch to TLS connection now */ + result = imap_perform_starttls(conn); + else if(data->set.use_ssl == CURLUSESSL_TRY) + /* Fallback and carry on with authentication */ + result = imap_perform_authenticate(conn); + else { + failf(data, "STARTTLS not supported."); + result = CURLE_USE_SSL_FAILED; + } + } + else + result = imap_perform_authenticate(conn); + } + else + result = imap_perform_login(conn); return result; } -/* For CAPABILITY responses */ -static CURLcode imap_state_capability_resp(struct connectdata *conn, - int imapcode, - imapstate instate) +/* For STARTTLS responses */ +static CURLcode imap_state_starttls_resp(struct connectdata *conn, + int imapcode, + imapstate instate) { CURLcode result = CURLE_OK; + struct SessionHandle *data = conn->data; (void)instate; /* no use for this yet */ - if(imapcode == 'O') - result = imap_authenticate(conn); + if(imapcode != 'O') { + if(data->set.use_ssl != CURLUSESSL_TRY) { + failf(data, "STARTTLS denied. %c", imapcode); + result = CURLE_USE_SSL_FAILED; + } + else + result = imap_perform_authenticate(conn); + } else - result = imap_state_login(conn); + result = imap_perform_upgrade_tls(conn); return result; } -/* For AUTHENTICATE PLAIN responses */ +/* For AUTHENTICATE PLAIN (without initial response) responses */ static CURLcode imap_state_auth_plain_resp(struct connectdata *conn, int imapcode, imapstate instate) @@ -707,7 +983,7 @@ static CURLcode imap_state_auth_plain_resp(struct connectdata *conn, result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", plainauth); if(!result) - state(conn, IMAP_AUTHENTICATE); + state(conn, IMAP_AUTHENTICATE_FINAL); } Curl_safefree(plainauth); @@ -717,7 +993,7 @@ static CURLcode imap_state_auth_plain_resp(struct connectdata *conn, return result; } -/* For AUTHENTICATE LOGIN responses */ +/* For AUTHENTICATE LOGIN (without initial response) responses */ static CURLcode imap_state_auth_login_resp(struct connectdata *conn, int imapcode, imapstate instate) @@ -781,7 +1057,7 @@ static CURLcode imap_state_auth_login_password_resp(struct connectdata *conn, result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", authpasswd); if(!result) - state(conn, IMAP_AUTHENTICATE); + state(conn, IMAP_AUTHENTICATE_FINAL); } Curl_safefree(authpasswd); @@ -836,7 +1112,7 @@ static CURLcode imap_state_auth_cram_resp(struct connectdata *conn, result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", rplyb64); if(!result) - state(conn, IMAP_AUTHENTICATE); + state(conn, IMAP_AUTHENTICATE_FINAL); } Curl_safefree(rplyb64); @@ -903,10 +1179,10 @@ static CURLcode imap_state_auth_digest_resp_resp(struct connectdata *conn, } else { /* Send an empty response */ - result = Curl_pp_sendf(&conn->proto.imapc.pp, ""); + result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", ""); if(!result) - state(conn, IMAP_AUTHENTICATE); + state(conn, IMAP_AUTHENTICATE_FINAL); } return result; @@ -914,7 +1190,7 @@ static CURLcode imap_state_auth_digest_resp_resp(struct connectdata *conn, #endif #ifdef USE_NTLM -/* For AUTHENTICATE NTLM responses */ +/* For AUTHENTICATE NTLM (without initial response) responses */ static CURLcode imap_state_auth_ntlm_resp(struct connectdata *conn, int imapcode, imapstate instate) @@ -982,7 +1258,7 @@ static CURLcode imap_state_auth_ntlm_type2msg_resp(struct connectdata *conn, result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", type3msg); if(!result) - state(conn, IMAP_AUTHENTICATE); + state(conn, IMAP_AUTHENTICATE_FINAL); } Curl_safefree(type3msg); @@ -993,6 +1269,44 @@ static CURLcode imap_state_auth_ntlm_type2msg_resp(struct connectdata *conn, } #endif +/* For AUTH XOAUTH2 (without initial response) responses */ +static CURLcode imap_state_auth_xoauth2_resp(struct connectdata *conn, + int imapcode, + imapstate instate) +{ + CURLcode result = CURLE_OK; + struct SessionHandle *data = conn->data; + size_t len = 0; + char *xoauth = NULL; + + (void)instate; /* no use for this yet */ + + if(imapcode != '+') { + failf(data, "Access denied: %d", imapcode); + result = CURLE_LOGIN_DENIED; + } + else { + /* Create the authorisation message */ + result = Curl_sasl_create_xoauth2_message(conn->data, conn->user, + conn->xoauth2_bearer, + &xoauth, &len); + + /* Send the message */ + if(!result) { + if(xoauth) { + result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", xoauth); + + if(!result) + state(conn, IMAP_AUTHENTICATE_FINAL); + } + + Curl_safefree(xoauth); + } + } + + return result; +} + /* For final responses to the AUTHENTICATE sequence */ static CURLcode imap_state_auth_final_resp(struct connectdata *conn, int imapcode, @@ -1007,9 +1321,9 @@ static CURLcode imap_state_auth_final_resp(struct connectdata *conn, failf(data, "Authentication failed: %d", imapcode); result = CURLE_LOGIN_DENIED; } - - /* End of connect phase */ - state(conn, IMAP_STOP); + else + /* End of connect phase */ + state(conn, IMAP_STOP); return result; } @@ -1035,97 +1349,112 @@ static CURLcode imap_state_login_resp(struct connectdata *conn, return result; } -/* Start the DO phase */ -static CURLcode imap_select(struct connectdata *conn) -{ - CURLcode result = CURLE_OK; - struct imap_conn *imapc = &conn->proto.imapc; - const char *str = getcmdid(conn); - - result = imap_sendf(conn, str, "%s SELECT %s", str, - imapc->mailbox?imapc->mailbox:""); - if(result) - return result; - - state(conn, IMAP_SELECT); - - return result; -} - -static CURLcode imap_fetch(struct connectdata *conn) +/* For LIST responses */ +static CURLcode imap_state_list_resp(struct connectdata *conn, int imapcode, + imapstate instate) { CURLcode result = CURLE_OK; - const char *str = getcmdid(conn); - - /* TODO: make this select the correct mail - * Use "1 body[text]" to get the full mail body of mail 1 - */ - result = imap_sendf(conn, str, "%s FETCH 1 BODY[TEXT]", str); - if(result) - return result; + char *line = conn->data->state.buffer; + size_t len = strlen(line); - /* - * When issued, the server will respond with a single line similar to - * '* 1 FETCH (BODY[TEXT] {2021}' - * - * Identifying the fetch and how many bytes of contents we can expect. We - * must extract that number before continuing to "download as usual". - */ + (void)instate; /* No use for this yet */ - state(conn, IMAP_FETCH); + if(imapcode == '*') { + /* Temporarily add the LF character back and send as body to the client */ + line[len] = '\n'; + result = Curl_client_write(conn, CLIENTWRITE_BODY, line, len + 1); + line[len] = '\0'; + } + else if(imapcode != 'O') + result = CURLE_QUOTE_ERROR; /* TODO: Fix error code */ + else + /* End of DO phase */ + state(conn, IMAP_STOP); return result; } /* For SELECT responses */ -static CURLcode imap_state_select_resp(struct connectdata *conn, - int imapcode, +static CURLcode imap_state_select_resp(struct connectdata *conn, int imapcode, imapstate instate) { CURLcode result = CURLE_OK; struct SessionHandle *data = conn->data; + struct IMAP *imap = conn->data->req.protop; + struct imap_conn *imapc = &conn->proto.imapc; + const char *line = data->state.buffer; + char tmp[20]; (void)instate; /* no use for this yet */ - if(imapcode != 'O') { + if(imapcode == '*') { + /* See if this is an UIDVALIDITY response */ + if(sscanf(line + 2, "OK [UIDVALIDITY %19[0123456789]]", tmp) == 1) { + Curl_safefree(imapc->mailbox_uidvalidity); + imapc->mailbox_uidvalidity = strdup(tmp); + } + } + else if(imapcode == 'O') { + /* Check if the UIDVALIDITY has been specified and matches */ + if(imap->uidvalidity && imapc->mailbox_uidvalidity && + strcmp(imap->uidvalidity, imapc->mailbox_uidvalidity)) { + failf(conn->data, "Mailbox UIDVALIDITY has changed"); + result = CURLE_REMOTE_FILE_NOT_FOUND; + } + else { + /* Note the currently opened mailbox on this connection */ + imapc->mailbox = strdup(imap->mailbox); + + if(imap->custom) + result = imap_perform_list(conn); + else + result = imap_perform_fetch(conn); + } + } + else { failf(data, "Select failed"); result = CURLE_LOGIN_DENIED; } - else - result = imap_fetch(conn); return result; } -/* For the (first line of) FETCH BODY[TEXT] response */ +/* For the (first line of the) FETCH responses */ static CURLcode imap_state_fetch_resp(struct connectdata *conn, int imapcode, imapstate instate) { CURLcode result = CURLE_OK; struct SessionHandle *data = conn->data; struct imap_conn *imapc = &conn->proto.imapc; - struct FTP *imap = data->state.proto.imap; struct pingpong *pp = &imapc->pp; const char *ptr = data->state.buffer; + bool parsed = FALSE; + curl_off_t size; (void)instate; /* no use for this yet */ - if('*' != imapcode) { + if(imapcode != '*') { Curl_pgrsSetDownloadSize(data, 0); state(conn, IMAP_STOP); - return CURLE_OK; + return CURLE_REMOTE_FILE_NOT_FOUND; /* TODO: Fix error code */ } - /* Something like this comes "* 1 FETCH (BODY[TEXT] {2021}\r" */ + /* Something like this is received "* 1 FETCH (BODY[TEXT] {2021}\r" so parse + the continuation data contained within the curly brackets */ while(*ptr && (*ptr != '{')) ptr++; if(*ptr == '{') { - curl_off_t filesize = curlx_strtoofft(ptr + 1, NULL, 10); - if(filesize) - Curl_pgrsSetDownloadSize(data, filesize); + char *endptr; + size = curlx_strtoofft(ptr + 1, &endptr, 10); + if(endptr - ptr > 1 && endptr[0] == '}' && + endptr[1] == '\r' && endptr[2] == '\0') + parsed = TRUE; + } - infof(data, "Found %" FORMAT_OFF_TU " bytes to download\n", filesize); + if(parsed) { + infof(data, "Found %" FORMAT_OFF_TU " bytes to download\n", size); + Curl_pgrsSetDownloadSize(data, size); if(pp->cache) { /* At this point there is a bunch of data in the header "cache" that is @@ -1133,56 +1462,121 @@ static CURLcode imap_state_fetch_resp(struct connectdata *conn, int imapcode, that there may even be additional "headers" after the body. */ size_t chunk = pp->cache_size; - if(chunk > (size_t)filesize) - /* the conversion from curl_off_t to size_t is always fine here */ - chunk = (size_t)filesize; + if(chunk > (size_t)size) + /* The conversion from curl_off_t to size_t is always fine here */ + chunk = (size_t)size; result = Curl_client_write(conn, CLIENTWRITE_BODY, pp->cache, chunk); if(result) return result; - filesize -= chunk; + data->req.bytecount += chunk; + + infof(data, "Written %" FORMAT_OFF_TU " bytes, %" FORMAT_OFF_TU + " bytes are left for transfer\n", (curl_off_t)chunk, + size - chunk); - /* we've now used parts of or the entire cache */ + /* Have we used the entire cache or just part of it?*/ if(pp->cache_size > chunk) { - /* part of, move the trailing data to the start and reduce the size */ - memmove(pp->cache, pp->cache+chunk, - pp->cache_size - chunk); + /* Only part of it so shrink the cache to fit the trailing data */ + memmove(pp->cache, pp->cache + chunk, pp->cache_size - chunk); pp->cache_size -= chunk; } else { - /* cache is drained */ + /* Free the cache */ Curl_safefree(pp->cache); - pp->cache = NULL; + + /* Reset the cache size */ pp->cache_size = 0; } } - infof(data, "Filesize left: %" FORMAT_OFF_T "\n", filesize); - - if(!filesize) - /* the entire data is already transferred! */ + if(data->req.bytecount == size) + /* The entire data is already transferred! */ Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL); - else + else { /* IMAP download */ - Curl_setup_transfer(conn, FIRSTSOCKET, filesize, FALSE, - imap->bytecountp, -1, NULL); /* no upload here */ - - data->req.maxdownload = filesize; + data->req.maxdownload = size; + Curl_setup_transfer(conn, FIRSTSOCKET, size, FALSE, NULL, -1, NULL); + } } - else + else { /* We don't know how to parse this line */ + failf(pp->conn->data, "Failed to parse FETCH response."); result = CURLE_FTP_WEIRD_SERVER_REPLY; /* TODO: fix this code */ + } - /* End of do phase */ + /* End of DO phase */ state(conn, IMAP_STOP); return result; } +/* For final FETCH responses performed after the download */ +static CURLcode imap_state_fetch_final_resp(struct connectdata *conn, + int imapcode, + imapstate instate) +{ + CURLcode result = CURLE_OK; + + (void)instate; /* No use for this yet */ + + if(imapcode != 'O') + result = CURLE_FTP_WEIRD_SERVER_REPLY; /* TODO: Fix error code */ + else + /* End of DONE phase */ + state(conn, IMAP_STOP); + + return result; +} + +/* For APPEND responses */ +static CURLcode imap_state_append_resp(struct connectdata *conn, int imapcode, + imapstate instate) +{ + CURLcode result = CURLE_OK; + struct SessionHandle *data = conn->data; + + (void)instate; /* No use for this yet */ + + if(imapcode != '+') { + result = CURLE_UPLOAD_FAILED; + } + else { + /* Set the progress upload size */ + Curl_pgrsSetUploadSize(data, data->set.infilesize); + + /* IMAP upload */ + Curl_setup_transfer(conn, -1, -1, FALSE, NULL, FIRSTSOCKET, NULL); + + /* End of DO phase */ + state(conn, IMAP_STOP); + } + + return result; +} + +/* For final APPEND responses performed after the upload */ +static CURLcode imap_state_append_final_resp(struct connectdata *conn, + int imapcode, + imapstate instate) +{ + CURLcode result = CURLE_OK; + + (void)instate; /* No use for this yet */ + + if(imapcode != 'O') + result = CURLE_UPLOAD_FAILED; + else + /* End of DONE phase */ + state(conn, IMAP_STOP); + + return result; +} + static CURLcode imap_statemach_act(struct connectdata *conn) { - CURLcode result; + CURLcode result = CURLE_OK; curl_socket_t sock = conn->sock[FIRSTSOCKET]; int imapcode; struct imap_conn *imapc = &conn->proto.imapc; @@ -1191,32 +1585,39 @@ static CURLcode imap_statemach_act(struct connectdata *conn) /* Busy upgrading the connection; right now all I/O is SSL/TLS, not IMAP */ if(imapc->state == IMAP_UPGRADETLS) - return imap_state_upgrade_tls(conn); + return imap_perform_upgrade_tls(conn); /* Flush any data that needs to be sent */ if(pp->sendleft) return Curl_pp_flushsend(pp); - /* Read the response from the server */ - result = Curl_pp_readresp(sock, pp, &imapcode, &nread); - if(result) - return result; + do { + /* Read the response from the server */ + result = Curl_pp_readresp(sock, pp, &imapcode, &nread); + if(result) + return result; + + /* Was there an error parsing the response line? */ + if(imapcode == -1) + return CURLE_FTP_WEIRD_SERVER_REPLY; + + if(!imapcode) + break; - if(imapcode) { /* We have now received a full IMAP server response */ switch(imapc->state) { case IMAP_SERVERGREET: result = imap_state_servergreet_resp(conn, imapcode, imapc->state); break; - case IMAP_STARTTLS: - result = imap_state_starttls_resp(conn, imapcode, imapc->state); - break; - case IMAP_CAPABILITY: result = imap_state_capability_resp(conn, imapcode, imapc->state); break; + case IMAP_STARTTLS: + result = imap_state_starttls_resp(conn, imapcode, imapc->state); + break; + case IMAP_AUTHENTICATE_PLAIN: result = imap_state_auth_plain_resp(conn, imapcode, imapc->state); break; @@ -1255,7 +1656,11 @@ static CURLcode imap_statemach_act(struct connectdata *conn) break; #endif - case IMAP_AUTHENTICATE: + case IMAP_AUTHENTICATE_XOAUTH2: + result = imap_state_auth_xoauth2_resp(conn, imapcode, imapc->state); + break; + + case IMAP_AUTHENTICATE_FINAL: result = imap_state_auth_final_resp(conn, imapcode, imapc->state); break; @@ -1263,14 +1668,30 @@ static CURLcode imap_statemach_act(struct connectdata *conn) result = imap_state_login_resp(conn, imapcode, imapc->state); break; - case IMAP_FETCH: - result = imap_state_fetch_resp(conn, imapcode, imapc->state); + case IMAP_LIST: + result = imap_state_list_resp(conn, imapcode, imapc->state); break; case IMAP_SELECT: result = imap_state_select_resp(conn, imapcode, imapc->state); break; + case IMAP_FETCH: + result = imap_state_fetch_resp(conn, imapcode, imapc->state); + break; + + case IMAP_FETCH_FINAL: + result = imap_state_fetch_final_resp(conn, imapcode, imapc->state); + break; + + case IMAP_APPEND: + result = imap_state_append_resp(conn, imapcode, imapc->state); + break; + + case IMAP_APPEND_FINAL: + result = imap_state_append_final_resp(conn, imapcode, imapc->state); + break; + case IMAP_LOGOUT: /* fallthrough, just stop! */ default: @@ -1278,7 +1699,7 @@ static CURLcode imap_statemach_act(struct connectdata *conn) state(conn, IMAP_STOP); break; } - } + } while(!result && imapc->state != IMAP_STOP && Curl_pp_moredata(pp)); return result; } @@ -1286,30 +1707,28 @@ static CURLcode imap_statemach_act(struct connectdata *conn) /* Called repeatedly until done from multi.c */ static CURLcode imap_multi_statemach(struct connectdata *conn, bool *done) { + CURLcode result = CURLE_OK; struct imap_conn *imapc = &conn->proto.imapc; - CURLcode result; - if((conn->handler->flags & PROTOPT_SSL) && !imapc->ssldone) + if((conn->handler->flags & PROTOPT_SSL) && !imapc->ssldone) { result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, &imapc->ssldone); - else - result = Curl_pp_multi_statemach(&imapc->pp); + if(result || !imapc->ssldone) + return result; + } + result = Curl_pp_statemach(&imapc->pp, FALSE); *done = (imapc->state == IMAP_STOP) ? TRUE : FALSE; return result; } -static CURLcode imap_easy_statemach(struct connectdata *conn) +static CURLcode imap_block_statemach(struct connectdata *conn) { - struct imap_conn *imapc = &conn->proto.imapc; - struct pingpong *pp = &imapc->pp; CURLcode result = CURLE_OK; + struct imap_conn *imapc = &conn->proto.imapc; - while(imapc->state != IMAP_STOP) { - result = Curl_pp_easy_statemach(pp); - if(result) - break; - } + while(imapc->state != IMAP_STOP && !result) + result = Curl_pp_statemach(&imapc->pp, TRUE); return result; } @@ -1318,68 +1737,67 @@ static CURLcode imap_easy_statemach(struct connectdata *conn) required */ static CURLcode imap_init(struct connectdata *conn) { + CURLcode result = CURLE_OK; struct SessionHandle *data = conn->data; - struct FTP *imap = data->state.proto.imap; - - if(!imap) { - imap = data->state.proto.imap = calloc(sizeof(struct FTP), 1); - if(!imap) - return CURLE_OUT_OF_MEMORY; - } + struct IMAP *imap; - /* Get some initial data into the imap struct */ - imap->bytecountp = &data->req.bytecount; + imap = data->req.protop = calloc(sizeof(struct IMAP), 1); + if(!imap) + result = CURLE_OUT_OF_MEMORY; - /* No need to duplicate user+password, the connectdata struct won't change - during a session, but we re-init them here since on subsequent inits - since the conn struct may have changed or been replaced. - */ - imap->user = conn->user; - imap->passwd = conn->passwd; + return result; +} - return CURLE_OK; +/* For the IMAP "protocol connect" and "doing" phases only */ +static int imap_getsock(struct connectdata *conn, curl_socket_t *socks, + int numsocks) +{ + return Curl_pp_getsock(&conn->proto.imapc.pp, socks, numsocks); } /*********************************************************************** * - * imap_connect() should do everything that is to be considered a part of - * the connection phase. + * imap_connect() + * + * This function should do everything that is to be considered a part of the + * connection phase. * * The variable 'done' points to will be TRUE if the protocol-layer connect - * phase is done when this function returns, or FALSE is not. When called as - * a part of the easy interface, it will always be TRUE. + * phase is done when this function returns, or FALSE if not. */ static CURLcode imap_connect(struct connectdata *conn, bool *done) { - CURLcode result; + CURLcode result = CURLE_OK; struct imap_conn *imapc = &conn->proto.imapc; struct pingpong *pp = &imapc->pp; *done = FALSE; /* default to not done yet */ - /* If there already is a protocol-specific struct allocated for this - sessionhandle, deal with it */ - Curl_reset_reqproto(conn); - - result = imap_init(conn); - if(CURLE_OK != result) - return result; - - /* We always support persistent connections on imap */ + /* We always support persistent connections in IMAP */ conn->bits.close = FALSE; - pp->response_time = RESP_TIMEOUT; /* set default response time-out */ + /* Set the default response time-out */ + pp->response_time = RESP_TIMEOUT; pp->statemach_act = imap_statemach_act; pp->endofresp = imap_endofresp; pp->conn = conn; - Curl_pp_init(pp); /* init generic pingpong data */ + /* Set the default preferred authentication mechanism */ + imapc->prefmech = SASL_AUTH_ANY; + + /* Initialise the pingpong layer */ + Curl_pp_init(pp); + + /* Parse the URL options */ + result = imap_parse_url_options(conn); + if(result) + return result; /* Start off waiting for the server greeting response */ state(conn, IMAP_SERVERGREET); - /* Start off with an id of '*' */ - imapc->idstr = "*"; + /* Start off with an response id of '*' */ + strcpy(imapc->resptag, "*"); result = imap_multi_statemach(conn, done); @@ -1398,26 +1816,55 @@ static CURLcode imap_connect(struct connectdata *conn, bool *done) static CURLcode imap_done(struct connectdata *conn, CURLcode status, bool premature) { + CURLcode result = CURLE_OK; struct SessionHandle *data = conn->data; - struct FTP *imap = data->state.proto.imap; - CURLcode result=CURLE_OK; + struct IMAP *imap = data->req.protop; (void)premature; if(!imap) - /* When the easy handle is removed from the multi while libcurl is still - * trying to resolve the host name, it seems that the imap struct is not - * yet initialized, but the removal action calls Curl_done() which calls - * this function. So we simply return success if no imap pointer is set. - */ + /* When the easy handle is removed from the multi interface while libcurl + is still trying to resolve the host name, the IMAP struct is not yet + initialized. However, the removal action calls Curl_done() which in + turn calls this function, so we simply return success. */ return CURLE_OK; if(status) { conn->bits.close = TRUE; /* marked for closure */ result = status; /* use the already set error code */ } + else if(!data->set.connect_only && !imap->custom && + (imap->uid || data->set.upload)) { + /* Handle responses after FETCH or APPEND transfer has finished */ + if(!data->set.upload) + state(conn, IMAP_FETCH_FINAL); + else { + /* End the APPEND command first by sending an empty line */ + result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", ""); + if(!result) + state(conn, IMAP_APPEND_FINAL); + } + + /* Run the state-machine + + TODO: when the multi interface is used, this _really_ should be using + the imap_multi_statemach function but we have no general support for + non-blocking DONE operations, not in the multi state machine and with + Curl_done() invokes on several places in the code! + */ + if(!result) + result = imap_block_statemach(conn); + } - /* Clear the transfer mode for the next connection */ + /* Cleanup our per-request based variables */ + Curl_safefree(imap->mailbox); + Curl_safefree(imap->uidvalidity); + Curl_safefree(imap->uid); + Curl_safefree(imap->section); + Curl_safefree(imap->custom); + Curl_safefree(imap->custom_params); + + /* Clear the transfer mode for the next request */ imap->transfer = FTPTRANSFER_BODY; return result; @@ -1427,31 +1874,57 @@ static CURLcode imap_done(struct connectdata *conn, CURLcode status, * * imap_perform() * - * This is the actual DO function for IMAP. Get a file/directory according to - * the options previously setup. + * This is the actual DO function for IMAP. Fetch or append a message, or do + * other things according to the options previously setup. */ static CURLcode imap_perform(struct connectdata *conn, bool *connected, bool *dophase_done) { /* This is IMAP and no proxy */ CURLcode result = CURLE_OK; + struct SessionHandle *data = conn->data; + struct IMAP *imap = data->req.protop; + struct imap_conn *imapc = &conn->proto.imapc; + bool selected = FALSE; DEBUGF(infof(conn->data, "DO phase starts\n")); if(conn->data->set.opt_no_body) { /* Requested no body means no transfer */ - struct FTP *imap = conn->data->state.proto.imap; imap->transfer = FTPTRANSFER_INFO; } *dophase_done = FALSE; /* not done yet */ + /* Determine if the requested mailbox (with the same UIDVALIDITY if set) + has already been selected on this connection */ + if(imap->mailbox && imapc->mailbox && + !strcmp(imap->mailbox, imapc->mailbox) && + (!imap->uidvalidity || !imapc->mailbox_uidvalidity || + !strcmp(imap->uidvalidity, imapc->mailbox_uidvalidity))) + selected = TRUE; + /* Start the first command in the DO phase */ - result = imap_select(conn); + if(conn->data->set.upload) + /* APPEND can be executed directly */ + result = imap_perform_append(conn); + else if(imap->custom && (selected || !imap->mailbox)) + /* Custom command using the same mailbox or no mailbox */ + result = imap_perform_list(conn); + else if(!imap->custom && selected && imap->uid) + /* FETCH from the same mailbox */ + result = imap_perform_fetch(conn); + else if(imap->mailbox && !selected && (imap->custom || imap->uid)) + /* SELECT the mailbox */ + result = imap_perform_select(conn); + else + /* LIST */ + result = imap_perform_list(conn); + if(result) return result; - /* run the state-machine */ + /* Run the state-machine */ result = imap_multi_statemach(conn, dophase_done); *connected = conn->bits.tcpconnect[FIRSTSOCKET]; @@ -1473,52 +1946,21 @@ static CURLcode imap_perform(struct connectdata *conn, bool *connected, */ static CURLcode imap_do(struct connectdata *conn, bool *done) { - CURLcode retcode = CURLE_OK; + CURLcode result = CURLE_OK; *done = FALSE; /* default to false */ - /* - Since connections can be re-used between SessionHandles, this might be a - connection already existing but on a fresh SessionHandle struct so we must - make sure we have a good 'struct IMAP' to play with. For new connections, - the struct IMAP is allocated and setup in the imap_connect() function. - */ - Curl_reset_reqproto(conn); - retcode = imap_init(conn); - if(retcode) - return retcode; - /* Parse the URL path */ - retcode = imap_parse_url_path(conn); - if(retcode) - return retcode; - - retcode = imap_regular_transfer(conn, done); - - return retcode; -} - -/*********************************************************************** - * - * imap_logout() - * - * This should be called before calling sclose(). We should then wait for the - * response from the server before returning. The calling code should then try - * to close the connection. - * - */ -static CURLcode imap_logout(struct connectdata *conn) -{ - CURLcode result = CURLE_OK; - const char *str = getcmdid(conn); - - result = imap_sendf(conn, str, "%s LOGOUT", str, NULL); + result = imap_parse_url_path(conn); if(result) return result; - state(conn, IMAP_LOGOUT); + /* Parse the custom request */ + result = imap_parse_custom_request(conn); + if(result) + return result; - result = imap_easy_statemach(conn); + result = imap_regular_transfer(conn, done); return result; } @@ -1532,16 +1974,17 @@ static CURLcode imap_logout(struct connectdata *conn) */ static CURLcode imap_disconnect(struct connectdata *conn, bool dead_connection) { - struct imap_conn *imapc= &conn->proto.imapc; + struct imap_conn *imapc = &conn->proto.imapc; /* We cannot send quit unconditionally. If this connection is stale or bad in any way, sending quit and waiting around here will make the - disconnect wait in vain and cause more problems than we need to */ + disconnect wait in vain and cause more problems than we need to. */ /* The IMAP session may or may not have been allocated/setup at this point! */ if(!dead_connection && imapc->pp.conn) - (void)imap_logout(conn); /* ignore errors on the LOGOUT */ + if(!imap_perform_logout(conn)) + (void)imap_block_statemach(conn); /* ignore errors on LOGOUT */ /* Disconnect from the server */ Curl_pp_disconnect(&imapc->pp); @@ -1551,35 +1994,15 @@ static CURLcode imap_disconnect(struct connectdata *conn, bool dead_connection) /* Cleanup our connection based variables */ Curl_safefree(imapc->mailbox); + Curl_safefree(imapc->mailbox_uidvalidity); return CURLE_OK; } -/*********************************************************************** - * - * imap_parse_url_path() - * - * Parse the URL path into separate path components. - * - */ -static CURLcode imap_parse_url_path(struct connectdata *conn) -{ - /* The imap struct is already inited in imap_connect() */ - struct imap_conn *imapc = &conn->proto.imapc; - struct SessionHandle *data = conn->data; - const char *path = data->state.path; - - if(!*path) - path = "INBOX"; - - /* URL decode the path and use this mailbox */ - return Curl_urldecode(data, path, 0, &imapc->mailbox, NULL, TRUE); -} - /* Call this when the DO phase has completed */ static CURLcode imap_dophase_done(struct connectdata *conn, bool connected) { - struct FTP *imap = conn->data->state.proto.imap; + struct IMAP *imap = conn->data->req.protop; (void)connected; @@ -1597,12 +2020,10 @@ static CURLcode imap_doing(struct connectdata *conn, bool *dophase_done) if(result) DEBUGF(infof(conn->data, "DO phase failed\n")); - else { - if(*dophase_done) { - result = imap_dophase_done(conn, FALSE /* not connected */); + else if(*dophase_done) { + result = imap_dophase_done(conn, FALSE /* not connected */); - DEBUGF(infof(conn->data, "DO phase is complete\n")); - } + DEBUGF(infof(conn->data, "DO phase is complete\n")); } return result; @@ -1627,30 +2048,33 @@ static CURLcode imap_regular_transfer(struct connectdata *conn, /* Make sure size is unknown at this point */ data->req.size = -1; + /* Set the progress data */ Curl_pgrsSetUploadCounter(data, 0); Curl_pgrsSetDownloadCounter(data, 0); Curl_pgrsSetUploadSize(data, 0); Curl_pgrsSetDownloadSize(data, 0); + /* Carry out the perform */ result = imap_perform(conn, &connected, dophase_done); - if(CURLE_OK == result) { - if(!*dophase_done) - /* The DO phase has not completed yet */ - return CURLE_OK; - + /* Perform post DO phase operations if necessary */ + if(!result && *dophase_done) result = imap_dophase_done(conn, connected); - } return result; } -static CURLcode imap_setup_connection(struct connectdata * conn) +static CURLcode imap_setup_connection(struct connectdata *conn) { struct SessionHandle *data = conn->data; + /* Initialise the IMAP layer */ + CURLcode result = imap_init(conn); + if(result) + return result; + if(conn->bits.httpproxy && !data->set.tunnel_thru_httpproxy) { - /* Unless we have asked to tunnel imap operations through the proxy, we + /* Unless we have asked to tunnel IMAP operations through the proxy, we switch and use HTTP operations only */ #ifndef CURL_DISABLE_HTTP if(conn->handler == &Curl_handler_imap) @@ -1664,10 +2088,8 @@ static CURLcode imap_setup_connection(struct connectdata * conn) #endif } - /* We explicitly mark this connection as persistent here as we're doing - IMAP over HTTP and thus we accidentally avoid setting this value - otherwise */ - conn->bits.close = FALSE; + /* set it up as an HTTP connection instead */ + return conn->handler->setup_connection(conn); #else failf(data, "IMAP over http proxy requires HTTP support built-in!"); return CURLE_UNSUPPORTED_PROTOCOL; @@ -1679,4 +2101,352 @@ static CURLcode imap_setup_connection(struct connectdata * conn) return CURLE_OK; } +/*********************************************************************** + * + * imap_sendf() + * + * Sends the formated string as an IMAP command to the server. + * + * Designed to never block. + */ +static CURLcode imap_sendf(struct connectdata *conn, const char *fmt, ...) +{ + CURLcode result = CURLE_OK; + struct imap_conn *imapc = &conn->proto.imapc; + char *taggedfmt; + va_list ap; + + DEBUGASSERT(fmt); + + /* Calculate the next command ID wrapping at 3 digits */ + imapc->cmdid = (imapc->cmdid + 1) % 1000; + + /* Calculate the tag based on the connection ID and command ID */ + snprintf(imapc->resptag, sizeof(imapc->resptag), "%c%03d", + 'A' + curlx_sltosi(conn->connection_id % 26), imapc->cmdid); + + /* Prefix the format with the tag */ + taggedfmt = aprintf("%s %s", imapc->resptag, fmt); + if(!taggedfmt) + return CURLE_OUT_OF_MEMORY; + + /* Send the data with the tag */ + va_start(ap, fmt); + result = Curl_pp_vsendf(&imapc->pp, taggedfmt, ap); + va_end(ap); + + Curl_safefree(taggedfmt); + + return result; +} + +/*********************************************************************** + * + * imap_atom() + * + * Checks the input string for characters that need escaping and returns an + * atom ready for sending to the server. + * + * The returned string needs to be freed. + * + */ +static char *imap_atom(const char *str) +{ + const char *p1; + char *p2; + size_t backsp_count = 0; + size_t quote_count = 0; + bool space_exists = FALSE; + size_t newlen = 0; + char *newstr = NULL; + + if(!str) + return NULL; + + /* Count any unescapped characters */ + p1 = str; + while(*p1) { + if(*p1 == '\\') + backsp_count++; + else if(*p1 == '"') + quote_count++; + else if(*p1 == ' ') + space_exists = TRUE; + + p1++; + } + + /* Does the input contain any unescapped characters? */ + if(!backsp_count && !quote_count && !space_exists) + return strdup(str); + + /* Calculate the new string length */ + newlen = strlen(str) + backsp_count + quote_count + (space_exists ? 2 : 0); + + /* Allocate the new string */ + newstr = (char *) malloc((newlen + 1) * sizeof(char)); + if(!newstr) + return NULL; + + /* Surround the string in quotes if necessary */ + p2 = newstr; + if(space_exists) { + newstr[0] = '"'; + newstr[newlen - 1] = '"'; + p2++; + } + + /* Copy the string, escaping backslash and quote characters along the way */ + p1 = str; + while(*p1) { + if(*p1 == '\\' || *p1 == '"') { + *p2 = '\\'; + p2++; + } + + *p2 = *p1; + + p1++; + p2++; + } + + /* Terminate the string */ + newstr[newlen] = '\0'; + + return newstr; +} + +/*********************************************************************** + * + * imap_is_bchar() + * + * Portable test of whether the specified char is a "bchar" as defined in the + * grammar of RFC-5092. + */ +static bool imap_is_bchar(char ch) +{ + switch(ch) { + /* bchar */ + case ':': case '@': case '/': + /* bchar -> achar */ + case '&': case '=': + /* bchar -> achar -> uchar -> unreserved */ + case '0': case '1': case '2': case '3': case '4': case '5': case '6': + case '7': case '8': case '9': + case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G': + case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N': + case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U': + case 'V': case 'W': case 'X': case 'Y': case 'Z': + case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g': + case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n': + case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u': + case 'v': case 'w': case 'x': case 'y': case 'z': + case '-': case '.': case '_': case '~': + /* bchar -> achar -> uchar -> sub-delims-sh */ + case '!': case '$': case '\'': case '(': case ')': case '*': + case '+': case ',': + /* bchar -> achar -> uchar -> pct-encoded */ + case '%': /* HEXDIG chars are already included above */ + return true; + + default: + return false; + } +} + +/*********************************************************************** + * + * imap_parse_url_options() + * + * Parse the URL login options. + */ +static CURLcode imap_parse_url_options(struct connectdata *conn) +{ + CURLcode result = CURLE_OK; + struct imap_conn *imapc = &conn->proto.imapc; + const char *options = conn->options; + const char *ptr = options; + + if(options) { + const char *key = ptr; + + while(*ptr && *ptr != '=') + ptr++; + + if(strnequal(key, "AUTH", 4)) { + const char *value = ptr + 1; + + if(strequal(value, "*")) + imapc->prefmech = SASL_AUTH_ANY; + else if(strequal(value, SASL_MECH_STRING_LOGIN)) + imapc->prefmech = SASL_MECH_LOGIN; + else if(strequal(value, SASL_MECH_STRING_PLAIN)) + imapc->prefmech = SASL_MECH_PLAIN; + else if(strequal(value, SASL_MECH_STRING_CRAM_MD5)) + imapc->prefmech = SASL_MECH_CRAM_MD5; + else if(strequal(value, SASL_MECH_STRING_DIGEST_MD5)) + imapc->prefmech = SASL_MECH_DIGEST_MD5; + else if(strequal(value, SASL_MECH_STRING_GSSAPI)) + imapc->prefmech = SASL_MECH_GSSAPI; + else if(strequal(value, SASL_MECH_STRING_NTLM)) + imapc->prefmech = SASL_MECH_NTLM; + else if(strequal(value, SASL_MECH_STRING_XOAUTH2)) + imapc->prefmech = SASL_MECH_XOAUTH2; + else + imapc->prefmech = SASL_AUTH_NONE; + } + else + result = CURLE_URL_MALFORMAT; + } + + return result; +} + +/*********************************************************************** + * + * imap_parse_url_path() + * + * Parse the URL path into separate path components. + * + */ +static CURLcode imap_parse_url_path(struct connectdata *conn) +{ + /* The imap struct is already initialised in imap_connect() */ + CURLcode result = CURLE_OK; + struct SessionHandle *data = conn->data; + struct IMAP *imap = data->req.protop; + const char *begin = data->state.path; + const char *ptr = begin; + + /* See how much of the URL is a valid path and decode it */ + while(imap_is_bchar(*ptr)) + ptr++; + + if(ptr != begin) { + /* Remove the trailing slash if present */ + const char *end = ptr; + if(end > begin && end[-1] == '/') + end--; + + result = Curl_urldecode(data, begin, end - begin, &imap->mailbox, NULL, + TRUE); + if(result) + return result; + } + else + imap->mailbox = NULL; + + /* There can be any number of parameters in the form ";NAME=VALUE" */ + while(*ptr == ';') { + char *name; + char *value; + size_t valuelen; + + /* Find the length of the name parameter */ + begin = ++ptr; + while(*ptr && *ptr != '=') + ptr++; + + if(!*ptr) + return CURLE_URL_MALFORMAT; + + /* Decode the name parameter */ + result = Curl_urldecode(data, begin, ptr - begin, &name, NULL, TRUE); + if(result) + return result; + + /* Find the length of the value parameter */ + begin = ++ptr; + while(imap_is_bchar(*ptr)) + ptr++; + + /* Decode the value parameter */ + result = Curl_urldecode(data, begin, ptr - begin, &value, &valuelen, TRUE); + if(result) { + Curl_safefree(name); + return result; + } + + DEBUGF(infof(conn->data, "IMAP URL parameter '%s' = '%s'\n", name, value)); + + /* Process the known hierarchical parameters (UIDVALIDITY, UID and SECTION) + stripping of the trailing slash character if it is present. + + Note: Unknown parameters trigger a URL_MALFORMAT error. */ + if(Curl_raw_equal(name, "UIDVALIDITY") && !imap->uidvalidity) { + if(valuelen > 0 && value[valuelen - 1] == '/') + value[valuelen - 1] = '\0'; + + imap->uidvalidity = value; + value = NULL; + } + else if(Curl_raw_equal(name, "UID") && !imap->uid) { + if(valuelen > 0 && value[valuelen - 1] == '/') + value[valuelen - 1] = '\0'; + + imap->uid = value; + value = NULL; + } + else if(Curl_raw_equal(name, "SECTION") && !imap->section) { + if(valuelen > 0 && value[valuelen - 1] == '/') + value[valuelen - 1] = '\0'; + + imap->section = value; + value = NULL; + } + else { + Curl_safefree(name); + Curl_safefree(value); + + return CURLE_URL_MALFORMAT; + } + + Curl_safefree(name); + Curl_safefree(value); + } + + /* Any extra stuff at the end of the URL is an error */ + if(*ptr) + return CURLE_URL_MALFORMAT; + + return CURLE_OK; +} + +/*********************************************************************** + * + * imap_parse_custom_request() + * + * Parse the custom request. + */ +static CURLcode imap_parse_custom_request(struct connectdata *conn) +{ + CURLcode result = CURLE_OK; + struct SessionHandle *data = conn->data; + struct IMAP *imap = data->req.protop; + const char *custom = data->set.str[STRING_CUSTOMREQUEST]; + + if(custom) { + /* URL decode the custom request */ + result = Curl_urldecode(data, custom, 0, &imap->custom, NULL, TRUE); + + /* Extract the parameters if specified */ + if(!result) { + const char *params = imap->custom; + + while(*params && *params != ' ') + params++; + + if(*params) { + imap->custom_params = strdup(params); + imap->custom[params - imap->custom] = '\0'; + + if(!imap->custom_params) + result = CURLE_OUT_OF_MEMORY; + } + } + } + + return result; +} + #endif /* CURL_DISABLE_IMAP */ diff --git a/plugins/FTPFileYM/curl/lib/imap.h b/plugins/FTPFileYM/curl/lib/imap.h index b37cdab839..1d4faabd70 100644 --- a/plugins/FTPFileYM/curl/lib/imap.h +++ b/plugins/FTPFileYM/curl/lib/imap.h @@ -31,10 +31,10 @@ typedef enum { IMAP_STOP, /* do nothing state, stops the state machine */ IMAP_SERVERGREET, /* waiting for the initial greeting immediately after a connect */ + IMAP_CAPABILITY, IMAP_STARTTLS, IMAP_UPGRADETLS, /* asynchronously upgrade the connection to SSL/TLS (multi mode only) */ - IMAP_CAPABILITY, IMAP_AUTHENTICATE_PLAIN, IMAP_AUTHENTICATE_LOGIN, IMAP_AUTHENTICATE_LOGIN_PASSWD, @@ -43,26 +43,49 @@ typedef enum { IMAP_AUTHENTICATE_DIGESTMD5_RESP, IMAP_AUTHENTICATE_NTLM, IMAP_AUTHENTICATE_NTLM_TYPE2MSG, - IMAP_AUTHENTICATE, + IMAP_AUTHENTICATE_XOAUTH2, + IMAP_AUTHENTICATE_FINAL, IMAP_LOGIN, + IMAP_LIST, IMAP_SELECT, IMAP_FETCH, + IMAP_FETCH_FINAL, + IMAP_APPEND, + IMAP_APPEND_FINAL, IMAP_LOGOUT, IMAP_LAST /* never used */ } imapstate; +/* This IMAP struct is used in the SessionHandle. All IMAP data that is + connection-oriented must be in imap_conn to properly deal with the fact that + perhaps the SessionHandle is changed between the times the connection is + used. */ +struct IMAP { + curl_pp_transfer transfer; + char *mailbox; /* Mailbox to select */ + char *uidvalidity; /* UIDVALIDITY to check in select */ + char *uid; /* Message UID to fetch */ + char *section; /* Message SECTION to fetch */ + char *custom; /* Custom request */ + char *custom_params; /* Parameters for the custom request */ +}; + /* imap_conn is used for struct connection-oriented data in the connectdata struct */ struct imap_conn { struct pingpong pp; - char *mailbox; /* Mailbox to select */ - unsigned int authmechs; /* Accepted authentication mechanisms */ - unsigned int authused; /* Auth mechanism used for the connection */ - imapstate state; /* Always use imap.c:state() to change state! */ - int cmdid; /* Next command ID */ - const char *idstr; /* String based response ID to wait for */ - bool ssldone; /* Is connect() over SSL done? */ - bool login_disabled; /* LOGIN command explicitly disabled by server */ + imapstate state; /* Always use imap.c:state() to change state! */ + bool ssldone; /* Is connect() over SSL done? */ + unsigned int authmechs; /* Accepted authentication mechanisms */ + unsigned int prefmech; /* Preferred authentication mechanism */ + unsigned int authused; /* Auth mechanism used for the connection */ + int cmdid; /* Last used command ID */ + char resptag[5]; /* Response tag to wait for */ + bool tls_supported; /* StartTLS capability supported by server */ + bool login_disabled; /* LOGIN command disabled by server */ + bool ir_supported; /* Initial response supported by server */ + char *mailbox; /* The last selected mailbox */ + char *mailbox_uidvalidity; /* UIDVALIDITY parsed from select response */ }; extern const struct Curl_handler Curl_handler_imap; diff --git a/plugins/FTPFileYM/curl/lib/krb5.c b/plugins/FTPFileYM/curl/lib/krb5.c index 1e99c709e6..1643f11a6a 100644 --- a/plugins/FTPFileYM/curl/lib/krb5.c +++ b/plugins/FTPFileYM/curl/lib/krb5.c @@ -1,6 +1,6 @@ /* GSSAPI/krb5 support for FTP - loosely based on old krb4.c * - * Copyright (c) 1995, 1996, 1997, 1998, 1999 Kungliga Tekniska Högskolan + * Copyright (c) 1995, 1996, 1997, 1998, 1999, 2013 Kungliga Tekniska Högskolan * (Royal Institute of Technology, Stockholm, Sweden). * Copyright (c) 2004 - 2012 Daniel Stenberg * All rights reserved. @@ -51,7 +51,7 @@ #include "ftp.h" #include "curl_gssapi.h" #include "sendf.h" -#include "krb4.h" +#include "curl_sec.h" #include "curl_memory.h" #include "warnless.h" diff --git a/plugins/FTPFileYM/curl/lib/ldap.c b/plugins/FTPFileYM/curl/lib/ldap.c index 833ffa4453..c2fa1735fa 100644 --- a/plugins/FTPFileYM/curl/lib/ldap.c +++ b/plugins/FTPFileYM/curl/lib/ldap.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2012, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2013, 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 @@ -77,13 +77,16 @@ /* Use our own implementation. */ typedef struct { - char *lud_host; - int lud_port; - char *lud_dn; - char **lud_attrs; - int lud_scope; - char *lud_filter; - char **lud_exts; + char *lud_host; + int lud_port; + char *lud_dn; + char **lud_attrs; + int lud_scope; + char *lud_filter; + char **lud_exts; + size_t lud_attrs_dups; /* how many were dup'ed, this field is not in the + "real" struct so can only be used in code + without HAVE_LDAP_URL_PARSE defined */ } CURL_LDAPURLDesc; #undef LDAPURLDesc @@ -260,7 +263,7 @@ static CURLcode Curl_ldap(struct connectdata *conn, bool *done) } server = ldapssl_init(conn->host.name, (int)conn->port, 1); if(server == NULL) { - failf(data, "LDAP local: Cannot connect to %s:%hu", + failf(data, "LDAP local: Cannot connect to %s:%ld", conn->host.name, conn->port); status = CURLE_COULDNT_CONNECT; goto quit; @@ -301,7 +304,7 @@ static CURLcode Curl_ldap(struct connectdata *conn, bool *done) } server = ldap_init(conn->host.name, (int)conn->port); if(server == NULL) { - failf(data, "LDAP local: Cannot connect to %s:%hu", + failf(data, "LDAP local: Cannot connect to %s:%ld", conn->host.name, conn->port); status = CURLE_COULDNT_CONNECT; goto quit; @@ -337,7 +340,7 @@ static CURLcode Curl_ldap(struct connectdata *conn, bool *done) else { server = ldap_init(conn->host.name, (int)conn->port); if(server == NULL) { - failf(data, "LDAP local: Cannot connect to %s:%hu", + failf(data, "LDAP local: Cannot connect to %s:%ld", conn->host.name, conn->port); status = CURLE_COULDNT_CONNECT; goto quit; @@ -539,19 +542,15 @@ static bool unescape_elements (void *data, LDAPURLDesc *ludp) if(ludp->lud_filter) { ludp->lud_filter = curl_easy_unescape(data, ludp->lud_filter, 0, NULL); if(!ludp->lud_filter) - return (FALSE); + return FALSE; } for(i = 0; ludp->lud_attrs && ludp->lud_attrs[i]; i++) { - ludp->lud_attrs[i] = curl_easy_unescape(data, ludp->lud_attrs[i], 0, NULL); + ludp->lud_attrs[i] = curl_easy_unescape(data, ludp->lud_attrs[i], + 0, NULL); if(!ludp->lud_attrs[i]) - return (FALSE); - } - - for(i = 0; ludp->lud_exts && ludp->lud_exts[i]; i++) { - ludp->lud_exts[i] = curl_easy_unescape(data, ludp->lud_exts[i], 0, NULL); - if(!ludp->lud_exts[i]) - return (FALSE); + return FALSE; + ludp->lud_attrs_dups++; } if(ludp->lud_dn) { @@ -637,8 +636,9 @@ static int _ldap_url_parse2 (const struct connectdata *conn, LDAPURLDesc *ludp) if(*p && *p != '?') { ludp->lud_scope = str2scope(p); - if(ludp->lud_scope == -1) + if(ludp->lud_scope == -1) { return LDAP_INVALID_SYNTAX; + } LDAP_TRACE (("scope %d\n", ludp->lud_scope)); } @@ -651,25 +651,13 @@ static int _ldap_url_parse2 (const struct connectdata *conn, LDAPURLDesc *ludp) q = strchr(p, '?'); if(q) *q++ = '\0'; - if(!*p) + if(!*p) { return LDAP_INVALID_SYNTAX; + } ludp->lud_filter = p; LDAP_TRACE (("filter '%s'\n", ludp->lud_filter)); - p = q; - if(!p) - goto success; - - /* parse extensions - */ - ludp->lud_exts = split_str(p); - if(!ludp->lud_exts) - return LDAP_NO_MEMORY; - - for(i = 0; ludp->lud_exts[i]; i++) - LDAP_TRACE (("exts[%d] '%s'\n", i, ludp->lud_exts[i])); - success: if(!unescape_elements(conn->data, ludp)) return LDAP_NO_MEMORY; @@ -697,7 +685,7 @@ static int _ldap_url_parse (const struct connectdata *conn, static void _ldap_free_urldesc (LDAPURLDesc *ludp) { - int i; + size_t i; if(!ludp) return; @@ -709,16 +697,11 @@ static void _ldap_free_urldesc (LDAPURLDesc *ludp) free(ludp->lud_filter); if(ludp->lud_attrs) { - for(i = 0; ludp->lud_attrs[i]; i++) + for(i = 0; i < ludp->lud_attrs_dups; i++) free(ludp->lud_attrs[i]); free(ludp->lud_attrs); } - if(ludp->lud_exts) { - for(i = 0; ludp->lud_exts[i]; i++) - free(ludp->lud_exts[i]); - free(ludp->lud_exts); - } free (ludp); } #endif /* !HAVE_LDAP_URL_PARSE */ diff --git a/plugins/FTPFileYM/curl/lib/md5.c b/plugins/FTPFileYM/curl/lib/md5.c index 57efa431b5..d8344dbe43 100644 --- a/plugins/FTPFileYM/curl/lib/md5.c +++ b/plugins/FTPFileYM/curl/lib/md5.c @@ -90,16 +90,36 @@ static void MD5_Final(unsigned char digest[16], MD5_CTX * ctx) # include <md5.h> # endif -#elif defined(__MAC_10_4) || defined(__IPHONE_5_0) +#elif (defined(__MAC_OS_X_VERSION_MAX_ALLOWED) && \ + (__MAC_OS_X_VERSION_MAX_ALLOWED >= 1040)) || \ + (defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && \ + (__IPHONE_OS_VERSION_MAX_ALLOWED >= 20000)) /* For Apple operating systems: CommonCrypto has the functions we need. - The library's headers are even backward-compatible with OpenSSL's - headers as long as we define COMMON_DIGEST_FOR_OPENSSL first. + These functions are available on Tiger and later, as well as iOS 2.0 + and later. If you're building for an older cat, well, sorry. - These functions are available on Tiger and later, as well as iOS 5.0 - and later. If you're building for an older cat, well, sorry. */ -# define COMMON_DIGEST_FOR_OPENSSL + Declaring the functions as static like this seems to be a bit more + reliable than defining COMMON_DIGEST_FOR_OPENSSL on older cats. */ # include <CommonCrypto/CommonDigest.h> +# define MD5_CTX CC_MD5_CTX + +static void MD5_Init(MD5_CTX *ctx) +{ + CC_MD5_Init(ctx); +} + +static void MD5_Update(MD5_CTX *ctx, + const unsigned char *input, + unsigned int inputLen) +{ + CC_MD5_Update(ctx, input, inputLen); +} + +static void MD5_Final(unsigned char digest[16], MD5_CTX *ctx) +{ + CC_MD5_Final(digest, ctx); +} #elif defined(_WIN32) diff --git a/plugins/FTPFileYM/curl/lib/memdebug.c b/plugins/FTPFileYM/curl/lib/memdebug.c index 4d5f44d2f0..7d68af874c 100644 --- a/plugins/FTPFileYM/curl/lib/memdebug.c +++ b/plugins/FTPFileYM/curl/lib/memdebug.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2012, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2013, 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 @@ -185,8 +185,10 @@ void *curl_domalloc(size_t wantedsize, int line, const char *source) } if(source) - curl_memlog("MEM %s:%d malloc(%zd) = %p\n", - source, line, wantedsize, mem ? mem->mem : 0); + curl_memlog("MEM %s:%d malloc(%zu) = %p\n", + source, line, wantedsize, + mem ? (void *)mem->mem : (void *)0); + return (mem ? mem->mem : NULL); } @@ -212,7 +214,9 @@ void *curl_docalloc(size_t wanted_elements, size_t wanted_size, if(source) curl_memlog("MEM %s:%d calloc(%zu,%zu) = %p\n", - source, line, wanted_elements, wanted_size, mem?mem->mem:0); + source, line, wanted_elements, wanted_size, + mem ? (void *)mem->mem : (void *)0); + return (mem ? mem->mem : NULL); } @@ -234,10 +238,36 @@ char *curl_dostrdup(const char *str, int line, const char *source) if(source) curl_memlog("MEM %s:%d strdup(%p) (%zu) = %p\n", - source, line, str, len, mem); + source, line, (void *)str, len, (void *)mem); + + return mem; +} + +#if defined(WIN32) && defined(UNICODE) +wchar_t *curl_dowcsdup(const wchar_t *str, int line, const char *source) +{ + wchar_t *mem; + size_t wsiz, bsiz; + + assert(str != NULL); + + if(countcheck("wcsdup", line, source)) + return NULL; + + wsiz = wcslen(str) + 1; + bsiz = wsiz * sizeof(wchar_t); + + mem = curl_domalloc(bsiz, 0, NULL); /* NULL prevents logging */ + if(mem) + memcpy(mem, str, bsiz); + + if(source) + curl_memlog("MEM %s:%d wcsdup(%p) (%zu) = %p\n", + source, line, (void *)str, bsiz, (void *)mem); return mem; } +#endif /* We provide a realloc() that accepts a NULL as pointer, which then performs a malloc(). In order to work with ares. */ @@ -269,7 +299,8 @@ void *curl_dorealloc(void *ptr, size_t wantedsize, mem = (Curl_crealloc)(mem, size); if(source) curl_memlog("MEM %s:%d realloc(%p, %zu) = %p\n", - source, line, ptr, wantedsize, mem?mem->mem:NULL); + source, line, (void *)ptr, wantedsize, + mem ? (void *)mem->mem : (void *)0); if(mem) { mem->size = wantedsize; @@ -304,7 +335,7 @@ void curl_dofree(void *ptr, int line, const char *source) (Curl_cfree)(mem); if(source) - curl_memlog("MEM %s:%d free(%p)\n", source, line, ptr); + curl_memlog("MEM %s:%d free(%p)\n", source, line, (void *)ptr); } curl_socket_t curl_socket(int domain, int type, int protocol, @@ -317,8 +348,10 @@ curl_socket_t curl_socket(int domain, int type, int protocol, "FD %s:%d socket() = %zd\n" ; curl_socket_t sockfd = socket(domain, type, protocol); + if(source && (sockfd != CURL_SOCKET_BAD)) curl_memlog(fmt, source, line, sockfd); + return sockfd; } @@ -334,8 +367,10 @@ int curl_socketpair(int domain, int type, int protocol, "FD %s:%d socketpair() = %zd %zd\n" ; int res = socketpair(domain, type, protocol, socket_vector); + if(source && (0 == res)) curl_memlog(fmt, source, line, socket_vector[0], socket_vector[1]); + return res; } #endif @@ -351,9 +386,12 @@ curl_socket_t curl_accept(curl_socket_t s, void *saddr, void *saddrlen, struct sockaddr *addr = (struct sockaddr *)saddr; curl_socklen_t *addrlen = (curl_socklen_t *)saddrlen; + curl_socket_t sockfd = accept(s, addr, addrlen); + if(source && (sockfd != CURL_SOCKET_BAD)) curl_memlog(fmt, source, line, sockfd); + return sockfd; } @@ -382,9 +420,11 @@ FILE *curl_fopen(const char *file, const char *mode, int line, const char *source) { FILE *res=fopen(file, mode); + if(source) curl_memlog("FILE %s:%d fopen(\"%s\",\"%s\") = %p\n", - source, line, file, mode, res); + source, line, file, mode, (void *)res); + return res; } @@ -393,9 +433,11 @@ FILE *curl_fdopen(int filedes, const char *mode, int line, const char *source) { FILE *res=fdopen(filedes, mode); + if(source) curl_memlog("FILE %s:%d fdopen(\"%d\",\"%s\") = %p\n", - source, line, filedes, mode, res); + source, line, filedes, mode, (void *)res); + return res; } #endif @@ -407,9 +449,11 @@ int curl_fclose(FILE *file, int line, const char *source) assert(file != NULL); res=fclose(file); + if(source) curl_memlog("FILE %s:%d fclose(%p)\n", - source, line, file); + source, line, (void *)file); + return res; } diff --git a/plugins/FTPFileYM/curl/lib/memdebug.h b/plugins/FTPFileYM/curl/lib/memdebug.h index fbeb61de5d..bd565c8dcf 100644 --- a/plugins/FTPFileYM/curl/lib/memdebug.h +++ b/plugins/FTPFileYM/curl/lib/memdebug.h @@ -8,7 +8,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2012, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2013, 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 @@ -46,6 +46,11 @@ CURL_EXTERN void *curl_dorealloc(void *ptr, size_t size, int line, const char *source); CURL_EXTERN void curl_dofree(void *ptr, int line, const char *source); CURL_EXTERN char *curl_dostrdup(const char *str, int line, const char *source); +#if defined(WIN32) && defined(UNICODE) +CURL_EXTERN wchar_t *curl_dowcsdup(const wchar_t *str, int line, + const char *source); +#endif + CURL_EXTERN void curl_memdebug(const char *logname); CURL_EXTERN void curl_memlimit(long limit); CURL_EXTERN void curl_memlog(const char *format, ...); @@ -84,6 +89,20 @@ CURL_EXTERN int curl_fclose(FILE *file, int line, const char *source); #define realloc(ptr,size) curl_dorealloc(ptr, size, __LINE__, __FILE__) #define free(ptr) curl_dofree(ptr, __LINE__, __FILE__) +#ifdef WIN32 +# ifdef UNICODE +# undef wcsdup +# define wcsdup(ptr) curl_dowcsdup(ptr, __LINE__, __FILE__) +# undef _wcsdup +# define _wcsdup(ptr) curl_dowcsdup(ptr, __LINE__, __FILE__) +# undef _tcsdup +# define _tcsdup(ptr) curl_dowcsdup(ptr, __LINE__, __FILE__) +# else +# undef _tcsdup +# define _tcsdup(ptr) curl_dostrdup(ptr, __LINE__, __FILE__) +# endif +#endif + #define socket(domain,type,protocol)\ curl_socket(domain,type,protocol,__LINE__,__FILE__) #undef accept /* for those with accept as a macro */ diff --git a/plugins/FTPFileYM/curl/lib/mprintf.c b/plugins/FTPFileYM/curl/lib/mprintf.c index b5b81536ab..8f392c7f29 100644 --- a/plugins/FTPFileYM/curl/lib/mprintf.c +++ b/plugins/FTPFileYM/curl/lib/mprintf.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1999 - 2011, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1999 - 2013, 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 @@ -203,101 +203,6 @@ static int dprintf_IsQualifierNoDollar(char c) } } -#ifdef DPRINTF_DEBUG2 -static void dprintf_Pass1Report(va_stack_t *vto, int max) -{ - int i; - char buffer[256]; - int bit; - int flags; - - for(i=0; i<max; i++) { - char *type; - switch(vto[i].type) { - case FORMAT_UNKNOWN: - type = "unknown"; - break; - case FORMAT_STRING: - type ="string"; - break; - case FORMAT_PTR: - type ="pointer"; - break; - case FORMAT_INT: - type = "int"; - break; - case FORMAT_INTPTR: - type = "intptr"; - break; - case FORMAT_LONG: - type = "long"; - break; - case FORMAT_LONGLONG: - type = "long long"; - break; - case FORMAT_DOUBLE: - type = "double"; - break; - case FORMAT_LONGDOUBLE: - type = "long double"; - break; - } - - - buffer[0]=0; - - for(bit=0; bit<31; bit++) { - flags = vto[i].flags & (1<<bit); - - if(flags & FLAGS_SPACE) - strcat(buffer, "space "); - else if(flags & FLAGS_SHOWSIGN) - strcat(buffer, "plus "); - else if(flags & FLAGS_LEFT) - strcat(buffer, "left "); - else if(flags & FLAGS_ALT) - strcat(buffer, "alt "); - else if(flags & FLAGS_SHORT) - strcat(buffer, "short "); - else if(flags & FLAGS_LONG) - strcat(buffer, "long "); - else if(flags & FLAGS_LONGLONG) - strcat(buffer, "longlong "); - else if(flags & FLAGS_LONGDOUBLE) - strcat(buffer, "longdouble "); - else if(flags & FLAGS_PAD_NIL) - strcat(buffer, "padnil "); - else if(flags & FLAGS_UNSIGNED) - strcat(buffer, "unsigned "); - else if(flags & FLAGS_OCTAL) - strcat(buffer, "octal "); - else if(flags & FLAGS_HEX) - strcat(buffer, "hex "); - else if(flags & FLAGS_UPPER) - strcat(buffer, "upper "); - else if(flags & FLAGS_WIDTH) - strcat(buffer, "width "); - else if(flags & FLAGS_WIDTHPARAM) - strcat(buffer, "widthparam "); - else if(flags & FLAGS_PREC) - strcat(buffer, "precision "); - else if(flags & FLAGS_PRECPARAM) - strcat(buffer, "precparam "); - else if(flags & FLAGS_CHAR) - strcat(buffer, "char "); - else if(flags & FLAGS_FLOATE) - strcat(buffer, "floate "); - else if(flags & FLAGS_FLOATG) - strcat(buffer, "floatg "); - } - printf("REPORT: %d. %s [%s]\n", i, type, buffer); - - } - - -} -#endif - /****************************************************************** * * Pass 1: @@ -475,11 +380,11 @@ static long dprintf_Pass1(const char *format, va_stack_t *vto, char **endpos, break; case 'x': vto[i].type = FORMAT_INT; - flags |= FLAGS_HEX; + flags |= FLAGS_HEX|FLAGS_UNSIGNED; break; case 'X': vto[i].type = FORMAT_INT; - flags |= FLAGS_HEX|FLAGS_UPPER; + flags |= FLAGS_HEX|FLAGS_UPPER|FLAGS_UNSIGNED; break; case 'c': vto[i].type = FORMAT_INT; @@ -537,10 +442,6 @@ static long dprintf_Pass1(const char *format, va_stack_t *vto, char **endpos, } } -#ifdef DPRINTF_DEBUG2 - dprintf_Pass1Report(vto, max_param); -#endif - /* Read the arg list parameters into our data list */ for(i=0; i<max_param; i++) { if((i + 1 < max_param) && (vto[i + 1].type == FORMAT_WIDTH)) { @@ -732,23 +633,23 @@ static int dprintf_formatf( OUTCHAR(' '); break; } - if(p->flags & FLAGS_UNSIGNED) { - /* Decimal unsigned integer. */ - base = 10; - goto unsigned_number; - } if(p->flags & FLAGS_OCTAL) { /* Octal unsigned integer. */ base = 8; goto unsigned_number; } - if(p->flags & FLAGS_HEX) { + else if(p->flags & FLAGS_HEX) { /* Hexadecimal unsigned integer. */ digits = (p->flags & FLAGS_UPPER)? upper_digits : lower_digits; base = 16; goto unsigned_number; } + else if(p->flags & FLAGS_UNSIGNED) { + /* Decimal unsigned integer. */ + base = 10; + goto unsigned_number; + } /* Decimal integer. */ base = 10; @@ -919,7 +820,7 @@ static int dprintf_formatf( case FORMAT_DOUBLE: { char formatbuf[32]="%"; - char *fptr; + char *fptr = &formatbuf[1]; size_t left = sizeof(formatbuf)-strlen(formatbuf); int len; @@ -936,15 +837,15 @@ static int dprintf_formatf( prec = (long)vto[p->precision].data.num.as_signed; if(p->flags & FLAGS_LEFT) - strcat(formatbuf, "-"); + *fptr++ = '-'; if(p->flags & FLAGS_SHOWSIGN) - strcat(formatbuf, "+"); + *fptr++ = '+'; if(p->flags & FLAGS_SPACE) - strcat(formatbuf, " "); + *fptr++ = ' '; if(p->flags & FLAGS_ALT) - strcat(formatbuf, "#"); + *fptr++ = '#'; - fptr=&formatbuf[strlen(formatbuf)]; + *fptr = 0; if(width >= 0) { /* RECURSIVE USAGE */ @@ -969,8 +870,8 @@ static int dprintf_formatf( *fptr = 0; /* and a final zero termination */ - /* NOTE NOTE NOTE!! Not all sprintf() implementations returns number - of output characters */ + /* NOTE NOTE NOTE!! Not all sprintf implementations return number of + output characters */ (sprintf)(work, formatbuf, p->data.dnum); for(fptr=work; *fptr; fptr++) diff --git a/plugins/FTPFileYM/curl/lib/multi.c b/plugins/FTPFileYM/curl/lib/multi.c index fa0afb9f83..e723a3ebf0 100644 --- a/plugins/FTPFileYM/curl/lib/multi.c +++ b/plugins/FTPFileYM/curl/lib/multi.c @@ -40,6 +40,7 @@ #include "conncache.h" #include "bundles.h" #include "multihandle.h" +#include "pipeline.h" #define _MPRINTF_REPLACE /* use our functions only */ #include <curl/mprintf.h> @@ -57,6 +58,7 @@ #define CURL_SOCKET_HASH_TABLE_SIZE 911 #endif +#define CURL_CONNECTION_HASH_SIZE 97 #define CURL_MULTI_HANDLE 0x000bab1e @@ -66,25 +68,21 @@ ((x) && (((struct SessionHandle *)(x))->magic == CURLEASY_MAGIC_NUMBER)) static void singlesocket(struct Curl_multi *multi, - struct Curl_one_easy *easy); + struct SessionHandle *data); static int update_timer(struct Curl_multi *multi); -static CURLcode addHandleToSendOrPendPipeline(struct SessionHandle *handle, - struct connectdata *conn); -static int checkPendPipeline(struct connectdata *conn); -static void moveHandleFromSendToRecvPipeline(struct SessionHandle *handle, - struct connectdata *conn); -static void moveHandleFromRecvToDonePipeline(struct SessionHandle *handle, - struct connectdata *conn); static bool isHandleAtHead(struct SessionHandle *handle, struct curl_llist *pipeline); static CURLMcode add_next_timeout(struct timeval now, struct Curl_multi *multi, struct SessionHandle *d); +static CURLMcode multi_timeout(struct Curl_multi *multi, + long *timeout_ms); #ifdef DEBUGBUILD static const char * const statename[]={ "INIT", + "CONNECT_PEND", "CONNECT", "WAITRESOLVE", "WAITCONNECT", @@ -107,7 +105,7 @@ static const char * const statename[]={ static void multi_freetimeout(void *a, void *b); /* always use this function to change state, to make debugging easier */ -static void mstate(struct Curl_one_easy *easy, CURLMstate state +static void mstate(struct SessionHandle *data, CURLMstate state #ifdef DEBUGBUILD , int lineno #endif @@ -116,29 +114,29 @@ static void mstate(struct Curl_one_easy *easy, CURLMstate state #ifdef DEBUGBUILD long connection_id = -5000; #endif - CURLMstate oldstate = easy->state; + CURLMstate oldstate = data->mstate; if(oldstate == state) /* don't bother when the new state is the same as the old state */ return; - easy->state = state; + data->mstate = state; #ifdef DEBUGBUILD - if(easy->easy_conn) { - if(easy->state > CURLM_STATE_CONNECT && - easy->state < CURLM_STATE_COMPLETED) - connection_id = easy->easy_conn->connection_id; + if(data->mstate >= CURLM_STATE_CONNECT_PEND && + data->mstate < CURLM_STATE_COMPLETED) { + if(data->easy_conn) + connection_id = data->easy_conn->connection_id; - infof(easy->easy_handle, + infof(data, "STATE: %s => %s handle %p; line %d (connection #%ld) \n", - statename[oldstate], statename[easy->state], - (char *)easy, lineno, connection_id); + statename[oldstate], statename[data->mstate], + (void *)data, lineno, connection_id); } #endif if(state == CURLM_STATE_COMPLETED) /* changing to COMPLETED means there's one less easy handle 'alive' */ - easy->easy_handle->multi->num_alive--; + data->multi->num_alive--; } #ifndef DEBUGBUILD @@ -218,16 +216,16 @@ static void sh_freeentry(void *freethis) free(p); } -static size_t fd_key_compare(void*k1, size_t k1_len, void*k2, size_t k2_len) +static size_t fd_key_compare(void *k1, size_t k1_len, void *k2, size_t k2_len) { (void) k1_len; (void) k2_len; - return (*((int* ) k1)) == (*((int* ) k2)); + return (*((int *) k1)) == (*((int *) k2)); } -static size_t hash_fd(void* key, size_t key_length, size_t slots_num) +static size_t hash_fd(void *key, size_t key_length, size_t slots_num) { - int fd = * ((int* ) key); + int fd = *((int *) key); (void) key_length; return (fd % (int)slots_num); @@ -251,9 +249,9 @@ static size_t hash_fd(void* key, size_t key_length, size_t slots_num) * per call." * */ -static struct curl_hash *sh_init(void) +static struct curl_hash *sh_init(int hashsize) { - return Curl_hash_alloc(CURL_SOCKET_HASH_TABLE_SIZE, hash_fd, fd_key_compare, + return Curl_hash_alloc(hashsize, hash_fd, fd_key_compare, sh_freeentry); } @@ -283,7 +281,8 @@ static void multi_freeamsg(void *a, void *b) (void)b; } -CURLM *curl_multi_init(void) +struct Curl_multi *Curl_multi_handle(int hashsize, /* socket hash */ + int chashsize) /* connection hash */ { struct Curl_multi *multi = calloc(1, sizeof(struct Curl_multi)); @@ -296,11 +295,11 @@ CURLM *curl_multi_init(void) if(!multi->hostcache) goto error; - multi->sockhash = sh_init(); + multi->sockhash = sh_init(hashsize); if(!multi->sockhash) goto error; - multi->conn_cache = Curl_conncache_init(); + multi->conn_cache = Curl_conncache_init(chashsize); if(!multi->conn_cache) goto error; @@ -308,12 +307,15 @@ CURLM *curl_multi_init(void) if(!multi->msglist) goto error; - /* Let's make the doubly-linked list a circular list. This makes - the linked list code simpler and allows inserting at the end - with less work (we didn't keep a tail pointer before). */ - multi->easy.next = &multi->easy; - multi->easy.prev = &multi->easy; + /* allocate a new easy handle to use when closing cached connections */ + multi->closure_handle = curl_easy_init(); + if(!multi->closure_handle) + goto error; + + multi->closure_handle->multi = multi; + multi->closure_handle->state.conn_cache = multi->conn_cache; + multi->max_pipeline_length = 5; return (CURLM *) multi; error: @@ -324,20 +326,25 @@ CURLM *curl_multi_init(void) multi->hostcache = NULL; Curl_conncache_destroy(multi->conn_cache); multi->conn_cache = NULL; + Curl_close(multi->closure_handle); + multi->closure_handle = NULL; free(multi); return NULL; } +CURLM *curl_multi_init(void) +{ + return Curl_multi_handle(CURL_SOCKET_HASH_TABLE_SIZE, + CURL_CONNECTION_HASH_SIZE); +} + CURLMcode curl_multi_add_handle(CURLM *multi_handle, CURL *easy_handle) { struct curl_llist *timeoutlist; - struct Curl_one_easy *easy; struct Curl_multi *multi = (struct Curl_multi *)multi_handle; struct SessionHandle *data = (struct SessionHandle *)easy_handle; - struct SessionHandle *new_closure = NULL; - struct curl_hash *hostcache = NULL; /* First, make some basic checks that the CURLM handle is a good handle */ if(!GOOD_MULTI_HANDLE(multi)) @@ -347,110 +354,73 @@ CURLMcode curl_multi_add_handle(CURLM *multi_handle, if(!GOOD_EASY_HANDLE(easy_handle)) return CURLM_BAD_EASY_HANDLE; - /* Prevent users from adding same easy handle more than - once and prevent adding to more than one multi stack */ + /* Prevent users from adding same easy handle more than once and prevent + adding to more than one multi stack */ if(data->multi) - /* possibly we should create a new unique error code for this condition */ - return CURLM_BAD_EASY_HANDLE; + return CURLM_ADDED_ALREADY; /* Allocate and initialize timeout list for easy handle */ timeoutlist = Curl_llist_alloc(multi_freetimeout); if(!timeoutlist) return CURLM_OUT_OF_MEMORY; - /* Allocate new node for the doubly-linked circular list of - Curl_one_easy structs that holds pointers to easy handles */ - easy = calloc(1, sizeof(struct Curl_one_easy)); - if(!easy) { - Curl_llist_destroy(timeoutlist, NULL); - return CURLM_OUT_OF_MEMORY; - } - - /* In case multi handle has no hostcache yet, allocate one */ - if(!multi->hostcache) { - hostcache = Curl_mk_dnscache(); - if(!hostcache) { - free(easy); - Curl_llist_destroy(timeoutlist, NULL); - return CURLM_OUT_OF_MEMORY; - } - } - - /* In case multi handle has no closure_handle yet, allocate - a new easy handle to use when closing cached connections */ - if(!multi->closure_handle) { - new_closure = (struct SessionHandle *)curl_easy_init(); - if(!new_closure) { - Curl_hash_destroy(hostcache); - free(easy); - Curl_llist_destroy(timeoutlist, NULL); - return CURLM_OUT_OF_MEMORY; - } - } - /* - ** No failure allowed in this function beyond this point. And - ** no modification of easy nor multi handle allowed before this - ** except for potential multi's connection cache growing which - ** won't be undone in this function no matter what. - */ - - /* In case a new closure handle has been initialized above, it - is associated now with the multi handle which lacked one. */ - if(new_closure) { - multi->closure_handle = new_closure; - Curl_easy_addmulti(multi->closure_handle, multi_handle); - multi->closure_handle->state.conn_cache = multi->conn_cache; - } - - /* In case hostcache has been allocated above, - it is associated now with the multi handle. */ - if(hostcache) - multi->hostcache = hostcache; + * No failure allowed in this function beyond this point. And no + * modification of easy nor multi handle allowed before this except for + * potential multi's connection cache growing which won't be undone in this + * function no matter what. + */ /* Make easy handle use timeout list initialized above */ data->state.timeoutlist = timeoutlist; timeoutlist = NULL; /* set the easy handle */ - easy->easy_handle = data; - multistate(easy, CURLM_STATE_INIT); - - /* set the back pointer to one_easy to assist in removal */ - easy->easy_handle->multi_pos = easy; - + multistate(data, CURLM_STATE_INIT); + + if((data->set.global_dns_cache) && + (data->dns.hostcachetype != HCACHE_GLOBAL)) { + /* global dns cache was requested but still isn't */ + struct curl_hash *global = Curl_global_host_cache_init(); + if(global) { + /* only do this if the global cache init works */ + data->dns.hostcache = global; + data->dns.hostcachetype = HCACHE_GLOBAL; + } + } /* for multi interface connections, we share DNS cache automatically if the easy handle's one is currently not set. */ - if(!easy->easy_handle->dns.hostcache || - (easy->easy_handle->dns.hostcachetype == HCACHE_NONE)) { - easy->easy_handle->dns.hostcache = multi->hostcache; - easy->easy_handle->dns.hostcachetype = HCACHE_MULTI; + else if(!data->dns.hostcache || + (data->dns.hostcachetype == HCACHE_NONE)) { + data->dns.hostcache = multi->hostcache; + data->dns.hostcachetype = HCACHE_MULTI; } /* Point to the multi's connection cache */ - easy->easy_handle->state.conn_cache = multi->conn_cache; + data->state.conn_cache = multi->conn_cache; /* This adds the new entry at the 'end' of the doubly-linked circular - list of Curl_one_easy structs to try and maintain a FIFO queue so + list of SessionHandle structs to try and maintain a FIFO queue so the pipelined requests are in order. */ - /* We add this new entry last in the list. We make our 'next' point to the - 'first' struct and our 'prev' point to the previous 'prev' */ - easy->next = &multi->easy; - easy->prev = multi->easy.prev; - - /* make 'easy' the last node in the chain */ - multi->easy.prev = easy; + /* We add this new entry last in the list. */ - /* if there was a prev node, make sure its 'next' pointer links to - the new node */ - easy->prev->next = easy; + data->next = NULL; /* end of the line */ + if(multi->easyp) { + struct SessionHandle *last = multi->easylp; + last->next = data; + data->prev = last; + multi->easylp = data; /* the new last node */ + } + else { + /* first node, make both prev and next be NULL! */ + data->next = NULL; + data->prev = NULL; + multi->easylp = multi->easyp = data; /* both first and last */ + } /* make the SessionHandle refer back to this multi handle */ - Curl_easy_addmulti(easy_handle, multi_handle); - - /* make the SessionHandle struct refer back to this struct */ - easy->easy_handle->set.one_easy = easy; + data->multi = multi_handle; /* Set the timeout for this handle to expire really soon so that it will be taken care of even when this handle is added in the midst of operation @@ -458,7 +428,7 @@ CURLMcode curl_multi_add_handle(CURLM *multi_handle, sockets that time-out or have actions will be dealt with. Since this handle has no action yet, we make sure it times out to get things to happen. */ - Curl_expire(easy->easy_handle, 1); + Curl_expire(data, 1); /* increase the node-counter */ multi->num_easy++; @@ -494,7 +464,7 @@ static void debug_print_sock_hash(void *p) struct Curl_sh_entry *sh = (struct Curl_sh_entry *)p; fprintf(stderr, " [easy %p/magic %x/socket %d]", - (void *)sh->easy, sh->easy->magic, (int)sh->socket); + (void *)sh->data, sh->data->magic, (int)sh->socket); } #endif @@ -502,8 +472,8 @@ CURLMcode curl_multi_remove_handle(CURLM *multi_handle, CURL *curl_handle) { struct Curl_multi *multi=(struct Curl_multi *)multi_handle; - struct Curl_one_easy *easy; - struct SessionHandle *data = curl_handle; + struct SessionHandle *easy = curl_handle; + struct SessionHandle *data = easy; /* First, make some basic checks that the CURLM handle is a good handle */ if(!GOOD_MULTI_HANDLE(multi)) @@ -513,13 +483,14 @@ CURLMcode curl_multi_remove_handle(CURLM *multi_handle, if(!GOOD_EASY_HANDLE(curl_handle)) return CURLM_BAD_EASY_HANDLE; - /* pick-up from the 'curl_handle' the kept position in the list */ - easy = data->multi_pos; + /* Prevent users from trying to remove same easy handle more than once */ + if(!data->multi) + return CURLM_OK; /* it is already removed so let's say it is fine! */ if(easy) { - bool premature = (easy->state < CURLM_STATE_COMPLETED) ? TRUE : FALSE; - bool easy_owns_conn = (easy->easy_conn && - (easy->easy_conn->data == easy->easy_handle)) ? + bool premature = (data->mstate < CURLM_STATE_COMPLETED) ? TRUE : FALSE; + bool easy_owns_conn = (data->easy_conn && + (data->easy_conn->data == easy)) ? TRUE : FALSE; /* If the 'state' is not INIT or COMPLETED, we might need to do something @@ -529,24 +500,24 @@ CURLMcode curl_multi_remove_handle(CURLM *multi_handle, alive connections when this is removed */ multi->num_alive--; - if(easy->easy_conn && - (easy->easy_conn->send_pipe->size + - easy->easy_conn->recv_pipe->size > 1) && - easy->state > CURLM_STATE_WAITDO && - easy->state < CURLM_STATE_COMPLETED) { + if(data->easy_conn && + (data->easy_conn->send_pipe->size + + data->easy_conn->recv_pipe->size > 1) && + data->mstate > CURLM_STATE_WAITDO && + data->mstate < CURLM_STATE_COMPLETED) { /* If the handle is in a pipeline and has started sending off its request but not received its response yet, we need to close connection. */ - easy->easy_conn->bits.close = TRUE; + data->easy_conn->bits.close = TRUE; /* Set connection owner so that Curl_done() closes it. We can sefely do this here since connection is killed. */ - easy->easy_conn->data = easy->easy_handle; + data->easy_conn->data = easy; } - /* The timer must be shut down before easy->multi is set to NULL, + /* The timer must be shut down before data->multi is set to NULL, else the timenode will remain in the splay tree after curl_easy_cleanup is called. */ - Curl_expire(easy->easy_handle, 0); + Curl_expire(data, 0); /* destroy the timeout list that is held in the easy handle */ if(data->state.timeoutlist) { @@ -554,13 +525,13 @@ CURLMcode curl_multi_remove_handle(CURLM *multi_handle, data->state.timeoutlist = NULL; } - if(easy->easy_handle->dns.hostcachetype == HCACHE_MULTI) { + if(data->dns.hostcachetype == HCACHE_MULTI) { /* stop using the multi handle's DNS cache */ - easy->easy_handle->dns.hostcache = NULL; - easy->easy_handle->dns.hostcachetype = HCACHE_NONE; + data->dns.hostcache = NULL; + data->dns.hostcachetype = HCACHE_NONE; } - if(easy->easy_conn) { + if(data->easy_conn) { /* we must call Curl_done() here (if we still "own it") so that we don't leave a half-baked one around */ @@ -571,31 +542,30 @@ CURLMcode curl_multi_remove_handle(CURLM *multi_handle, Note that this ignores the return code simply because there's nothing really useful to do with it anyway! */ - (void)Curl_done(&easy->easy_conn, easy->result, premature); + (void)Curl_done(&data->easy_conn, data->result, premature); } else /* Clear connection pipelines, if Curl_done above was not called */ - Curl_getoff_all_pipelines(easy->easy_handle, easy->easy_conn); + Curl_getoff_all_pipelines(data, data->easy_conn); } /* as this was using a shared connection cache we clear the pointer to that since we're not part of that multi handle anymore */ - easy->easy_handle->state.conn_cache = NULL; + data->state.conn_cache = NULL; /* change state without using multistate(), only to make singlesocket() do what we want */ - easy->state = CURLM_STATE_COMPLETED; + data->mstate = CURLM_STATE_COMPLETED; singlesocket(multi, easy); /* to let the application know what sockets that vanish with this handle */ /* Remove the association between the connection and the handle */ - if(easy->easy_conn) { - easy->easy_conn->data = NULL; - easy->easy_conn = NULL; + if(data->easy_conn) { + data->easy_conn->data = NULL; + data->easy_conn = NULL; } - Curl_easy_addmulti(easy->easy_handle, NULL); /* clear the association - to this multi handle */ + data->multi = NULL; /* clear the association to this multi handle */ { /* make sure there's no pending message in the queue sent from this easy @@ -605,7 +575,7 @@ CURLMcode curl_multi_remove_handle(CURLM *multi_handle, for(e = multi->msglist->head; e; e = e->next) { struct Curl_message *msg = e->ptr; - if(msg->extmsg.easy_handle == easy->easy_handle) { + if(msg->extmsg.easy_handle == easy) { Curl_llist_remove(multi->msglist, e, NULL); /* there can only be one from this specific handle */ break; @@ -614,20 +584,19 @@ CURLMcode curl_multi_remove_handle(CURLM *multi_handle, } /* make the previous node point to our next */ - if(easy->prev) - easy->prev->next = easy->next; - /* make our next point to our previous node */ - if(easy->next) - easy->next->prev = easy->prev; - - easy->easy_handle->set.one_easy = NULL; /* detached */ + if(data->prev) + data->prev->next = data->next; + else + multi->easyp = data->next; /* point to first node */ - /* Null the position in the controlling structure */ - easy->easy_handle->multi_pos = NULL; + /* make our next point to our previous node */ + if(data->next) + data->next->prev = data->prev; + else + multi->easylp = data->prev; /* point to last node */ /* NOTE NOTE NOTE We do not touch the easy handle here! */ - free(easy); multi->num_easy--; /* one less to care about now */ @@ -638,17 +607,14 @@ CURLMcode curl_multi_remove_handle(CURLM *multi_handle, return CURLM_BAD_EASY_HANDLE; /* twasn't found */ } -bool Curl_multi_canPipeline(const struct Curl_multi* multi) +bool Curl_multi_pipeline_enabled(const struct Curl_multi *multi) { - return multi->pipelining_enabled; + return (multi && multi->pipelining_enabled) ? TRUE : FALSE; } void Curl_multi_handlePipeBreak(struct SessionHandle *data) { - struct Curl_one_easy *one_easy = data->set.one_easy; - - if(one_easy) - one_easy->easy_conn = NULL; + data->easy_conn = NULL; } static int waitconnect_getsock(struct connectdata *conn, @@ -678,7 +644,7 @@ static int domore_getsock(struct connectdata *conn, } /* returns bitmapped flags for this handle and its sockets */ -static int multi_getsock(struct Curl_one_easy *easy, +static int multi_getsock(struct SessionHandle *data, curl_socket_t *socks, /* points to numsocks number of sockets */ int numsocks) @@ -688,16 +654,16 @@ static int multi_getsock(struct Curl_one_easy *easy, happen when this is called from curl_multi_remove_handle() => singlesocket() => multi_getsock(). */ - if(easy->easy_handle->state.pipe_broke || !easy->easy_conn) + if(data->state.pipe_broke || !data->easy_conn) return 0; - if(easy->state > CURLM_STATE_CONNECT && - easy->state < CURLM_STATE_COMPLETED) { + if(data->mstate > CURLM_STATE_CONNECT && + data->mstate < CURLM_STATE_COMPLETED) { /* Set up ownership correctly */ - easy->easy_conn->data = easy->easy_handle; + data->easy_conn->data = data; } - switch(easy->state) { + switch(data->mstate) { default: #if 0 /* switch back on these cases to get the compiler to check for all enums to be present */ @@ -715,28 +681,28 @@ static int multi_getsock(struct Curl_one_easy *easy, return 0; case CURLM_STATE_WAITRESOLVE: - return Curl_resolver_getsock(easy->easy_conn, socks, numsocks); + return Curl_resolver_getsock(data->easy_conn, socks, numsocks); case CURLM_STATE_PROTOCONNECT: - return Curl_protocol_getsock(easy->easy_conn, socks, numsocks); + return Curl_protocol_getsock(data->easy_conn, socks, numsocks); case CURLM_STATE_DO: case CURLM_STATE_DOING: - return Curl_doing_getsock(easy->easy_conn, socks, numsocks); + return Curl_doing_getsock(data->easy_conn, socks, numsocks); case CURLM_STATE_WAITPROXYCONNECT: case CURLM_STATE_WAITCONNECT: - return waitconnect_getsock(easy->easy_conn, socks, numsocks); + return waitconnect_getsock(data->easy_conn, socks, numsocks); case CURLM_STATE_DO_MORE: - return domore_getsock(easy->easy_conn, socks, numsocks); + return domore_getsock(data->easy_conn, socks, numsocks); case CURLM_STATE_DO_DONE: /* since is set after DO is completed, we switch to waiting for the same as the *PERFORM states */ case CURLM_STATE_PERFORM: case CURLM_STATE_WAITPERFORM: - return Curl_single_getsock(easy->easy_conn, socks, numsocks); + return Curl_single_getsock(data->easy_conn, socks, numsocks); } } @@ -749,7 +715,7 @@ CURLMcode curl_multi_fdset(CURLM *multi_handle, Some easy handles may not have connected to the remote host yet, and then we must make sure that is done. */ struct Curl_multi *multi=(struct Curl_multi *)multi_handle; - struct Curl_one_easy *easy; + struct SessionHandle *data; int this_max_fd=-1; curl_socket_t sockbunch[MAX_SOCKSPEREASYHANDLE]; int bitmap; @@ -759,9 +725,9 @@ CURLMcode curl_multi_fdset(CURLM *multi_handle, if(!GOOD_MULTI_HANDLE(multi)) return CURLM_BAD_HANDLE; - easy=multi->easy.next; - while(easy != &multi->easy) { - bitmap = multi_getsock(easy, sockbunch, MAX_SOCKSPEREASYHANDLE); + data=multi->easyp; + while(data) { + bitmap = multi_getsock(data, sockbunch, MAX_SOCKSPEREASYHANDLE); for(i=0; i< MAX_SOCKSPEREASYHANDLE; i++) { curl_socket_t s = CURL_SOCKET_BAD; @@ -783,7 +749,7 @@ CURLMcode curl_multi_fdset(CURLM *multi_handle, } } - easy = easy->next; /* check next handle */ + data = data->next; /* check next handle */ } *max_fd = this_max_fd; @@ -798,20 +764,29 @@ CURLMcode curl_multi_wait(CURLM *multi_handle, int *ret) { struct Curl_multi *multi=(struct Curl_multi *)multi_handle; - struct Curl_one_easy *easy; + struct SessionHandle *data; curl_socket_t sockbunch[MAX_SOCKSPEREASYHANDLE]; int bitmap; unsigned int i; - unsigned int nfds = extra_nfds; + unsigned int nfds = 0; + unsigned int curlfds; struct pollfd *ufds = NULL; + long timeout_internal; if(!GOOD_MULTI_HANDLE(multi)) return CURLM_BAD_HANDLE; + /* If the internally desired timeout is actually shorter than requested from + the outside, then use the shorter time! But only if the internal timer + is actually larger than 0! */ + (void)multi_timeout(multi, &timeout_internal); + if((timeout_internal > 0) && (timeout_internal < (long)timeout_ms)) + timeout_ms = (int)timeout_internal; + /* Count up how many fds we have from the multi handle */ - easy=multi->easy.next; - while(easy != &multi->easy) { - bitmap = multi_getsock(easy, sockbunch, MAX_SOCKSPEREASYHANDLE); + data=multi->easyp; + while(data) { + bitmap = multi_getsock(data, sockbunch, MAX_SOCKSPEREASYHANDLE); for(i=0; i< MAX_SOCKSPEREASYHANDLE; i++) { curl_socket_t s = CURL_SOCKET_BAD; @@ -829,9 +804,12 @@ CURLMcode curl_multi_wait(CURLM *multi_handle, } } - easy = easy->next; /* check next handle */ + data = data->next; /* check next handle */ } + curlfds = nfds; /* number of internal file descriptors */ + nfds += extra_nfds; /* add the externally provided ones */ + if(nfds) { ufds = malloc(nfds * sizeof(struct pollfd)); if(!ufds) @@ -839,32 +817,37 @@ CURLMcode curl_multi_wait(CURLM *multi_handle, } nfds = 0; - /* Add the curl handles to our pollfds first */ - easy=multi->easy.next; - while(easy != &multi->easy) { - bitmap = multi_getsock(easy, sockbunch, MAX_SOCKSPEREASYHANDLE); + /* only do the second loop if we found descriptors in the first stage run + above */ - for(i=0; i< MAX_SOCKSPEREASYHANDLE; i++) { - curl_socket_t s = CURL_SOCKET_BAD; + if(curlfds) { + /* Add the curl handles to our pollfds first */ + data=multi->easyp; + while(data) { + bitmap = multi_getsock(data, sockbunch, MAX_SOCKSPEREASYHANDLE); - if(bitmap & GETSOCK_READSOCK(i)) { - ufds[nfds].fd = sockbunch[i]; - ufds[nfds].events = POLLIN; - ++nfds; - s = sockbunch[i]; - } - if(bitmap & GETSOCK_WRITESOCK(i)) { - ufds[nfds].fd = sockbunch[i]; - ufds[nfds].events = POLLOUT; - ++nfds; - s = sockbunch[i]; - } - if(s == CURL_SOCKET_BAD) { - break; + for(i=0; i< MAX_SOCKSPEREASYHANDLE; i++) { + curl_socket_t s = CURL_SOCKET_BAD; + + if(bitmap & GETSOCK_READSOCK(i)) { + ufds[nfds].fd = sockbunch[i]; + ufds[nfds].events = POLLIN; + ++nfds; + s = sockbunch[i]; + } + if(bitmap & GETSOCK_WRITESOCK(i)) { + ufds[nfds].fd = sockbunch[i]; + ufds[nfds].events = POLLOUT; + ++nfds; + s = sockbunch[i]; + } + if(s == CURL_SOCKET_BAD) { + break; + } } - } - easy = easy->next; /* check next handle */ + data = data->next; /* check next handle */ + } } /* Add external file descriptions from poll-like struct curl_waitfd */ @@ -880,9 +863,30 @@ CURLMcode curl_multi_wait(CURLM *multi_handle, ++nfds; } - if(nfds) + if(nfds) { /* wait... */ i = Curl_poll(ufds, nfds, timeout_ms); + + if(i) { + unsigned int j; + /* copy revents results from the poll to the curl_multi_wait poll + struct, the bit values of the actual underlying poll() implementation + may not be the same as the ones in the public libcurl API! */ + for(j = 0; j < extra_nfds; j++) { + unsigned short mask = 0; + unsigned r = ufds[curlfds + j].revents; + + if(r & POLLIN) + mask |= CURL_WAIT_POLLIN; + if(r & POLLOUT) + mask |= CURL_WAIT_POLLOUT; + if(r & POLLPRI) + mask |= CURL_WAIT_POLLPRI; + + extra_fds[j].revents = mask; + } + } + } else i = 0; @@ -894,7 +898,7 @@ CURLMcode curl_multi_wait(CURLM *multi_handle, static CURLMcode multi_runsingle(struct Curl_multi *multi, struct timeval now, - struct Curl_one_easy *easy) + struct SessionHandle *data) { struct Curl_message *msg = NULL; bool connected; @@ -904,14 +908,12 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, bool done = FALSE; CURLMcode result = CURLM_OK; struct SingleRequest *k; - struct SessionHandle *data; long timeout_ms; + int control; - if(!GOOD_EASY_HANDLE(easy->easy_handle)) + if(!GOOD_EASY_HANDLE(data)) return CURLM_BAD_EASY_HANDLE; - data = easy->easy_handle; - do { /* this is a single-iteration do-while loop just to allow a break to skip to the end of it */ @@ -921,51 +923,51 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, we're using gets cleaned up and we're left with nothing. */ if(data->state.pipe_broke) { infof(data, "Pipe broke: handle 0x%p, url = %s\n", - easy, data->state.path); + (void *)data, data->state.path); - if(easy->state < CURLM_STATE_COMPLETED) { + if(data->mstate < CURLM_STATE_COMPLETED) { /* Head back to the CONNECT state */ - multistate(easy, CURLM_STATE_CONNECT); + multistate(data, CURLM_STATE_CONNECT); result = CURLM_CALL_MULTI_PERFORM; - easy->result = CURLE_OK; + data->result = CURLE_OK; } data->state.pipe_broke = FALSE; - easy->easy_conn = NULL; + data->easy_conn = NULL; break; } - if(!easy->easy_conn && - easy->state > CURLM_STATE_CONNECT && - easy->state < CURLM_STATE_DONE) { - /* In all these states, the code will blindly access 'easy->easy_conn' + if(!data->easy_conn && + data->mstate > CURLM_STATE_CONNECT && + data->mstate < CURLM_STATE_DONE) { + /* In all these states, the code will blindly access 'data->easy_conn' so this is precaution that it isn't NULL. And it silences static analyzers. */ - failf(data, "In state %d with no easy_conn, bail out!\n", easy->state); + failf(data, "In state %d with no easy_conn, bail out!\n", data->mstate); return CURLM_INTERNAL_ERROR; } - if(easy->easy_conn && easy->state > CURLM_STATE_CONNECT && - easy->state < CURLM_STATE_COMPLETED) + if(data->easy_conn && data->mstate > CURLM_STATE_CONNECT && + data->mstate < CURLM_STATE_COMPLETED) /* Make sure we set the connection's current owner */ - easy->easy_conn->data = data; + data->easy_conn->data = data; - if(easy->easy_conn && - (easy->state >= CURLM_STATE_CONNECT) && - (easy->state < CURLM_STATE_COMPLETED)) { + if(data->easy_conn && + (data->mstate >= CURLM_STATE_CONNECT) && + (data->mstate < CURLM_STATE_COMPLETED)) { /* we need to wait for the connect state as only then is the start time stored, but we must not check already completed handles */ timeout_ms = Curl_timeleft(data, &now, - (easy->state <= CURLM_STATE_WAITDO)? + (data->mstate <= CURLM_STATE_WAITDO)? TRUE:FALSE); if(timeout_ms < 0) { /* Handle timed out */ - if(easy->state == CURLM_STATE_WAITRESOLVE) + if(data->mstate == CURLM_STATE_WAITRESOLVE) failf(data, "Resolving timed out after %ld milliseconds", Curl_tvdiff(now, data->progress.t_startsingle)); - else if(easy->state == CURLM_STATE_WAITCONNECT) + else if(data->mstate == CURLM_STATE_WAITCONNECT) failf(data, "Connection timed out after %ld milliseconds", Curl_tvdiff(now, data->progress.t_startsingle)); else { @@ -979,41 +981,52 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, /* Force the connection closed because the server could continue to send us stuff at any time. (The disconnect_conn logic used below doesn't work at this point). */ - easy->easy_conn->bits.close = TRUE; - easy->result = CURLE_OPERATION_TIMEDOUT; - multistate(easy, CURLM_STATE_COMPLETED); + data->easy_conn->bits.close = TRUE; + data->result = CURLE_OPERATION_TIMEDOUT; + multistate(data, CURLM_STATE_COMPLETED); break; } } - switch(easy->state) { + switch(data->mstate) { case CURLM_STATE_INIT: /* init this transfer. */ - easy->result=Curl_pretransfer(data); + data->result=Curl_pretransfer(data); - if(CURLE_OK == easy->result) { + if(CURLE_OK == data->result) { /* after init, go CONNECT */ - multistate(easy, CURLM_STATE_CONNECT); + multistate(data, CURLM_STATE_CONNECT); result = CURLM_CALL_MULTI_PERFORM; } break; + case CURLM_STATE_CONNECT_PEND: + /* We will stay here until there is a connection available. Then + we try again in the CURLM_STATE_CONNECT state. */ + break; + case CURLM_STATE_CONNECT: - /* Connect. We get a connection identifier filled in. */ + /* Connect. We want to get a connection identifier filled in. */ Curl_pgrsTime(data, TIMER_STARTSINGLE); - easy->result = Curl_connect(data, &easy->easy_conn, + data->result = Curl_connect(data, &data->easy_conn, &async, &protocol_connect); + if(CURLE_NO_CONNECTION_AVAILABLE == data->result) { + /* There was no connection available. We will go to the pending + state and wait for an available connection. */ + multistate(data, CURLM_STATE_CONNECT_PEND); + data->result = CURLE_OK; + break; + } - if(CURLE_OK == easy->result) { + if(CURLE_OK == data->result) { /* Add this handle to the send or pend pipeline */ - easy->result = addHandleToSendOrPendPipeline(data, - easy->easy_conn); - if(CURLE_OK != easy->result) + data->result = Curl_add_handle_to_pipeline(data, data->easy_conn); + if(CURLE_OK != data->result) disconnect_conn = TRUE; else { if(async) /* We're now waiting for an asynchronous name lookup */ - multistate(easy, CURLM_STATE_WAITRESOLVE); + multistate(data, CURLM_STATE_WAITRESOLVE); else { /* after the connect has been sent off, go WAITCONNECT unless the protocol connect is already done and we can go directly to @@ -1021,15 +1034,15 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, result = CURLM_CALL_MULTI_PERFORM; if(protocol_connect) - multistate(easy, multi->pipelining_enabled? + multistate(data, multi->pipelining_enabled? CURLM_STATE_WAITDO:CURLM_STATE_DO); else { #ifndef CURL_DISABLE_HTTP - if(easy->easy_conn->tunnel_state[FIRSTSOCKET] == TUNNEL_CONNECT) - multistate(easy, CURLM_STATE_WAITPROXYCONNECT); + if(data->easy_conn->tunnel_state[FIRSTSOCKET] == TUNNEL_CONNECT) + multistate(data, CURLM_STATE_WAITPROXYCONNECT); else #endif - multistate(easy, CURLM_STATE_WAITCONNECT); + multistate(data, CURLM_STATE_WAITCONNECT); } } } @@ -1042,7 +1055,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, struct Curl_dns_entry *dns = NULL; /* check if we have the name resolved by now */ - easy->result = Curl_resolver_is_resolved(easy->easy_conn, &dns); + data->result = Curl_resolver_is_resolved(data->easy_conn, &dns); /* Update sockets here, because the socket(s) may have been closed and the application thus needs to be told, even if it @@ -1050,36 +1063,36 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, down. If the name has not yet been resolved, it is likely that new sockets have been opened in an attempt to contact another resolver. */ - singlesocket(multi, easy); + singlesocket(multi, data); if(dns) { /* Perform the next step in the connection phase, and then move on to the WAITCONNECT state */ - easy->result = Curl_async_resolved(easy->easy_conn, + data->result = Curl_async_resolved(data->easy_conn, &protocol_connect); - if(CURLE_OK != easy->result) + if(CURLE_OK != data->result) /* if Curl_async_resolved() returns failure, the connection struct is already freed and gone */ - easy->easy_conn = NULL; /* no more connection */ + data->easy_conn = NULL; /* no more connection */ else { /* call again please so that we get the next socket setup */ result = CURLM_CALL_MULTI_PERFORM; if(protocol_connect) - multistate(easy, multi->pipelining_enabled? + multistate(data, multi->pipelining_enabled? CURLM_STATE_WAITDO:CURLM_STATE_DO); else { #ifndef CURL_DISABLE_HTTP - if(easy->easy_conn->tunnel_state[FIRSTSOCKET] == TUNNEL_CONNECT) - multistate(easy, CURLM_STATE_WAITPROXYCONNECT); + if(data->easy_conn->tunnel_state[FIRSTSOCKET] == TUNNEL_CONNECT) + multistate(data, CURLM_STATE_WAITPROXYCONNECT); else #endif - multistate(easy, CURLM_STATE_WAITCONNECT); + multistate(data, CURLM_STATE_WAITCONNECT); } } } - if(CURLE_OK != easy->result) { + if(CURLE_OK != data->result) { /* failure detected */ disconnect_conn = TRUE; break; @@ -1090,40 +1103,40 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, #ifndef CURL_DISABLE_HTTP case CURLM_STATE_WAITPROXYCONNECT: /* this is HTTP-specific, but sending CONNECT to a proxy is HTTP... */ - easy->result = Curl_http_connect(easy->easy_conn, &protocol_connect); + data->result = Curl_http_connect(data->easy_conn, &protocol_connect); - if(easy->easy_conn->bits.proxy_connect_closed) { + if(data->easy_conn->bits.proxy_connect_closed) { /* reset the error buffer */ if(data->set.errorbuffer) data->set.errorbuffer[0] = '\0'; data->state.errorbuf = FALSE; - easy->result = CURLE_OK; + data->result = CURLE_OK; result = CURLM_CALL_MULTI_PERFORM; - multistate(easy, CURLM_STATE_CONNECT); + multistate(data, CURLM_STATE_CONNECT); } - else if(CURLE_OK == easy->result) { - if(easy->easy_conn->tunnel_state[FIRSTSOCKET] == TUNNEL_COMPLETE) - multistate(easy, CURLM_STATE_WAITCONNECT); + else if(CURLE_OK == data->result) { + if(data->easy_conn->tunnel_state[FIRSTSOCKET] == TUNNEL_COMPLETE) + multistate(data, CURLM_STATE_WAITCONNECT); } break; #endif case CURLM_STATE_WAITCONNECT: /* awaiting a completion of an asynch connect */ - easy->result = Curl_is_connected(easy->easy_conn, + data->result = Curl_is_connected(data->easy_conn, FIRSTSOCKET, &connected); if(connected) { - if(!easy->result) + if(!data->result) /* if everything is still fine we do the protocol-specific connect setup */ - easy->result = Curl_protocol_connect(easy->easy_conn, + data->result = Curl_protocol_connect(data->easy_conn, &protocol_connect); } - if(CURLE_OK != easy->result) { + if(CURLE_OK != data->result) { /* failure detected */ /* Just break, the cleaning up is handled all in one place */ disconnect_conn = TRUE; @@ -1138,16 +1151,16 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, BUT if we are using a proxy we must change to WAITPROXYCONNECT */ #ifndef CURL_DISABLE_HTTP - if(easy->easy_conn->tunnel_state[FIRSTSOCKET] == TUNNEL_CONNECT) - multistate(easy, CURLM_STATE_WAITPROXYCONNECT); + if(data->easy_conn->tunnel_state[FIRSTSOCKET] == TUNNEL_CONNECT) + multistate(data, CURLM_STATE_WAITPROXYCONNECT); else #endif - multistate(easy, CURLM_STATE_PROTOCONNECT); + multistate(data, CURLM_STATE_PROTOCONNECT); } else /* after the connect has completed, go WAITDO or DO */ - multistate(easy, multi->pipelining_enabled? + multistate(data, multi->pipelining_enabled? CURLM_STATE_WAITDO:CURLM_STATE_DO); result = CURLM_CALL_MULTI_PERFORM; @@ -1156,18 +1169,18 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, case CURLM_STATE_PROTOCONNECT: /* protocol-specific connect phase */ - easy->result = Curl_protocol_connecting(easy->easy_conn, + data->result = Curl_protocol_connecting(data->easy_conn, &protocol_connect); - if((easy->result == CURLE_OK) && protocol_connect) { + if((data->result == CURLE_OK) && protocol_connect) { /* after the connect has completed, go WAITDO or DO */ - multistate(easy, multi->pipelining_enabled? + multistate(data, multi->pipelining_enabled? CURLM_STATE_WAITDO:CURLM_STATE_DO); result = CURLM_CALL_MULTI_PERFORM; } - else if(easy->result) { + else if(data->result) { /* failure detected */ Curl_posttransfer(data); - Curl_done(&easy->easy_conn, easy->result, TRUE); + Curl_done(&data->easy_conn, data->result, TRUE); disconnect_conn = TRUE; } break; @@ -1175,19 +1188,19 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, case CURLM_STATE_WAITDO: /* Wait for our turn to DO when we're pipelining requests */ #ifdef DEBUGBUILD - infof(data, "WAITDO: Conn %ld send pipe %zu inuse %d athead %d\n", - easy->easy_conn->connection_id, - easy->easy_conn->send_pipe->size, - easy->easy_conn->writechannel_inuse?1:0, + infof(data, "WAITDO: Conn %ld send pipe %zu inuse %s athead %s\n", + data->easy_conn->connection_id, + data->easy_conn->send_pipe->size, + data->easy_conn->writechannel_inuse?"TRUE":"FALSE", isHandleAtHead(data, - easy->easy_conn->send_pipe)?1:0); + data->easy_conn->send_pipe)?"TRUE":"FALSE"); #endif - if(!easy->easy_conn->writechannel_inuse && + if(!data->easy_conn->writechannel_inuse && isHandleAtHead(data, - easy->easy_conn->send_pipe)) { + data->easy_conn->send_pipe)) { /* Grab the channel */ - easy->easy_conn->writechannel_inuse = TRUE; - multistate(easy, CURLM_STATE_DO); + data->easy_conn->writechannel_inuse = TRUE; + multistate(data, CURLM_STATE_DO); result = CURLM_CALL_MULTI_PERFORM; } break; @@ -1195,50 +1208,51 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, case CURLM_STATE_DO: if(data->set.connect_only) { /* keep connection open for application to use the socket */ - easy->easy_conn->bits.close = FALSE; - multistate(easy, CURLM_STATE_DONE); - easy->result = CURLE_OK; + data->easy_conn->bits.close = FALSE; + multistate(data, CURLM_STATE_DONE); + data->result = CURLE_OK; result = CURLM_CALL_MULTI_PERFORM; } else { /* Perform the protocol's DO action */ - easy->result = Curl_do(&easy->easy_conn, - &dophase_done); + data->result = Curl_do(&data->easy_conn, &dophase_done); + + /* When Curl_do() returns failure, data->easy_conn might be NULL! */ - if(CURLE_OK == easy->result) { + if(CURLE_OK == data->result) { if(!dophase_done) { /* some steps needed for wildcard matching */ if(data->set.wildcardmatch) { struct WildcardData *wc = &data->wildcard; if(wc->state == CURLWC_DONE || wc->state == CURLWC_SKIP) { /* skip some states if it is important */ - Curl_done(&easy->easy_conn, CURLE_OK, FALSE); - multistate(easy, CURLM_STATE_DONE); + Curl_done(&data->easy_conn, CURLE_OK, FALSE); + multistate(data, CURLM_STATE_DONE); result = CURLM_CALL_MULTI_PERFORM; break; } } /* DO was not completed in one function call, we must continue DOING... */ - multistate(easy, CURLM_STATE_DOING); + multistate(data, CURLM_STATE_DOING); result = CURLM_OK; } /* after DO, go DO_DONE... or DO_MORE */ - else if(easy->easy_conn->bits.do_more) { + else if(data->easy_conn->bits.do_more) { /* we're supposed to do more, but we need to sit down, relax and wait a little while first */ - multistate(easy, CURLM_STATE_DO_MORE); + multistate(data, CURLM_STATE_DO_MORE); result = CURLM_OK; } else { /* we're done with the DO, now DO_DONE */ - multistate(easy, CURLM_STATE_DO_DONE); + multistate(data, CURLM_STATE_DO_DONE); result = CURLM_CALL_MULTI_PERFORM; } } - else if((CURLE_SEND_ERROR == easy->result) && - easy->easy_conn->bits.reuse) { + else if((CURLE_SEND_ERROR == data->result) && + data->easy_conn->bits.reuse) { /* * In this situation, a connection that we were trying to use * may have unexpectedly died. If possible, send the connection @@ -1249,17 +1263,17 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, CURLcode drc; bool retry = FALSE; - drc = Curl_retry_request(easy->easy_conn, &newurl); + drc = Curl_retry_request(data->easy_conn, &newurl); if(drc) { /* a failure here pretty much implies an out of memory */ - easy->result = drc; + data->result = drc; disconnect_conn = TRUE; } else retry = (newurl)?TRUE:FALSE; Curl_posttransfer(data); - drc = Curl_done(&easy->easy_conn, easy->result, FALSE); + drc = Curl_done(&data->easy_conn, data->result, FALSE); /* When set to retry the connection, we must to go back to * the CONNECT state */ @@ -1268,19 +1282,19 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, follow = FOLLOW_RETRY; drc = Curl_follow(data, newurl, follow); if(drc == CURLE_OK) { - multistate(easy, CURLM_STATE_CONNECT); + multistate(data, CURLM_STATE_CONNECT); result = CURLM_CALL_MULTI_PERFORM; - easy->result = CURLE_OK; + data->result = CURLE_OK; } else { /* Follow failed */ - easy->result = drc; + data->result = drc; free(newurl); } } else { /* done didn't return OK or SEND_ERROR */ - easy->result = drc; + data->result = drc; free(newurl); } } @@ -1292,7 +1306,8 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, else { /* failure detected */ Curl_posttransfer(data); - Curl_done(&easy->easy_conn, easy->result, FALSE); + if(data->easy_conn) + Curl_done(&data->easy_conn, data->result, FALSE); disconnect_conn = TRUE; } } @@ -1300,12 +1315,12 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, case CURLM_STATE_DOING: /* we continue DOING until the DO phase is complete */ - easy->result = Curl_protocol_doing(easy->easy_conn, + data->result = Curl_protocol_doing(data->easy_conn, &dophase_done); - if(CURLE_OK == easy->result) { + if(CURLE_OK == data->result) { if(dophase_done) { /* after DO, go DO_DONE or DO_MORE */ - multistate(easy, easy->easy_conn->bits.do_more? + multistate(data, data->easy_conn->bits.do_more? CURLM_STATE_DO_MORE: CURLM_STATE_DO_DONE); result = CURLM_CALL_MULTI_PERFORM; @@ -1314,7 +1329,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, else { /* failure detected */ Curl_posttransfer(data); - Curl_done(&easy->easy_conn, easy->result, FALSE); + Curl_done(&data->easy_conn, data->result, FALSE); disconnect_conn = TRUE; } break; @@ -1323,13 +1338,17 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, /* * When we are connected, DO MORE and then go DO_DONE */ - easy->result = Curl_do_more(easy->easy_conn, &dophase_done); + data->result = Curl_do_more(data->easy_conn, &control); /* No need to remove this handle from the send pipeline here since that is done in Curl_done() */ - if(CURLE_OK == easy->result) { - if(dophase_done) { - multistate(easy, CURLM_STATE_DO_DONE); + if(CURLE_OK == data->result) { + if(control) { + /* if positive, advance to DO_DONE + if negative, go back to DOING */ + multistate(data, control==1? + CURLM_STATE_DO_DONE: + CURLM_STATE_DOING); result = CURLM_CALL_MULTI_PERFORM; } else @@ -1339,54 +1358,54 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, else { /* failure detected */ Curl_posttransfer(data); - Curl_done(&easy->easy_conn, easy->result, FALSE); + Curl_done(&data->easy_conn, data->result, FALSE); disconnect_conn = TRUE; } break; case CURLM_STATE_DO_DONE: /* Move ourselves from the send to recv pipeline */ - moveHandleFromSendToRecvPipeline(data, easy->easy_conn); + Curl_move_handle_from_send_to_recv_pipe(data, data->easy_conn); /* Check if we can move pending requests to send pipe */ - checkPendPipeline(easy->easy_conn); - multistate(easy, CURLM_STATE_WAITPERFORM); + Curl_multi_process_pending_handles(multi); + multistate(data, CURLM_STATE_WAITPERFORM); result = CURLM_CALL_MULTI_PERFORM; break; case CURLM_STATE_WAITPERFORM: /* Wait for our turn to PERFORM */ - if(!easy->easy_conn->readchannel_inuse && + if(!data->easy_conn->readchannel_inuse && isHandleAtHead(data, - easy->easy_conn->recv_pipe)) { + data->easy_conn->recv_pipe)) { /* Grab the channel */ - easy->easy_conn->readchannel_inuse = TRUE; - multistate(easy, CURLM_STATE_PERFORM); + data->easy_conn->readchannel_inuse = TRUE; + multistate(data, CURLM_STATE_PERFORM); result = CURLM_CALL_MULTI_PERFORM; } #ifdef DEBUGBUILD else { - infof(data, "WAITPERFORM: Conn %ld recv pipe %zu inuse %d athead %d\n", - easy->easy_conn->connection_id, - easy->easy_conn->recv_pipe->size, - easy->easy_conn->readchannel_inuse?1:0, + infof(data, "WAITPERFORM: Conn %ld recv pipe %zu inuse %s athead %s\n", + data->easy_conn->connection_id, + data->easy_conn->recv_pipe->size, + data->easy_conn->readchannel_inuse?"TRUE":"FALSE", isHandleAtHead(data, - easy->easy_conn->recv_pipe)?1:0); + data->easy_conn->recv_pipe)?"TRUE":"FALSE"); } #endif break; case CURLM_STATE_TOOFAST: /* limit-rate exceeded in either direction */ /* if both rates are within spec, resume transfer */ - if(Curl_pgrsUpdate(easy->easy_conn)) - easy->result = CURLE_ABORTED_BY_CALLBACK; + if(Curl_pgrsUpdate(data->easy_conn)) + data->result = CURLE_ABORTED_BY_CALLBACK; else - easy->result = Curl_speedcheck(data, now); + data->result = Curl_speedcheck(data, now); if(( (data->set.max_send_speed == 0) || (data->progress.ulspeed < data->set.max_send_speed )) && ( (data->set.max_recv_speed == 0) || (data->progress.dlspeed < data->set.max_recv_speed))) - multistate(easy, CURLM_STATE_PERFORM); + multistate(data, CURLM_STATE_PERFORM); break; case CURLM_STATE_PERFORM: @@ -1399,7 +1418,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, (data->progress.ulspeed > data->set.max_send_speed)) { int buffersize; - multistate(easy, CURLM_STATE_TOOFAST); + multistate(data, CURLM_STATE_TOOFAST); /* calculate upload rate-limitation timeout. */ buffersize = (int)(data->set.buffer_size ? @@ -1415,7 +1434,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, (data->progress.dlspeed > data->set.max_recv_speed)) { int buffersize; - multistate(easy, CURLM_STATE_TOOFAST); + multistate(data, CURLM_STATE_TOOFAST); /* Calculate download rate-limitation timeout. */ buffersize = (int)(data->set.buffer_size ? @@ -1427,38 +1446,38 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, } /* read/write data if it is ready to do so */ - easy->result = Curl_readwrite(easy->easy_conn, &done); + data->result = Curl_readwrite(data->easy_conn, &done); k = &data->req; if(!(k->keepon & KEEP_RECV)) { /* We're done receiving */ - easy->easy_conn->readchannel_inuse = FALSE; + data->easy_conn->readchannel_inuse = FALSE; } if(!(k->keepon & KEEP_SEND)) { /* We're done sending */ - easy->easy_conn->writechannel_inuse = FALSE; + data->easy_conn->writechannel_inuse = FALSE; } - if(done || (easy->result == CURLE_RECV_ERROR)) { + if(done || (data->result == CURLE_RECV_ERROR)) { /* If CURLE_RECV_ERROR happens early enough, we assume it was a race * condition and the server closed the re-used connection exactly when * we wanted to use it, so figure out if that is indeed the case. */ - CURLcode ret = Curl_retry_request(easy->easy_conn, &newurl); + CURLcode ret = Curl_retry_request(data->easy_conn, &newurl); if(!ret) retry = (newurl)?TRUE:FALSE; if(retry) { /* if we are to retry, set the result to OK and consider the request as done */ - easy->result = CURLE_OK; + data->result = CURLE_OK; done = TRUE; } } - if(easy->result) { + if(data->result) { /* * The transfer phase returned error, we mark the connection to get * closed to prevent being re-used. This is because we can't possibly @@ -1467,11 +1486,11 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, * happened in the data connection. */ - if(!(easy->easy_conn->handler->flags & PROTOPT_DUAL)) - easy->easy_conn->bits.close = TRUE; + if(!(data->easy_conn->handler->flags & PROTOPT_DUAL)) + data->easy_conn->bits.close = TRUE; Curl_posttransfer(data); - Curl_done(&easy->easy_conn, easy->result, FALSE); + Curl_done(&data->easy_conn, data->result, FALSE); } else if(done) { followtype follow=FOLLOW_NONE; @@ -1480,15 +1499,14 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, Curl_posttransfer(data); /* we're no longer receiving */ - moveHandleFromRecvToDonePipeline(data, - easy->easy_conn); + Curl_removeHandleFromPipeline(data, data->easy_conn->recv_pipe); /* expire the new receiving pipeline head */ - if(easy->easy_conn->recv_pipe->head) - Curl_expire(easy->easy_conn->recv_pipe->head->ptr, 1); + if(data->easy_conn->recv_pipe->head) + Curl_expire(data->easy_conn->recv_pipe->head->ptr, 1); /* Check if we can move pending requests to send pipe */ - checkPendPipeline(easy->easy_conn); + Curl_multi_process_pending_handles(multi); /* When we follow redirects or is set to retry the connection, we must to go back to the CONNECT state */ @@ -1502,15 +1520,16 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, } else follow = FOLLOW_RETRY; - easy->result = Curl_done(&easy->easy_conn, CURLE_OK, FALSE); - if(easy->result == CURLE_OK) - easy->result = Curl_follow(data, newurl, follow); - if(CURLE_OK == easy->result) { - multistate(easy, CURLM_STATE_CONNECT); - result = CURLM_CALL_MULTI_PERFORM; - newurl = NULL; /* handed over the memory ownership to - Curl_follow(), make sure we don't free() it - here */ + data->result = Curl_done(&data->easy_conn, CURLE_OK, FALSE); + if(CURLE_OK == data->result) { + data->result = Curl_follow(data, newurl, follow); + if(CURLE_OK == data->result) { + multistate(data, CURLM_STATE_CONNECT); + result = CURLM_CALL_MULTI_PERFORM; + newurl = NULL; /* handed over the memory ownership to + Curl_follow(), make sure we don't free() it + here */ + } } } else { @@ -1523,14 +1542,14 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, free(newurl); newurl = data->req.location; data->req.location = NULL; - easy->result = Curl_follow(data, newurl, FOLLOW_FAKE); - if(CURLE_OK == easy->result) + data->result = Curl_follow(data, newurl, FOLLOW_FAKE); + if(CURLE_OK == data->result) newurl = NULL; /* allocation was handed over Curl_follow() */ else disconnect_conn = TRUE; } - multistate(easy, CURLM_STATE_DONE); + multistate(data, CURLM_STATE_DONE); result = CURLM_CALL_MULTI_PERFORM; } } @@ -1541,50 +1560,39 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, } case CURLM_STATE_DONE: + /* this state is highly transient, so run another loop after this */ + result = CURLM_CALL_MULTI_PERFORM; - if(easy->easy_conn) { - /* Remove ourselves from the receive and done pipelines. Handle - should be on one of these lists, depending upon how we got here. */ - Curl_removeHandleFromPipeline(data, - easy->easy_conn->recv_pipe); - Curl_removeHandleFromPipeline(data, - easy->easy_conn->done_pipe); + if(data->easy_conn) { + /* Remove ourselves from the receive pipeline, if we are there. */ + Curl_removeHandleFromPipeline(data, data->easy_conn->recv_pipe); /* Check if we can move pending requests to send pipe */ - checkPendPipeline(easy->easy_conn); - - if(easy->easy_conn->bits.stream_was_rewound) { - /* This request read past its response boundary so we quickly let - the other requests consume those bytes since there is no - guarantee that the socket will become active again */ - result = CURLM_CALL_MULTI_PERFORM; - } + Curl_multi_process_pending_handles(multi); /* post-transfer command */ - easy->result = Curl_done(&easy->easy_conn, CURLE_OK, FALSE); + data->result = Curl_done(&data->easy_conn, CURLE_OK, FALSE); /* * If there are other handles on the pipeline, Curl_done won't set * easy_conn to NULL. In such a case, curl_multi_remove_handle() can * access free'd data, if the connection is free'd and the handle * removed before we perform the processing in CURLM_STATE_COMPLETED */ - if(easy->easy_conn) - easy->easy_conn = NULL; + if(data->easy_conn) + data->easy_conn = NULL; } if(data->set.wildcardmatch) { if(data->wildcard.state != CURLWC_DONE) { /* if a wildcard is set and we are not ending -> lets start again with CURLM_STATE_INIT */ - result = CURLM_CALL_MULTI_PERFORM; - multistate(easy, CURLM_STATE_INIT); + multistate(data, CURLM_STATE_INIT); break; } } /* after we have DONE what we're supposed to do, go COMPLETED, and it doesn't matter what the Curl_done() returned! */ - multistate(easy, CURLM_STATE_COMPLETED); - + multistate(data, CURLM_STATE_COMPLETED); break; case CURLM_STATE_COMPLETED: @@ -1595,7 +1603,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, /* Important: reset the conn pointer so that we don't point to memory that could be freed anytime */ - easy->easy_conn = NULL; + data->easy_conn = NULL; Curl_expire(data, 0); /* stop all timers */ break; @@ -1607,8 +1615,8 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, return CURLM_INTERNAL_ERROR; } - if(easy->state < CURLM_STATE_COMPLETED) { - if(CURLE_OK != easy->result) { + if(data->mstate < CURLM_STATE_COMPLETED) { + if(CURLE_OK != data->result) { /* * If an error was returned, and we aren't in completed state now, * then we go to completed and consider this transfer aborted. @@ -1619,61 +1627,59 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, data->state.pipe_broke = FALSE; - if(easy->easy_conn) { + if(data->easy_conn) { /* if this has a connection, unsubscribe from the pipelines */ - easy->easy_conn->writechannel_inuse = FALSE; - easy->easy_conn->readchannel_inuse = FALSE; + data->easy_conn->writechannel_inuse = FALSE; + data->easy_conn->readchannel_inuse = FALSE; Curl_removeHandleFromPipeline(data, - easy->easy_conn->send_pipe); + data->easy_conn->send_pipe); Curl_removeHandleFromPipeline(data, - easy->easy_conn->recv_pipe); - Curl_removeHandleFromPipeline(data, - easy->easy_conn->done_pipe); + data->easy_conn->recv_pipe); /* Check if we can move pending requests to send pipe */ - checkPendPipeline(easy->easy_conn); + Curl_multi_process_pending_handles(multi); if(disconnect_conn) { /* disconnect properly */ - Curl_disconnect(easy->easy_conn, /* dead_connection */ FALSE); + Curl_disconnect(data->easy_conn, /* dead_connection */ FALSE); /* This is where we make sure that the easy_conn pointer is reset. We don't have to do this in every case block above where a failure is detected */ - easy->easy_conn = NULL; + data->easy_conn = NULL; } } - else if(easy->state == CURLM_STATE_CONNECT) { + else if(data->mstate == CURLM_STATE_CONNECT) { /* Curl_connect() failed */ (void)Curl_posttransfer(data); } - multistate(easy, CURLM_STATE_COMPLETED); + multistate(data, CURLM_STATE_COMPLETED); } /* if there's still a connection to use, call the progress function */ - else if(easy->easy_conn && Curl_pgrsUpdate(easy->easy_conn)) { + else if(data->easy_conn && Curl_pgrsUpdate(data->easy_conn)) { /* aborted due to progress callback return code must close the connection */ - easy->easy_conn->bits.close = TRUE; + data->easy_conn->bits.close = TRUE; /* if not yet in DONE state, go there, otherwise COMPLETED */ - multistate(easy, (easy->state < CURLM_STATE_DONE)? + multistate(data, (data->mstate < CURLM_STATE_DONE)? CURLM_STATE_DONE: CURLM_STATE_COMPLETED); result = CURLM_CALL_MULTI_PERFORM; } } } WHILE_FALSE; /* just to break out from! */ - if(CURLM_STATE_COMPLETED == easy->state) { + if(CURLM_STATE_COMPLETED == data->mstate) { /* now fill in the Curl_message with this info */ - msg = &easy->msg; + msg = &data->msg; msg->extmsg.msg = CURLMSG_DONE; msg->extmsg.easy_handle = data; - msg->extmsg.data.result = easy->result; + msg->extmsg.data.result = data->result; result = multi_addmsg(multi, msg); - multistate(easy, CURLM_STATE_MSGSENT); + multistate(data, CURLM_STATE_MSGSENT); } return result; @@ -1683,7 +1689,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles) { struct Curl_multi *multi=(struct Curl_multi *)multi_handle; - struct Curl_one_easy *easy; + struct SessionHandle *data; CURLMcode returncode=CURLM_OK; struct Curl_tree *t; struct timeval now = Curl_tvnow(); @@ -1691,12 +1697,12 @@ CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles) if(!GOOD_MULTI_HANDLE(multi)) return CURLM_BAD_HANDLE; - easy=multi->easy.next; - while(easy != &multi->easy) { + data=multi->easyp; + while(data) { CURLMcode result; - struct WildcardData *wc = &easy->easy_handle->wildcard; + struct WildcardData *wc = &data->wildcard; - if(easy->easy_handle->set.wildcardmatch) { + if(data->set.wildcardmatch) { if(!wc->filelist) { CURLcode ret = Curl_wildcard_init(wc); /* init wildcard structures */ if(ret) @@ -1705,10 +1711,10 @@ CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles) } do - result = multi_runsingle(multi, now, easy); + result = multi_runsingle(multi, now, data); while(CURLM_CALL_MULTI_PERFORM == result); - if(easy->easy_handle->set.wildcardmatch) { + if(data->set.wildcardmatch) { /* destruct wildcard structures if it is needed */ if(wc->state == CURLWC_DONE || result) Curl_wildcard_dtor(wc); @@ -1717,7 +1723,7 @@ CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles) if(result) returncode = result; - easy = easy->next; /* operate on next handle */ + data = data->next; /* operate on next handle */ } /* @@ -1764,8 +1770,8 @@ static void close_all_connections(struct Curl_multi *multi) CURLMcode curl_multi_cleanup(CURLM *multi_handle) { struct Curl_multi *multi=(struct Curl_multi *)multi_handle; - struct Curl_one_easy *easy; - struct Curl_one_easy *nexteasy; + struct SessionHandle *data; + struct SessionHandle *nextdata; if(GOOD_MULTI_HANDLE(multi)) { multi->type = 0; /* not good anymore */ @@ -1773,11 +1779,14 @@ CURLMcode curl_multi_cleanup(CURLM *multi_handle) /* Close all the connections in the connection cache */ close_all_connections(multi); - multi->closure_handle->dns.hostcache = multi->hostcache; - Curl_hostcache_clean(multi->closure_handle); + if(multi->closure_handle) { + multi->closure_handle->dns.hostcache = multi->hostcache; + Curl_hostcache_clean(multi->closure_handle, + multi->closure_handle->dns.hostcache); - Curl_close(multi->closure_handle); - multi->closure_handle = NULL; + Curl_close(multi->closure_handle); + multi->closure_handle = NULL; + } Curl_hash_destroy(multi->sockhash); multi->sockhash = NULL; @@ -1790,28 +1799,30 @@ CURLMcode curl_multi_cleanup(CURLM *multi_handle) multi->msglist = NULL; /* remove all easy handles */ - easy = multi->easy.next; - while(easy != &multi->easy) { - nexteasy=easy->next; - if(easy->easy_handle->dns.hostcachetype == HCACHE_MULTI) { + data = multi->easyp; + while(data) { + nextdata=data->next; + if(data->dns.hostcachetype == HCACHE_MULTI) { /* clear out the usage of the shared DNS cache */ - Curl_hostcache_clean(easy->easy_handle); - easy->easy_handle->dns.hostcache = NULL; - easy->easy_handle->dns.hostcachetype = HCACHE_NONE; + Curl_hostcache_clean(data, data->dns.hostcache); + data->dns.hostcache = NULL; + data->dns.hostcachetype = HCACHE_NONE; } /* Clear the pointer to the connection cache */ - easy->easy_handle->state.conn_cache = NULL; - - Curl_easy_addmulti(easy->easy_handle, NULL); /* clear the association */ + data->state.conn_cache = NULL; + data->multi = NULL; /* clear the association */ - free(easy); - easy = nexteasy; + data = nextdata; } Curl_hash_destroy(multi->hostcache); multi->hostcache = NULL; + /* Free the blacklists by setting them to NULL */ + Curl_pipeline_set_site_blacklist(NULL, &multi->pipelining_site_bl); + Curl_pipeline_set_server_blacklist(NULL, &multi->pipelining_server_bl); + free(multi); return CURLM_OK; @@ -1863,7 +1874,7 @@ CURLMsg *curl_multi_info_read(CURLM *multi_handle, int *msgs_in_queue) * call the callback accordingly. */ static void singlesocket(struct Curl_multi *multi, - struct Curl_one_easy *easy) + struct SessionHandle *data) { curl_socket_t socks[MAX_SOCKSPEREASYHANDLE]; int i; @@ -1871,7 +1882,6 @@ static void singlesocket(struct Curl_multi *multi, curl_socket_t s; int num; unsigned int curraction; - struct Curl_one_easy *easy_by_hash; bool remove_sock_from_hash; for(i=0; i< MAX_SOCKSPEREASYHANDLE; i++) @@ -1879,7 +1889,7 @@ static void singlesocket(struct Curl_multi *multi, /* Fill in the 'current' struct with the state as it is now: what sockets to supervise and for what actions */ - curraction = multi_getsock(easy, socks, MAX_SOCKSPEREASYHANDLE); + curraction = multi_getsock(data, socks, MAX_SOCKSPEREASYHANDLE); /* We have 0 .. N sockets already and we get to know about the 0 .. M sockets we should have from now on. Detect the differences, remove no @@ -1909,7 +1919,7 @@ static void singlesocket(struct Curl_multi *multi, } else { /* this is a socket we didn't have before, add it! */ - entry = sh_addentry(multi->sockhash, s, easy->easy_handle); + entry = sh_addentry(multi->sockhash, s, data); if(!entry) /* fatal */ return; @@ -1917,7 +1927,7 @@ static void singlesocket(struct Curl_multi *multi, /* we know (entry != NULL) at this point, see the logic above */ if(multi->socket_cb) - multi->socket_cb(easy->easy_handle, + multi->socket_cb(data, s, action, multi->socket_userp, @@ -1930,9 +1940,9 @@ static void singlesocket(struct Curl_multi *multi, /* when we've walked over all the sockets we should have right now, we must make sure to detect sockets that are removed */ - for(i=0; i< easy->numsocks; i++) { + for(i=0; i< data->numsocks; i++) { int j; - s = easy->sockets[i]; + s = data->sockets[i]; for(j=0; j<num; j++) { if(s == socks[j]) { /* this is still supervised */ @@ -1950,10 +1960,7 @@ static void singlesocket(struct Curl_multi *multi, /* check if the socket to be removed serves a connection which has other easy-s in a pipeline. In this case the socket should not be removed. */ - struct connectdata *easy_conn; - - easy_by_hash = entry->easy->multi_pos; - easy_conn = easy_by_hash->easy_conn; + struct connectdata *easy_conn = data->easy_conn; if(easy_conn) { if(easy_conn->recv_pipe && easy_conn->recv_pipe->size > 1) { /* the handle should not be removed from the pipe yet */ @@ -1962,8 +1969,8 @@ static void singlesocket(struct Curl_multi *multi, /* Update the sockhash entry to instead point to the next in line for the recv_pipe, or the first (in case this particular easy isn't already) */ - if(entry->easy == easy->easy_handle) { - if(isHandleAtHead(easy->easy_handle, easy_conn->recv_pipe)) + if(entry->easy == data) { + if(isHandleAtHead(data, easy_conn->recv_pipe)) entry->easy = easy_conn->recv_pipe->head->next->ptr; else entry->easy = easy_conn->recv_pipe->head->ptr; @@ -1976,8 +1983,8 @@ static void singlesocket(struct Curl_multi *multi, /* Update the sockhash entry to instead point to the next in line for the send_pipe, or the first (in case this particular easy isn't already) */ - if(entry->easy == easy->easy_handle) { - if(isHandleAtHead(easy->easy_handle, easy_conn->send_pipe)) + if(entry->easy == data) { + if(isHandleAtHead(data, easy_conn->send_pipe)) entry->easy = easy_conn->send_pipe->head->next->ptr; else entry->easy = easy_conn->send_pipe->head->ptr; @@ -1998,7 +2005,7 @@ static void singlesocket(struct Curl_multi *multi, if(remove_sock_from_hash) { /* in this case 'entry' is always non-NULL */ if(multi->socket_cb) - multi->socket_cb(easy->easy_handle, + multi->socket_cb(data, s, CURL_POLL_REMOVE, multi->socket_userp, @@ -2009,11 +2016,44 @@ static void singlesocket(struct Curl_multi *multi, } } - memcpy(easy->sockets, socks, num*sizeof(curl_socket_t)); - easy->numsocks = num; + memcpy(data->sockets, socks, num*sizeof(curl_socket_t)); + data->numsocks = num; } /* + * Curl_multi_closed() + * + * Used by the connect code to tell the multi_socket code that one of the + * sockets we were using have just been closed. This function will then + * remove it from the sockethash for this handle to make the multi_socket API + * behave properly, especially for the case when libcurl will create another + * socket again and it gets the same file descriptor number. + */ + +void Curl_multi_closed(struct connectdata *conn, curl_socket_t s) +{ + struct Curl_multi *multi = conn->data->multi; + if(multi) { + /* this is set if this connection is part of a handle that is added to + a multi handle, and only then this is necessary */ + struct Curl_sh_entry *entry = + Curl_hash_pick(multi->sockhash, (char *)&s, sizeof(s)); + + if(entry) { + if(multi->socket_cb) + multi->socket_cb(conn->data, s, CURL_POLL_REMOVE, + multi->socket_userp, + entry->socketp); + + /* now remove it from the socket hash */ + sh_delentry(multi->sockhash, s); + } + } +} + + + +/* * add_next_timeout() * * Each SessionHandle has a list of timeouts. The add_next_timeout() is called @@ -2068,7 +2108,6 @@ static CURLMcode add_next_timeout(struct timeval now, return CURLM_OK; } - static CURLMcode multi_socket(struct Curl_multi *multi, bool checkall, curl_socket_t s, @@ -2081,16 +2120,15 @@ static CURLMcode multi_socket(struct Curl_multi *multi, struct timeval now = Curl_tvnow(); if(checkall) { - struct Curl_one_easy *easyp; /* *perform() deals with running_handles on its own */ result = curl_multi_perform(multi, running_handles); /* walk through each easy handle and do the socket state change magic and callbacks */ - easyp=multi->easy.next; - while(easyp != &multi->easy) { - singlesocket(multi, easyp); - easyp = easyp->next; + data=multi->easyp; + while(data) { + singlesocket(multi, data); + data = data->next; } /* or should we fall-through and do the timer-based stuff? */ @@ -2118,35 +2156,35 @@ static CURLMcode multi_socket(struct Curl_multi *multi, /* If the pipeline is enabled, take the handle which is in the head of the pipeline. If we should write into the socket, take the send_pipe head. If we should read from the socket, take the recv_pipe head. */ - if(data->set.one_easy->easy_conn) { + if(data->easy_conn) { if((ev_bitmask & CURL_POLL_OUT) && - data->set.one_easy->easy_conn->send_pipe && - data->set.one_easy->easy_conn->send_pipe->head) - data = data->set.one_easy->easy_conn->send_pipe->head->ptr; + data->easy_conn->send_pipe && + data->easy_conn->send_pipe->head) + data = data->easy_conn->send_pipe->head->ptr; else if((ev_bitmask & CURL_POLL_IN) && - data->set.one_easy->easy_conn->recv_pipe && - data->set.one_easy->easy_conn->recv_pipe->head) - data = data->set.one_easy->easy_conn->recv_pipe->head->ptr; + data->easy_conn->recv_pipe && + data->easy_conn->recv_pipe->head) + data = data->easy_conn->recv_pipe->head->ptr; } - if(data->set.one_easy->easy_conn && - !(data->set.one_easy->easy_conn->handler->flags & PROTOPT_DIRLOCK)) + if(data->easy_conn && + !(data->easy_conn->handler->flags & PROTOPT_DIRLOCK)) /* set socket event bitmask if they're not locked */ - data->set.one_easy->easy_conn->cselect_bits = ev_bitmask; + data->easy_conn->cselect_bits = ev_bitmask; do - result = multi_runsingle(multi, now, data->set.one_easy); + result = multi_runsingle(multi, now, data); while(CURLM_CALL_MULTI_PERFORM == result); - if(data->set.one_easy->easy_conn && - !(data->set.one_easy->easy_conn->handler->flags & PROTOPT_DIRLOCK)) + if(data->easy_conn && + !(data->easy_conn->handler->flags & PROTOPT_DIRLOCK)) /* clear the bitmask only if not locked */ - data->set.one_easy->easy_conn->cselect_bits = 0; + data->easy_conn->cselect_bits = 0; if(CURLM_OK >= result) /* get the socket(s) and check if the state has been changed since last */ - singlesocket(multi, data->set.one_easy); + singlesocket(multi, data); /* Now we fall-through and do the timer-based stuff, since we don't want to force the user to have to deal with timeouts as long as at least @@ -2157,8 +2195,25 @@ static CURLMcode multi_socket(struct Curl_multi *multi, } } - now.tv_usec += 40000; /* compensate for bad precision timers that might've - triggered too early */ + /* Compensate for bad precision timers that might've triggered too early. + + This precaution was added in commit 2c72732ebf3da5e as a result of bad + resolution in the windows function use(d). + + The problematic case here is when using the multi_socket API and libcurl + has told the application about a timeout, and that timeout is what fires + off a bit early. As we don't have any IDs associated with the timeout we + can't tell which timeout that fired off but we only have the times to use + to check what to do. If it fires off too early, we don't run the correct + actions and we don't tell the application again about the same timeout as + was already first in the queue... + + Originally we made the timeouts run 40 milliseconds early on all systems, + but now we have an #ifdef setup to provide a decent precaution inaccuracy + margin. + */ + + now.tv_usec += MULTI_TIMEOUT_INACCURACY; if(now.tv_usec >= 1000000) { now.tv_sec++; now.tv_usec -= 1000000; @@ -2173,13 +2228,13 @@ static CURLMcode multi_socket(struct Curl_multi *multi, /* the first loop lap 'data' can be NULL */ if(data) { do - result = multi_runsingle(multi, now, data->set.one_easy); + result = multi_runsingle(multi, now, data); while(CURLM_CALL_MULTI_PERFORM == result); if(CURLM_OK >= result) /* get the socket(s) and check if the state has been changed since last */ - singlesocket(multi, data->set.one_easy); + singlesocket(multi, data); } /* Check if there's one (more) expired timer to deal with! This function @@ -2229,6 +2284,29 @@ CURLMcode curl_multi_setopt(CURLM *multi_handle, case CURLMOPT_MAXCONNECTS: multi->maxconnects = va_arg(param, long); break; + case CURLMOPT_MAX_HOST_CONNECTIONS: + multi->max_host_connections = va_arg(param, long); + break; + case CURLMOPT_MAX_PIPELINE_LENGTH: + multi->max_pipeline_length = va_arg(param, long); + break; + case CURLMOPT_CONTENT_LENGTH_PENALTY_SIZE: + multi->content_length_penalty_size = va_arg(param, long); + break; + case CURLMOPT_CHUNK_LENGTH_PENALTY_SIZE: + multi->chunk_length_penalty_size = va_arg(param, long); + break; + case CURLMOPT_PIPELINING_SITE_BL: + res = Curl_pipeline_set_site_blacklist(va_arg(param, char **), + &multi->pipelining_site_bl); + break; + case CURLMOPT_PIPELINING_SERVER_BL: + res = Curl_pipeline_set_server_blacklist(va_arg(param, char **), + &multi->pipelining_server_bl); + break; + case CURLMOPT_MAX_TOTAL_CONNECTIONS: + multi->max_total_connections = va_arg(param, long); + break; default: res = CURLM_UNKNOWN_OPTION; break; @@ -2353,131 +2431,12 @@ static int update_timer(struct Curl_multi *multi) return multi->timer_cb((CURLM*)multi, timeout_ms, multi->timer_userp); } -static CURLcode addHandleToSendOrPendPipeline(struct SessionHandle *handle, - struct connectdata *conn) -{ - size_t pipeLen = conn->send_pipe->size + conn->recv_pipe->size; - struct curl_llist_element *sendhead = conn->send_pipe->head; - struct curl_llist *pipeline; - CURLcode rc; - - if(!Curl_isPipeliningEnabled(handle) || - pipeLen == 0) - pipeline = conn->send_pipe; - else { - if(conn->server_supports_pipelining && - pipeLen < MAX_PIPELINE_LENGTH) - pipeline = conn->send_pipe; - else - pipeline = conn->pend_pipe; - } - - rc = Curl_addHandleToPipeline(handle, pipeline); - - if(pipeline == conn->send_pipe && sendhead != conn->send_pipe->head) { - /* this is a new one as head, expire it */ - conn->writechannel_inuse = FALSE; /* not in use yet */ -#ifdef DEBUGBUILD - infof(conn->data, "%p is at send pipe head!\n", - conn->send_pipe->head->ptr); -#endif - Curl_expire(conn->send_pipe->head->ptr, 1); - } - - return rc; -} - -static int checkPendPipeline(struct connectdata *conn) -{ - int result = 0; - struct curl_llist_element *sendhead = conn->send_pipe->head; - - size_t pipeLen = conn->send_pipe->size + conn->recv_pipe->size; - if(conn->server_supports_pipelining || pipeLen == 0) { - struct curl_llist_element *curr = conn->pend_pipe->head; - const size_t maxPipeLen = - conn->server_supports_pipelining ? MAX_PIPELINE_LENGTH : 1; - - while(pipeLen < maxPipeLen && curr) { - Curl_llist_move(conn->pend_pipe, curr, - conn->send_pipe, conn->send_pipe->tail); - Curl_pgrsTime(curr->ptr, TIMER_PRETRANSFER); - ++result; /* count how many handles we moved */ - curr = conn->pend_pipe->head; - ++pipeLen; - } - } - - if(result) { - conn->now = Curl_tvnow(); - /* something moved, check for a new send pipeline leader */ - if(sendhead != conn->send_pipe->head) { - /* this is a new one as head, expire it */ - conn->writechannel_inuse = FALSE; /* not in use yet */ -#ifdef DEBUGBUILD - infof(conn->data, "%p is at send pipe head!\n", - conn->send_pipe->head->ptr); -#endif - Curl_expire(conn->send_pipe->head->ptr, 1); - } - } - - return result; -} - -/* Move this transfer from the sending list to the receiving list. - - Pay special attention to the new sending list "leader" as it needs to get - checked to update what sockets it acts on. - -*/ -static void moveHandleFromSendToRecvPipeline(struct SessionHandle *handle, - struct connectdata *conn) +void Curl_multi_set_easy_connection(struct SessionHandle *handle, + struct connectdata *conn) { - struct curl_llist_element *curr; - - curr = conn->send_pipe->head; - while(curr) { - if(curr->ptr == handle) { - Curl_llist_move(conn->send_pipe, curr, - conn->recv_pipe, conn->recv_pipe->tail); - - if(conn->send_pipe->head) { - /* Since there's a new easy handle at the start of the send pipeline, - set its timeout value to 1ms to make it trigger instantly */ - conn->writechannel_inuse = FALSE; /* not used now */ -#ifdef DEBUGBUILD - infof(conn->data, "%p is at send pipe head B!\n", - conn->send_pipe->head->ptr); -#endif - Curl_expire(conn->send_pipe->head->ptr, 1); - } - - /* The receiver's list is not really interesting here since either this - handle is now first in the list and we'll deal with it soon, or - another handle is already first and thus is already taken care of */ - - break; /* we're done! */ - } - curr = curr->next; - } + handle->easy_conn = conn; } -static void moveHandleFromRecvToDonePipeline(struct SessionHandle *handle, - struct connectdata *conn) -{ - struct curl_llist_element *curr; - - curr = conn->recv_pipe->head; - while(curr) { - if(curr->ptr == handle) { - Curl_llist_move(conn->recv_pipe, curr, - conn->done_pipe, conn->done_pipe->tail); - break; - } - curr = curr->next; - } -} static bool isHandleAtHead(struct SessionHandle *handle, struct curl_llist *pipeline) { @@ -2563,8 +2522,8 @@ void Curl_expire(struct SessionHandle *data, long milli) struct timeval *nowp = &data->state.expiretime; int rc; - /* this is only interesting for multi-interface using libcurl, and only - while there is still a multi interface struct remaining! */ + /* this is only interesting while there is still an associated multi struct + remaining! */ if(!multi) return; @@ -2657,22 +2616,72 @@ CURLMcode curl_multi_assign(CURLM *multi_handle, return CURLM_OK; } +size_t Curl_multi_max_host_connections(struct Curl_multi *multi) +{ + return multi ? multi->max_host_connections : 0; +} + +size_t Curl_multi_max_total_connections(struct Curl_multi *multi) +{ + return multi ? multi->max_total_connections : 0; +} + +size_t Curl_multi_max_pipeline_length(struct Curl_multi *multi) +{ + return multi ? multi->max_pipeline_length : 0; +} + +curl_off_t Curl_multi_content_length_penalty_size(struct Curl_multi *multi) +{ + return multi ? multi->content_length_penalty_size : 0; +} + +curl_off_t Curl_multi_chunk_length_penalty_size(struct Curl_multi *multi) +{ + return multi ? multi->chunk_length_penalty_size : 0; +} + +struct curl_llist *Curl_multi_pipelining_site_bl(struct Curl_multi *multi) +{ + return multi->pipelining_site_bl; +} + +struct curl_llist *Curl_multi_pipelining_server_bl(struct Curl_multi *multi) +{ + return multi->pipelining_server_bl; +} + +void Curl_multi_process_pending_handles(struct Curl_multi *multi) +{ + struct SessionHandle *data; + + data=multi->easyp; + while(data) { + if(data->mstate == CURLM_STATE_CONNECT_PEND) { + multistate(data, CURLM_STATE_CONNECT); + /* Make sure that the handle will be processed soonish. */ + Curl_expire(data, 1); + } + data = data->next; /* operate on next handle */ + } +} + #ifdef DEBUGBUILD void Curl_multi_dump(const struct Curl_multi *multi_handle) { struct Curl_multi *multi=(struct Curl_multi *)multi_handle; - struct Curl_one_easy *easy; + struct SessionHandle *data; int i; fprintf(stderr, "* Multi status: %d handles, %d alive\n", multi->num_easy, multi->num_alive); - for(easy=multi->easy.next; easy != &multi->easy; easy = easy->next) { - if(easy->state < CURLM_STATE_COMPLETED) { + for(data=multi->easyp; data; data = data->next) { + if(data->mstate < CURLM_STATE_COMPLETED) { /* only display handles that are not completed */ fprintf(stderr, "handle %p, state %s, %d sockets\n", - (void *)easy->easy_handle, - statename[easy->state], easy->numsocks); - for(i=0; i < easy->numsocks; i++) { - curl_socket_t s = easy->sockets[i]; + (void *)data, + statename[data->mstate], data->numsocks); + for(i=0; i < data->numsocks; i++) { + curl_socket_t s = data->sockets[i]; struct Curl_sh_entry *entry = Curl_hash_pick(multi->sockhash, (char *)&s, sizeof(s)); @@ -2685,7 +2694,7 @@ void Curl_multi_dump(const struct Curl_multi *multi_handle) entry->action&CURL_POLL_IN?"RECVING":"", entry->action&CURL_POLL_OUT?"SENDING":""); } - if(easy->numsocks) + if(data->numsocks) fprintf(stderr, "\n"); } } diff --git a/plugins/FTPFileYM/curl/lib/multihandle.h b/plugins/FTPFileYM/curl/lib/multihandle.h index 9418359415..552aa93797 100644 --- a/plugins/FTPFileYM/curl/lib/multihandle.h +++ b/plugins/FTPFileYM/curl/lib/multihandle.h @@ -31,25 +31,26 @@ struct Curl_message { well! */ typedef enum { - CURLM_STATE_INIT, /* 0 - start in this state */ - CURLM_STATE_CONNECT, /* 1 - resolve/connect has been sent off */ - CURLM_STATE_WAITRESOLVE, /* 2 - awaiting the resolve to finalize */ - CURLM_STATE_WAITCONNECT, /* 3 - awaiting the connect to finalize */ - CURLM_STATE_WAITPROXYCONNECT, /* 4 - awaiting proxy CONNECT to finalize */ - CURLM_STATE_PROTOCONNECT, /* 5 - completing the protocol-specific connect - phase */ - CURLM_STATE_WAITDO, /* 6 - wait for our turn to send the request */ - CURLM_STATE_DO, /* 7 - start send off the request (part 1) */ - CURLM_STATE_DOING, /* 8 - sending off the request (part 1) */ - CURLM_STATE_DO_MORE, /* 9 - send off the request (part 2) */ - CURLM_STATE_DO_DONE, /* 10 - done sending off request */ - CURLM_STATE_WAITPERFORM, /* 11 - wait for our turn to read the response */ - CURLM_STATE_PERFORM, /* 12 - transfer data */ - CURLM_STATE_TOOFAST, /* 13 - wait because limit-rate exceeded */ - CURLM_STATE_DONE, /* 14 - post data transfer operation */ - CURLM_STATE_COMPLETED, /* 15 - operation complete */ - CURLM_STATE_MSGSENT, /* 16 - the operation complete message is sent */ - CURLM_STATE_LAST /* 17 - not a true state, never use this */ + CURLM_STATE_INIT, /* 0 - start in this state */ + CURLM_STATE_CONNECT_PEND, /* 1 - no connections, waiting for one */ + CURLM_STATE_CONNECT, /* 2 - resolve/connect has been sent off */ + CURLM_STATE_WAITRESOLVE, /* 3 - awaiting the resolve to finalize */ + CURLM_STATE_WAITCONNECT, /* 4 - awaiting the connect to finalize */ + CURLM_STATE_WAITPROXYCONNECT, /* 5 - awaiting proxy CONNECT to finalize */ + CURLM_STATE_PROTOCONNECT, /* 6 - completing the protocol-specific connect + phase */ + CURLM_STATE_WAITDO, /* 7 - wait for our turn to send the request */ + CURLM_STATE_DO, /* 8 - start send off the request (part 1) */ + CURLM_STATE_DOING, /* 9 - sending off the request (part 1) */ + CURLM_STATE_DO_MORE, /* 10 - send off the request (part 2) */ + CURLM_STATE_DO_DONE, /* 11 - done sending off request */ + CURLM_STATE_WAITPERFORM, /* 12 - wait for our turn to read the response */ + CURLM_STATE_PERFORM, /* 13 - transfer data */ + CURLM_STATE_TOOFAST, /* 14 - wait because limit-rate exceeded */ + CURLM_STATE_DONE, /* 15 - post data transfer operation */ + CURLM_STATE_COMPLETED, /* 16 - operation complete */ + CURLM_STATE_MSGSENT, /* 17 - the operation complete message is sent */ + CURLM_STATE_LAST /* 18 - not a true state, never use this */ } CURLMstate; /* we support N sockets per easy handle. Set the corresponding bit to what @@ -58,27 +59,6 @@ typedef enum { #define GETSOCK_READABLE (0x00ff) #define GETSOCK_WRITABLE (0xff00) -struct Curl_one_easy { - /* first, two fields for the linked list of these */ - struct Curl_one_easy *next; - struct Curl_one_easy *prev; - - struct SessionHandle *easy_handle; /* the easy handle for this unit */ - struct connectdata *easy_conn; /* the "unit's" connection */ - - CURLMstate state; /* the handle's state */ - CURLcode result; /* previous result */ - - struct Curl_message msg; /* A single posted message. */ - - /* Array with the plain socket numbers this handle takes care of, in no - particular order. Note that all sockets are added to the sockhash, where - the state etc are also kept. This array is mostly used to detect when a - socket is to be removed from the hash. See singlesocket(). */ - curl_socket_t sockets[MAX_SOCKSPEREASYHANDLE]; - int numsocks; -}; - /* This is the struct known as CURLM on the outside */ struct Curl_multi { /* First a simple identifier to easier detect if a user mix up @@ -86,7 +66,8 @@ struct Curl_multi { long type; /* We have a doubly-linked circular list with easy handles */ - struct Curl_one_easy easy; + struct SessionHandle *easyp; + struct SessionHandle *easylp; /* last node */ int num_easy; /* amount of entries in the linked list above. */ int num_alive; /* amount of easy handles that are added but have not yet @@ -123,6 +104,30 @@ struct Curl_multi { long maxconnects; /* if >0, a fixed limit of the maximum number of entries we're allowed to grow the connection cache to */ + long max_host_connections; /* if >0, a fixed limit of the maximum number + of connections per host */ + + long max_total_connections; /* if >0, a fixed limit of the maximum number + of connections in total */ + + long max_pipeline_length; /* if >0, maximum number of requests in a + pipeline */ + + long content_length_penalty_size; /* a connection with a + content-length bigger than + this is not considered + for pipelining */ + + long chunk_length_penalty_size; /* a connection with a chunk length + bigger than this is not + considered for pipelining */ + + struct curl_llist *pipelining_site_bl; /* List of sites that are blacklisted + from pipelining */ + + struct curl_llist *pipelining_server_bl; /* List of server types that are + blacklisted from pipelining */ + /* timer callback and user data pointer for the *socket() API */ curl_multi_timer_callback timer_cb; void *timer_userp; diff --git a/plugins/FTPFileYM/curl/lib/multiif.h b/plugins/FTPFileYM/curl/lib/multiif.h index c84b6184c0..e61c5c6f2c 100644 --- a/plugins/FTPFileYM/curl/lib/multiif.h +++ b/plugins/FTPFileYM/curl/lib/multiif.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2010, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2013, 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 @@ -22,14 +22,26 @@ * ***************************************************************************/ +/* See multi_socket() for the explanation of this constant. Counted in number + of microseconds. */ +#ifdef WIN32 +#define MULTI_TIMEOUT_INACCURACY 40000 +#else +#define MULTI_TIMEOUT_INACCURACY 3000 +#endif + /* * Prototypes for library-wide functions provided by multi.c */ void Curl_expire(struct SessionHandle *data, long milli); -bool Curl_multi_canPipeline(const struct Curl_multi* multi); +bool Curl_multi_pipeline_enabled(const struct Curl_multi* multi); void Curl_multi_handlePipeBreak(struct SessionHandle *data); +/* Internal version of curl_multi_init() accepts size parameters for the + socket and connection hashes */ +struct Curl_multi *Curl_multi_handle(int hashsize, int chashsize); + /* the write bits start at bit 16 for the *getsock() bitmap */ #define GETSOCK_WRITEBITSTART 16 @@ -50,5 +62,43 @@ void Curl_multi_handlePipeBreak(struct SessionHandle *data); void Curl_multi_dump(const struct Curl_multi *multi_handle); #endif -#endif /* HEADER_CURL_MULTIIF_H */ +/* Update the current connection of a One_Easy handle */ +void Curl_multi_set_easy_connection(struct SessionHandle *handle, + struct connectdata *conn); + +void Curl_multi_process_pending_handles(struct Curl_multi *multi); + +/* Return the value of the CURLMOPT_MAX_HOST_CONNECTIONS option */ +size_t Curl_multi_max_host_connections(struct Curl_multi *multi); + +/* Return the value of the CURLMOPT_MAX_PIPELINE_LENGTH option */ +size_t Curl_multi_max_pipeline_length(struct Curl_multi *multi); + +/* Return the value of the CURLMOPT_CONTENT_LENGTH_PENALTY_SIZE option */ +curl_off_t Curl_multi_content_length_penalty_size(struct Curl_multi *multi); + +/* Return the value of the CURLMOPT_CHUNK_LENGTH_PENALTY_SIZE option */ +curl_off_t Curl_multi_chunk_length_penalty_size(struct Curl_multi *multi); +/* Return the value of the CURLMOPT_PIPELINING_SITE_BL option */ +struct curl_llist *Curl_multi_pipelining_site_bl(struct Curl_multi *multi); + +/* Return the value of the CURLMOPT_PIPELINING_SERVER_BL option */ +struct curl_llist *Curl_multi_pipelining_server_bl(struct Curl_multi *multi); + +/* Return the value of the CURLMOPT_MAX_TOTAL_CONNECTIONS option */ +size_t Curl_multi_max_total_connections(struct Curl_multi *multi); + +/* + * Curl_multi_closed() + * + * Used by the connect code to tell the multi_socket code that one of the + * sockets we were using have just been closed. This function will then + * remove it from the sockethash for this handle to make the multi_socket API + * behave properly, especially for the case when libcurl will create another + * socket again and it gets the same file descriptor number. + */ + +void Curl_multi_closed(struct connectdata *conn, curl_socket_t s); + +#endif /* HEADER_CURL_MULTIIF_H */ diff --git a/plugins/FTPFileYM/curl/lib/netrc.c b/plugins/FTPFileYM/curl/lib/netrc.c index 2c5942afc4..e35328a9ed 100644 --- a/plugins/FTPFileYM/curl/lib/netrc.c +++ b/plugins/FTPFileYM/curl/lib/netrc.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2012, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2013, 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 @@ -50,15 +50,18 @@ enum host_lookup_state { /* * @unittest: 1304 + * + * *loginp and *passwordp MUST be allocated if they aren't NULL when passed + * in. */ int Curl_parsenetrc(const char *host, - char *login, - char *password, + char **loginp, + char **passwordp, char *netrcfile) { FILE *file; int retcode=1; - int specific_login = (login[0] != 0); + int specific_login = (**loginp != 0); char *home = NULL; bool home_alloc = FALSE; bool netrc_alloc = FALSE; @@ -109,7 +112,7 @@ int Curl_parsenetrc(const char *host, tok=strtok_r(netrcbuffer, " \t\n", &tok_buf); while(!done && tok) { - if(login[0] && password[0]) { + if(**loginp && **passwordp) { done=TRUE; break; } @@ -138,16 +141,22 @@ int Curl_parsenetrc(const char *host, /* we are now parsing sub-keywords concerning "our" host */ if(state_login) { if(specific_login) { - state_our_login = Curl_raw_equal(login, tok); + state_our_login = Curl_raw_equal(*loginp, tok); } else { - strncpy(login, tok, LOGINSIZE-1); + free(*loginp); + *loginp = strdup(tok); + if(!*loginp) + return -1; /* allocation failed */ } state_login=0; } else if(state_password) { if(state_our_login || !specific_login) { - strncpy(password, tok, PASSWORDSIZE-1); + free(*passwordp); + *passwordp = strdup(tok); + if(!*passwordp) + return -1; /* allocation failed */ } state_password=0; } diff --git a/plugins/FTPFileYM/curl/lib/netrc.h b/plugins/FTPFileYM/curl/lib/netrc.h index 4db764df24..a145601148 100644 --- a/plugins/FTPFileYM/curl/lib/netrc.h +++ b/plugins/FTPFileYM/curl/lib/netrc.h @@ -22,19 +22,15 @@ * ***************************************************************************/ -/* Make sure we have room for at least this size: */ -#define LOGINSIZE 64 -#define PASSWORDSIZE 64 - /* returns -1 on failure, 0 if the host is found, 1 is the host isn't found */ int Curl_parsenetrc(const char *host, - char *login, - char *password, + char **loginp, + char **passwordp, char *filename); - /* Assume: password[0]=0, host[0] != 0. - * If login[0] = 0, search for login and password within a machine section - * in the netrc. - * If login[0] != 0, search for password within machine and login. + /* Assume: (*passwordp)[0]=0, host[0] != 0. + * If (*loginp)[0] = 0, search for login and password within a machine + * section in the netrc. + * If (*loginp)[0] != 0, search for password within machine and login. */ #endif /* HEADER_CURL_NETRC_H */ diff --git a/plugins/FTPFileYM/curl/lib/nss.c b/plugins/FTPFileYM/curl/lib/nss.c index 8a2cb09e66..2d4bf9e9c8 100644 --- a/plugins/FTPFileYM/curl/lib/nss.c +++ b/plugins/FTPFileYM/curl/lib/nss.c @@ -1237,12 +1237,6 @@ CURLcode Curl_nss_connect(struct connectdata *conn, int sockindex) goto error; model = SSL_ImportFD(NULL, model); - /* make the socket nonblocking */ - sock_opt.option = PR_SockOpt_Nonblocking; - sock_opt.value.non_blocking = PR_TRUE; - if(PR_SetSocketOption(model, &sock_opt) != PR_SUCCESS) - goto error; - if(SSL_OptionSet(model, SSL_SECURITY, PR_TRUE) != SECSuccess) goto error; if(SSL_OptionSet(model, SSL_HANDSHAKE_AS_SERVER, PR_FALSE) != SECSuccess) @@ -1415,6 +1409,12 @@ CURLcode Curl_nss_connect(struct connectdata *conn, int sockindex) goto error; } + /* switch the SSL socket into non-blocking mode */ + sock_opt.option = PR_SockOpt_Nonblocking; + sock_opt.value.non_blocking = PR_TRUE; + if(PR_SetSocketOption(connssl->handle, &sock_opt) != PR_SUCCESS) + goto error; + connssl->state = ssl_connection_complete; conn->recv[sockindex] = nss_recv; conn->send[sockindex] = nss_send; @@ -1482,10 +1482,8 @@ static ssize_t nss_send(struct connectdata *conn, /* connection data */ size_t len, /* amount to write */ CURLcode *curlcode) { - int rc; - - rc = PR_Send(conn->ssl[sockindex].handle, mem, (int)len, 0, -1); - + ssize_t rc = PR_Send(conn->ssl[sockindex].handle, mem, (int)len, 0, + PR_INTERVAL_NO_WAIT); if(rc < 0) { PRInt32 err = PR_GetError(); if(err == PR_WOULD_BLOCK_ERROR) @@ -1513,9 +1511,8 @@ static ssize_t nss_recv(struct connectdata * conn, /* connection data */ size_t buffersize, /* max amount to read */ CURLcode *curlcode) { - ssize_t nread; - - nread = PR_Recv(conn->ssl[num].handle, buf, (int)buffersize, 0, -1); + ssize_t nread = PR_Recv(conn->ssl[num].handle, buf, (int)buffersize, 0, + PR_INTERVAL_NO_WAIT); if(nread < 0) { /* failed SSL read */ PRInt32 err = PR_GetError(); @@ -1546,9 +1543,8 @@ size_t Curl_nss_version(char *buffer, size_t size) int Curl_nss_seed(struct SessionHandle *data) { - /* TODO: implement? */ - (void) data; - return 0; + /* make sure that NSS is initialized */ + return !!Curl_nss_force_init(data); } void Curl_nss_random(struct SessionHandle *data, @@ -1556,7 +1552,11 @@ void Curl_nss_random(struct SessionHandle *data, size_t length) { Curl_nss_seed(data); /* Initiate the seed if not already done */ - PK11_GenerateRandom(entropy, curlx_uztosi(length)); + if(SECSuccess != PK11_GenerateRandom(entropy, curlx_uztosi(length))) { + /* no way to signal a failure from here, we have to abort */ + failf(data, "PK11_GenerateRandom() failed, calling abort()..."); + abort(); + } } void Curl_nss_md5sum(unsigned char *tmp, /* input */ diff --git a/plugins/FTPFileYM/curl/lib/nssg.h b/plugins/FTPFileYM/curl/lib/nssg.h index a881a9ad2f..cd32706a71 100644 --- a/plugins/FTPFileYM/curl/lib/nssg.h +++ b/plugins/FTPFileYM/curl/lib/nssg.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2012, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2013, 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 @@ -60,6 +60,10 @@ void Curl_nss_md5sum(unsigned char *tmp, /* input */ unsigned char *md5sum, /* output */ size_t md5len); +/* this backend provides these functions: */ +#define have_curlssl_random 1 +#define have_curlssl_md5sum 1 + /* API setup for NSS */ #define curlssl_init Curl_nss_init #define curlssl_cleanup Curl_nss_cleanup diff --git a/plugins/FTPFileYM/curl/lib/openldap.c b/plugins/FTPFileYM/curl/lib/openldap.c index cb3b420205..98793b3067 100644 --- a/plugins/FTPFileYM/curl/lib/openldap.c +++ b/plugins/FTPFileYM/curl/lib/openldap.c @@ -378,7 +378,7 @@ static CURLcode ldap_do(struct connectdata *conn, bool *done) if(!lr) return CURLE_OUT_OF_MEMORY; lr->msgid = msgid; - data->state.proto.generic = lr; + data->req.protop = lr; Curl_setup_transfer(conn, FIRSTSOCKET, -1, FALSE, NULL, -1, NULL); *done = TRUE; return CURLE_OK; @@ -387,7 +387,7 @@ static CURLcode ldap_do(struct connectdata *conn, bool *done) static CURLcode ldap_done(struct connectdata *conn, CURLcode res, bool premature) { - ldapreqinfo *lr = conn->data->state.proto.generic; + ldapreqinfo *lr = conn->data->req.protop; (void)res; (void)premature; @@ -398,7 +398,7 @@ static CURLcode ldap_done(struct connectdata *conn, CURLcode res, ldap_abandon_ext(li->ld, lr->msgid, NULL, NULL); lr->msgid = 0; } - conn->data->state.proto.generic = NULL; + conn->data->req.protop = NULL; free(lr); } return CURLE_OK; @@ -409,7 +409,7 @@ static ssize_t ldap_recv(struct connectdata *conn, int sockindex, char *buf, { ldapconninfo *li = conn->proto.generic; struct SessionHandle *data=conn->data; - ldapreqinfo *lr = data->state.proto.generic; + ldapreqinfo *lr = data->req.protop; int rc, ret; LDAPMessage *result = NULL; LDAPMessage *ent; diff --git a/plugins/FTPFileYM/curl/lib/pingpong.c b/plugins/FTPFileYM/curl/lib/pingpong.c index 16b4ad37e4..3ebeabe537 100644 --- a/plugins/FTPFileYM/curl/lib/pingpong.c +++ b/plugins/FTPFileYM/curl/lib/pingpong.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2012, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2013, 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 @@ -33,6 +33,7 @@ #include "pingpong.h" #include "multiif.h" #include "non-ascii.h" +#include "sslgen.h" #define _MPRINTF_REPLACE /* use our functions only */ #include <curl/mprintf.h> @@ -76,48 +77,10 @@ long Curl_pp_state_timeout(struct pingpong *pp) return timeout_ms; } - -/* - * Curl_pp_multi_statemach() - * - * called repeatedly until done when the multi interface is used. - */ -CURLcode Curl_pp_multi_statemach(struct pingpong *pp) -{ - struct connectdata *conn = pp->conn; - curl_socket_t sock = conn->sock[FIRSTSOCKET]; - int rc; - struct SessionHandle *data=conn->data; - CURLcode result = CURLE_OK; - long timeout_ms = Curl_pp_state_timeout(pp); - - if(timeout_ms <= 0) { - failf(data, "server response timeout"); - return CURLE_OPERATION_TIMEDOUT; - } - - rc = Curl_socket_ready(pp->sendleft?CURL_SOCKET_BAD:sock, /* reading */ - pp->sendleft?sock:CURL_SOCKET_BAD, /* writing */ - 0); - - if(rc == -1) { - failf(data, "select/poll error"); - return CURLE_OUT_OF_MEMORY; - } - else if(rc != 0) - result = pp->statemach_act(conn); - - /* if rc == 0, then select() timed out */ - - return result; -} - /* - * Curl_pp_easy_statemach() - * - * called repeatedly until done when the easy interface is used. + * Curl_pp_statemach() */ -CURLcode Curl_pp_easy_statemach(struct pingpong *pp) +CURLcode Curl_pp_statemach(struct pingpong *pp, bool block) { struct connectdata *conn = pp->conn; curl_socket_t sock = conn->sock[FIRSTSOCKET]; @@ -125,29 +88,44 @@ CURLcode Curl_pp_easy_statemach(struct pingpong *pp) long interval_ms; long timeout_ms = Curl_pp_state_timeout(pp); struct SessionHandle *data=conn->data; - CURLcode result; + CURLcode result = CURLE_OK; if(timeout_ms <=0 ) { failf(data, "server response timeout"); return CURLE_OPERATION_TIMEDOUT; /* already too little time */ } - interval_ms = 1000; /* use 1 second timeout intervals */ - if(timeout_ms < interval_ms) - interval_ms = timeout_ms; - - rc = Curl_socket_ready(pp->sendleft?CURL_SOCKET_BAD:sock, /* reading */ - pp->sendleft?sock:CURL_SOCKET_BAD, /* writing */ - interval_ms); - - if(Curl_pgrsUpdate(conn)) - result = CURLE_ABORTED_BY_CALLBACK; + if(block) { + interval_ms = 1000; /* use 1 second timeout intervals */ + if(timeout_ms < interval_ms) + interval_ms = timeout_ms; + } + else + interval_ms = 0; /* immediate */ + + if(Curl_pp_moredata(pp)) + /* We are receiving and there is data in the cache so just read it */ + rc = 1; + else if(!pp->sendleft && Curl_ssl_data_pending(conn, FIRSTSOCKET)) + /* We are receiving and there is data ready in the SSL library */ + rc = 1; else - result = Curl_speedcheck(data, Curl_tvnow()); + rc = Curl_socket_ready(pp->sendleft?CURL_SOCKET_BAD:sock, /* reading */ + pp->sendleft?sock:CURL_SOCKET_BAD, /* writing */ + interval_ms); + + if(block) { + /* if we didn't wait, we don't have to spend time on this now */ + if(Curl_pgrsUpdate(conn)) + result = CURLE_ABORTED_BY_CALLBACK; + else + result = Curl_speedcheck(data, Curl_tvnow()); + + if(result) + return result; + } - if(result) - ; - else if(rc == -1) { + if(rc == -1) { failf(data, "select/poll error"); result = CURLE_OUT_OF_MEMORY; } @@ -191,7 +169,7 @@ CURLcode Curl_pp_vsendf(struct pingpong *pp, struct connectdata *conn = pp->conn; struct SessionHandle *data = conn->data; -#if defined(HAVE_KRB4) || defined(HAVE_GSSAPI) +#ifdef HAVE_GSSAPI enum protection_level data_sec = conn->data_prot; #endif @@ -220,12 +198,12 @@ CURLcode Curl_pp_vsendf(struct pingpong *pp, return error; } -#if defined(HAVE_KRB4) || defined(HAVE_GSSAPI) +#ifdef HAVE_GSSAPI conn->data_prot = PROT_CMD; #endif error = Curl_write(conn, conn->sock[FIRSTSOCKET], s, write_len, &bytes_written); -#if defined(HAVE_KRB4) || defined(HAVE_GSSAPI) +#ifdef HAVE_GSSAPI DEBUGASSERT(data_sec > PROT_NONE && data_sec < PROT_LAST); conn->data_prot = data_sec; #endif @@ -328,14 +306,14 @@ CURLcode Curl_pp_readresp(curl_socket_t sockfd, } else { int res; -#if defined(HAVE_KRB4) || defined(HAVE_GSSAPI) +#ifdef HAVE_GSSAPI enum protection_level prot = conn->data_prot; conn->data_prot = PROT_CLEAR; #endif DEBUGASSERT((ptr+BUFSIZE-pp->nread_resp) <= (buf+BUFSIZE+1)); res = Curl_read(conn, sockfd, ptr, BUFSIZE-pp->nread_resp, &gotbytes); -#if defined(HAVE_KRB4) || defined(HAVE_GSSAPI) +#ifdef HAVE_GSSAPI DEBUGASSERT(prot > PROT_NONE && prot < PROT_LAST); conn->data_prot = prot; #endif @@ -378,7 +356,7 @@ CURLcode Curl_pp_readresp(curl_socket_t sockfd, the line isn't really terminated until the LF comes */ /* output debug output if that is requested */ -#if defined(HAVE_KRB4) || defined(HAVE_GSSAPI) +#ifdef HAVE_GSSAPI if(!conn->sec_complete) #endif if(data->set.verbose) @@ -395,10 +373,9 @@ CURLcode Curl_pp_readresp(curl_socket_t sockfd, if(result) return result; - if(pp->endofresp(pp, code)) { + if(pp->endofresp(conn, pp->linestart_resp, perline, code)) { /* This is the end of the last line, copy the last line to the - start of the buffer and zero terminate, for old times sake (and - krb4)! */ + start of the buffer and zero terminate, for old times sake */ char *meow; int n; for(meow=pp->linestart_resp, n=0; meow<ptr; meow++, n++) @@ -533,6 +510,10 @@ CURLcode Curl_pp_disconnect(struct pingpong *pp) return CURLE_OK; } - +bool Curl_pp_moredata(struct pingpong *pp) +{ + return (!pp->sendleft && pp->cache && pp->nread_resp < pp->cache_size) ? + TRUE : FALSE; +} #endif diff --git a/plugins/FTPFileYM/curl/lib/pingpong.h b/plugins/FTPFileYM/curl/lib/pingpong.h index b48c1ed61a..b925ab98e1 100644 --- a/plugins/FTPFileYM/curl/lib/pingpong.h +++ b/plugins/FTPFileYM/curl/lib/pingpong.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2011, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2013, 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 @@ -32,6 +32,13 @@ /* forward-declaration, this is defined in urldata.h */ struct connectdata; +typedef enum { + FTPTRANSFER_BODY, /* yes do transfer a body */ + FTPTRANSFER_INFO, /* do still go through to get info/headers */ + FTPTRANSFER_NONE, /* don't get anything and don't get info */ + FTPTRANSFER_LAST /* end of list marker, never used */ +} curl_pp_transfer; + /* * 'pingpong' is the generic struct used for protocols doing server<->client * conversations in a back-and-forth style such as FTP, IMAP, POP3, SMTP etc. @@ -64,23 +71,17 @@ struct pingpong { CURLcode (*statemach_act)(struct connectdata *conn); - int (*endofresp)(struct pingpong *pp, int *code); + bool (*endofresp)(struct connectdata *conn, char *ptr, size_t len, + int *code); }; /* - * Curl_pp_multi_statemach() - * - * called repeatedly until done when the multi interface is used. - */ -CURLcode Curl_pp_multi_statemach(struct pingpong *pp); - -/* - * Curl_pp_easy_statemach() + * Curl_pp_statemach() * - * called repeatedly until done when the easy interface is used. + * called repeatedly until done. Set 'wait' to make it wait a while on the + * socket if there's no traffic. */ -CURLcode Curl_pp_easy_statemach(struct pingpong *pp); - +CURLcode Curl_pp_statemach(struct pingpong *pp, bool block); /* initialize stuff to prepare for reading a fresh new response */ void Curl_pp_init(struct pingpong *pp); @@ -136,4 +137,14 @@ CURLcode Curl_pp_disconnect(struct pingpong *pp); int Curl_pp_getsock(struct pingpong *pp, curl_socket_t *socks, int numsocks); + +/*********************************************************************** + * + * Curl_pp_moredata() + * + * Returns whether there are still more data in the cache and so a call + * to Curl_pp_readresp() will not block. + */ +bool Curl_pp_moredata(struct pingpong *pp); + #endif /* HEADER_CURL_PINGPONG_H */ diff --git a/plugins/FTPFileYM/curl/lib/pipeline.c b/plugins/FTPFileYM/curl/lib/pipeline.c new file mode 100644 index 0000000000..418058afa0 --- /dev/null +++ b/plugins/FTPFileYM/curl/lib/pipeline.c @@ -0,0 +1,333 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 2013, Linus Nielsen Feltzing, <linus@haxx.se> + * Copyright (C) 2013, 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 + * are also available at http://curl.haxx.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ***************************************************************************/ + +#include "curl_setup.h" + +#include <curl/curl.h> + +#include "urldata.h" +#include "url.h" +#include "progress.h" +#include "multiif.h" +#include "pipeline.h" +#include "sendf.h" +#include "rawstr.h" +#include "bundles.h" + +#include "curl_memory.h" +/* The last #include file should be: */ +#include "memdebug.h" + +struct site_blacklist_entry { + char *hostname; + unsigned short port; +}; + +static void site_blacklist_llist_dtor(void *user, void *element) +{ + struct site_blacklist_entry *entry = element; + (void)user; + + Curl_safefree(entry->hostname); + Curl_safefree(entry); +} + +static void server_blacklist_llist_dtor(void *user, void *element) +{ + char *server_name = element; + (void)user; + + Curl_safefree(server_name); +} + +bool Curl_pipeline_penalized(struct SessionHandle *data, + struct connectdata *conn) +{ + if(data) { + bool penalized = FALSE; + curl_off_t penalty_size = + Curl_multi_content_length_penalty_size(data->multi); + curl_off_t chunk_penalty_size = + Curl_multi_chunk_length_penalty_size(data->multi); + curl_off_t recv_size = -2; /* Make it easy to spot in the log */ + + /* Find the head of the recv pipe, if any */ + if(conn->recv_pipe && conn->recv_pipe->head) { + struct SessionHandle *recv_handle = conn->recv_pipe->head->ptr; + + recv_size = recv_handle->req.size; + + if(penalty_size > 0 && recv_size > penalty_size) + penalized = TRUE; + } + + if(chunk_penalty_size > 0 && + (curl_off_t)conn->chunk.datasize > chunk_penalty_size) + penalized = TRUE; + + infof(data, "Conn: %ld (%p) Receive pipe weight: (%" FORMAT_OFF_T + "/%zu), penalized: %s\n", + conn->connection_id, (void *)conn, recv_size, + conn->chunk.datasize, penalized?"TRUE":"FALSE"); + return penalized; + } + return FALSE; +} + +CURLcode Curl_add_handle_to_pipeline(struct SessionHandle *handle, + struct connectdata *conn) +{ + struct curl_llist_element *sendhead = conn->send_pipe->head; + struct curl_llist *pipeline; + CURLcode rc; + + pipeline = conn->send_pipe; + + infof(conn->data, "Adding handle: conn: %p\n", (void *)conn); + infof(conn->data, "Adding handle: send: %d\n", conn->send_pipe->size); + infof(conn->data, "Adding handle: recv: %d\n", conn->recv_pipe->size); + rc = Curl_addHandleToPipeline(handle, pipeline); + + if(pipeline == conn->send_pipe && sendhead != conn->send_pipe->head) { + /* this is a new one as head, expire it */ + conn->writechannel_inuse = FALSE; /* not in use yet */ +#ifdef DEBUGBUILD + infof(conn->data, "%p is at send pipe head!\n", + (void *)conn->send_pipe->head->ptr); +#endif + Curl_expire(conn->send_pipe->head->ptr, 1); + } + + print_pipeline(conn); + + return rc; +} + +/* Move this transfer from the sending list to the receiving list. + + Pay special attention to the new sending list "leader" as it needs to get + checked to update what sockets it acts on. + +*/ +void Curl_move_handle_from_send_to_recv_pipe(struct SessionHandle *handle, + struct connectdata *conn) +{ + struct curl_llist_element *curr; + + curr = conn->send_pipe->head; + while(curr) { + if(curr->ptr == handle) { + Curl_llist_move(conn->send_pipe, curr, + conn->recv_pipe, conn->recv_pipe->tail); + + if(conn->send_pipe->head) { + /* Since there's a new easy handle at the start of the send pipeline, + set its timeout value to 1ms to make it trigger instantly */ + conn->writechannel_inuse = FALSE; /* not used now */ +#ifdef DEBUGBUILD + infof(conn->data, "%p is at send pipe head B!\n", + (void *)conn->send_pipe->head->ptr); +#endif + Curl_expire(conn->send_pipe->head->ptr, 1); + } + + /* The receiver's list is not really interesting here since either this + handle is now first in the list and we'll deal with it soon, or + another handle is already first and thus is already taken care of */ + + break; /* we're done! */ + } + curr = curr->next; + } +} + +bool Curl_pipeline_site_blacklisted(struct SessionHandle *handle, + struct connectdata *conn) +{ + if(handle->multi) { + struct curl_llist *blacklist = + Curl_multi_pipelining_site_bl(handle->multi); + + if(blacklist) { + struct curl_llist_element *curr; + + curr = blacklist->head; + while(curr) { + struct site_blacklist_entry *site; + + site = curr->ptr; + if(Curl_raw_equal(site->hostname, conn->host.name) && + site->port == conn->remote_port) { + infof(handle, "Site %s:%d is pipeline blacklisted\n", + conn->host.name, conn->remote_port); + return TRUE; + } + curr = curr->next; + } + } + } + return FALSE; +} + +CURLMcode Curl_pipeline_set_site_blacklist(char **sites, + struct curl_llist **list_ptr) +{ + struct curl_llist *old_list = *list_ptr; + struct curl_llist *new_list = NULL; + + if(sites) { + new_list = Curl_llist_alloc((curl_llist_dtor) site_blacklist_llist_dtor); + if(!new_list) + return CURLM_OUT_OF_MEMORY; + + /* Parse the URLs and populate the list */ + while(*sites) { + char *hostname; + char *port; + struct site_blacklist_entry *entry; + + entry = malloc(sizeof(struct site_blacklist_entry)); + + hostname = strdup(*sites); + if(!hostname) + return CURLM_OUT_OF_MEMORY; + + port = strchr(hostname, ':'); + if(port) { + *port = '\0'; + port++; + entry->port = (unsigned short)strtol(port, NULL, 10); + } + else { + /* Default port number for HTTP */ + entry->port = 80; + } + + entry->hostname = hostname; + + if(!Curl_llist_insert_next(new_list, new_list->tail, entry)) + return CURLM_OUT_OF_MEMORY; + + sites++; + } + } + + /* Free the old list */ + if(old_list) { + Curl_llist_destroy(old_list, NULL); + } + + /* This might be NULL if sites == NULL, i.e the blacklist is cleared */ + *list_ptr = new_list; + + return CURLM_OK; +} + +bool Curl_pipeline_server_blacklisted(struct SessionHandle *handle, + char *server_name) +{ + if(handle->multi) { + struct curl_llist *blacklist = + Curl_multi_pipelining_server_bl(handle->multi); + + if(blacklist) { + struct curl_llist_element *curr; + + curr = blacklist->head; + while(curr) { + char *bl_server_name; + + bl_server_name = curr->ptr; + if(Curl_raw_nequal(bl_server_name, server_name, + strlen(bl_server_name))) { + infof(handle, "Server %s is blacklisted\n", server_name); + return TRUE; + } + curr = curr->next; + } + } + + infof(handle, "Server %s is not blacklisted\n", server_name); + } + return FALSE; +} + +CURLMcode Curl_pipeline_set_server_blacklist(char **servers, + struct curl_llist **list_ptr) +{ + struct curl_llist *old_list = *list_ptr; + struct curl_llist *new_list = NULL; + + if(servers) { + new_list = Curl_llist_alloc((curl_llist_dtor) server_blacklist_llist_dtor); + if(!new_list) + return CURLM_OUT_OF_MEMORY; + + /* Parse the URLs and populate the list */ + while(*servers) { + char *server_name; + + server_name = strdup(*servers); + if(!server_name) + return CURLM_OUT_OF_MEMORY; + + if(!Curl_llist_insert_next(new_list, new_list->tail, server_name)) + return CURLM_OUT_OF_MEMORY; + + servers++; + } + } + + /* Free the old list */ + if(old_list) { + Curl_llist_destroy(old_list, NULL); + } + + /* This might be NULL if sites == NULL, i.e the blacklist is cleared */ + *list_ptr = new_list; + + return CURLM_OK; +} + + +void print_pipeline(struct connectdata *conn) +{ + struct curl_llist_element *curr; + struct connectbundle *cb_ptr; + struct SessionHandle *data = conn->data; + + cb_ptr = conn->bundle; + + if(cb_ptr) { + curr = cb_ptr->conn_list->head; + while(curr) { + conn = curr->ptr; + infof(data, "- Conn %ld (%p) send_pipe: %zu, recv_pipe: %zu\n", + conn->connection_id, + (void *)conn, + conn->send_pipe->size, + conn->recv_pipe->size); + curr = curr->next; + } + } +} diff --git a/plugins/FTPFileYM/curl/lib/pipeline.h b/plugins/FTPFileYM/curl/lib/pipeline.h new file mode 100644 index 0000000000..1df9589535 --- /dev/null +++ b/plugins/FTPFileYM/curl/lib/pipeline.h @@ -0,0 +1,46 @@ +#ifndef HEADER_CURL_PIPELINE_H +#define HEADER_CURL_PIPELINE_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 2013, Linus Nielsen Feltzing, <linus@haxx.se> + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at http://curl.haxx.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ***************************************************************************/ + +CURLcode Curl_add_handle_to_pipeline(struct SessionHandle *handle, + struct connectdata *conn); +void Curl_move_handle_from_send_to_recv_pipe(struct SessionHandle *handle, + struct connectdata *conn); +bool Curl_pipeline_penalized(struct SessionHandle *data, + struct connectdata *conn); + +bool Curl_pipeline_site_blacklisted(struct SessionHandle *handle, + struct connectdata *conn); + +CURLMcode Curl_pipeline_set_site_blacklist(char **sites, + struct curl_llist **list_ptr); + +bool Curl_pipeline_server_blacklisted(struct SessionHandle *handle, + char *server_name); + +CURLMcode Curl_pipeline_set_server_blacklist(char **servers, + struct curl_llist **list_ptr); + +void print_pipeline(struct connectdata *conn); + +#endif /* HEADER_CURL_PIPELINE_H */ diff --git a/plugins/FTPFileYM/curl/lib/polarssl.c b/plugins/FTPFileYM/curl/lib/polarssl.c index f4ca63e116..808f54c243 100644 --- a/plugins/FTPFileYM/curl/lib/polarssl.c +++ b/plugins/FTPFileYM/curl/lib/polarssl.c @@ -5,8 +5,8 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 2010, 2011, Hoi-Ho Chan, <hoiho.chan@gmail.com> - * Copyright (C) 2012, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 2010 - 2011, Hoi-Ho Chan, <hoiho.chan@gmail.com> + * Copyright (C) 2012 - 2013, 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 @@ -33,13 +33,21 @@ #include <polarssl/net.h> #include <polarssl/ssl.h> -#include <polarssl/havege.h> #include <polarssl/certs.h> #include <polarssl/x509.h> #include <polarssl/version.h> +#if POLARSSL_VERSION_NUMBER >= 0x01000000 +#include <polarssl/error.h> +#endif /* POLARSSL_VERSION_NUMBER >= 0x01000000 */ + +#if POLARSSL_VERSION_NUMBER>0x01010000 #include <polarssl/entropy.h> #include <polarssl/ctr_drbg.h> +#else +#include <polarssl/havege.h> +#endif /* POLARSSL_VERSION_NUMBER>0x01010000 */ + #if POLARSSL_VERSION_NUMBER<0x01000000 /* @@ -58,6 +66,7 @@ #include "connect.h" /* for the connect timeout */ #include "select.h" #include "rawstr.h" +#include "polarssl_threadlock.h" #define _MPRINTF_REPLACE /* use our functions only */ #include <curl/mprintf.h> @@ -65,15 +74,44 @@ /* The last #include file should be: */ #include "memdebug.h" -/* version dependent differences */ -#if POLARSSL_VERSION_NUMBER < 0x01010000 -/* the old way */ -#define HAVEGE_RANDOM havege_rand -#else -/* from 1.1.0 */ -#define HAVEGE_RANDOM havege_random +/* apply threading? */ +#if defined(USE_THREADS_POSIX) || defined(USE_THREADS_WIN32) +#define THREADING_SUPPORT #endif +#if defined(THREADING_SUPPORT) && POLARSSL_VERSION_NUMBER>0x01010000 +static entropy_context entropy; + +static int entropy_init_initialized = 0; + +/* start of entropy_init_mutex() */ +static void entropy_init_mutex(entropy_context *ctx) +{ + /* lock 0 = entropy_init_mutex() */ + polarsslthreadlock_lock_function(0); + if(entropy_init_initialized == 0) { + entropy_init(ctx); + entropy_init_initialized = 1; + } + polarsslthreadlock_unlock_function(0); +} +/* end of entropy_init_mutex() */ + +/* start of entropy_func_mutex() */ +static int entropy_func_mutex(void *data, unsigned char *output, size_t len) +{ + int ret; + /* lock 1 = entropy_func_mutex() */ + polarsslthreadlock_lock_function(1); + ret = entropy_func(data, output, len); + polarsslthreadlock_unlock_function(1); + + return ret; +} +/* end of entropy_func_mutex() */ + +#endif /* THREADING_SUPPORT && POLARSSL_VERSION_NUMBER>0x01010000 */ + /* Define this to enable lots of debugging for PolarSSL */ #undef POLARSSL_DEBUG @@ -113,6 +151,10 @@ polarssl_connect_step1(struct connectdata *conn, void *old_session = NULL; size_t old_session_size = 0; + char errorbuf[128]; + memset(errorbuf, 0, sizeof(errorbuf)); + + /* PolarSSL only supports SSLv3 and TLSv1 */ if(data->set.ssl.version == CURL_SSLVERSION_SSLv2) { failf(data, "PolarSSL does not support SSLv2"); @@ -121,7 +163,33 @@ polarssl_connect_step1(struct connectdata *conn, else if(data->set.ssl.version == CURL_SSLVERSION_SSLv3) sni = FALSE; /* SSLv3 has no SNI */ +#if POLARSSL_VERSION_NUMBER<0x01010000 havege_init(&connssl->hs); +#else +#ifdef THREADING_SUPPORT + entropy_init_mutex(&entropy); + + if((ret = ctr_drbg_init(&connssl->ctr_drbg, entropy_func_mutex, &entropy, + connssl->ssn.id, connssl->ssn.length)) != 0) { +#ifdef POLARSSL_ERROR_C + error_strerror(ret, errorbuf, sizeof(errorbuf)); +#endif /* POLARSSL_ERROR_C */ + failf(data, "Failed - PolarSSL: ctr_drbg_init returned (-0x%04X) %s\n", + -ret, errorbuf); + } +#else + entropy_init(&connssl->entropy); + + if((ret = ctr_drbg_init(&connssl->ctr_drbg, entropy_func, &connssl->entropy, + connssl->ssn.id, connssl->ssn.length)) != 0) { +#ifdef POLARSSL_ERROR_C + error_strerror(ret, errorbuf, sizeof(errorbuf)); +#endif /* POLARSSL_ERROR_C */ + failf(data, "Failed - PolarSSL: ctr_drbg_init returned (-0x%04X) %s\n", + -ret, errorbuf); + } +#endif /* THREADING_SUPPORT */ +#endif /* POLARSSL_VERSION_NUMBER<0x01010000 */ /* Load the trusted CA */ memset(&connssl->cacert, 0, sizeof(x509_cert)); @@ -131,8 +199,11 @@ polarssl_connect_step1(struct connectdata *conn, data->set.str[STRING_SSL_CAFILE]); if(ret<0) { - failf(data, "Error reading ca cert file %s: -0x%04X", - data->set.str[STRING_SSL_CAFILE], ret); +#ifdef POLARSSL_ERROR_C + error_strerror(ret, errorbuf, sizeof(errorbuf)); +#endif /* POLARSSL_ERROR_C */ + failf(data, "Error reading ca cert file %s - PolarSSL: (-0x%04X) %s", + data->set.str[STRING_SSL_CAFILE], -ret, errorbuf); if(data->set.ssl.verifypeer) return CURLE_SSL_CACERT_BADFILE; @@ -147,8 +218,12 @@ polarssl_connect_step1(struct connectdata *conn, data->set.str[STRING_CERT]); if(ret) { - failf(data, "Error reading client cert file %s: -0x%04X", - data->set.str[STRING_CERT], -ret); +#ifdef POLARSSL_ERROR_C + error_strerror(ret, errorbuf, sizeof(errorbuf)); +#endif /* POLARSSL_ERROR_C */ + failf(data, "Error reading client cert file %s - PolarSSL: (-0x%04X) %s", + data->set.str[STRING_CERT], -ret, errorbuf); + return CURLE_SSL_CERTPROBLEM; } } @@ -160,8 +235,12 @@ polarssl_connect_step1(struct connectdata *conn, data->set.str[STRING_KEY_PASSWD]); if(ret) { - failf(data, "Error reading private key %s: -0x%04X", - data->set.str[STRING_KEY], -ret); +#ifdef POLARSSL_ERROR_C + error_strerror(ret, errorbuf, sizeof(errorbuf)); +#endif /* POLARSSL_ERROR_C */ + failf(data, "Error reading private key %s - PolarSSL: (-0x%04X) %s", + data->set.str[STRING_KEY], -ret, errorbuf); + return CURLE_SSL_CERTPROBLEM; } } @@ -174,8 +253,12 @@ polarssl_connect_step1(struct connectdata *conn, data->set.str[STRING_SSL_CRLFILE]); if(ret) { - failf(data, "Error reading CRL file %s: -0x%04X", - data->set.str[STRING_SSL_CRLFILE], -ret); +#ifdef POLARSSL_ERROR_C + error_strerror(ret, errorbuf, sizeof(errorbuf)); +#endif /* POLARSSL_ERROR_C */ + failf(data, "Error reading CRL file %s - PolarSSL: (-0x%04X) %s", + data->set.str[STRING_SSL_CRLFILE], -ret, errorbuf); + return CURLE_SSL_CRL_BADFILE; } } @@ -191,8 +274,13 @@ polarssl_connect_step1(struct connectdata *conn, ssl_set_endpoint(&connssl->ssl, SSL_IS_CLIENT); ssl_set_authmode(&connssl->ssl, SSL_VERIFY_OPTIONAL); - ssl_set_rng(&connssl->ssl, HAVEGE_RANDOM, +#if POLARSSL_VERSION_NUMBER<0x01010000 + ssl_set_rng(&connssl->ssl, havege_rand, &connssl->hs); +#else + ssl_set_rng(&connssl->ssl, ctr_drbg_random, + &connssl->ctr_drbg); +#endif /* POLARSSL_VERSION_NUMBER<0x01010000 */ ssl_set_bio(&connssl->ssl, net_recv, &conn->sock[sockindex], net_send, &conn->sock[sockindex]); @@ -253,6 +341,9 @@ polarssl_connect_step2(struct connectdata *conn, struct ssl_connect_data* connssl = &conn->ssl[sockindex]; char buffer[1024]; + char errorbuf[128]; + memset(errorbuf, 0, sizeof(errorbuf)); + conn->recv[sockindex] = polarssl_recv; conn->send[sockindex] = polarssl_send; @@ -261,8 +352,13 @@ polarssl_connect_step2(struct connectdata *conn, break; else if(ret != POLARSSL_ERR_NET_WANT_READ && ret != POLARSSL_ERR_NET_WANT_WRITE) { - failf(data, "ssl_handshake returned -0x%04X", -ret); - return CURLE_SSL_CONNECT_ERROR; +#ifdef POLARSSL_ERROR_C + error_strerror(ret, errorbuf, sizeof(errorbuf)); +#endif /* POLARSSL_ERROR_C */ + failf(data, "ssl_handshake returned - PolarSSL: (-0x%04X) %s", + -ret, errorbuf); + + return CURLE_SSL_CONNECT_ERROR; } else { if(ret == POLARSSL_ERR_NET_WANT_READ) { @@ -593,4 +689,18 @@ Curl_polarssl_connect(struct connectdata *conn, return CURLE_OK; } -#endif +/* + * return 0 error initializing SSL + * return 1 SSL initialized successfully + */ +int polarssl_init(void) +{ + return polarsslthreadlock_thread_setup(); +} + +void polarssl_cleanup(void) +{ + (void)polarsslthreadlock_thread_cleanup(); +} + +#endif /* USE_POLARSSL */ diff --git a/plugins/FTPFileYM/curl/lib/polarssl.h b/plugins/FTPFileYM/curl/lib/polarssl.h index 12b3db28c0..af3b28b89a 100644 --- a/plugins/FTPFileYM/curl/lib/polarssl.h +++ b/plugins/FTPFileYM/curl/lib/polarssl.h @@ -25,6 +25,11 @@ #ifdef USE_POLARSSL +/* Called on first use PolarSSL, setup threading if supported */ +int polarssl_init(void); +void polarssl_cleanup(void); + + CURLcode Curl_polarssl_connect(struct connectdata *conn, int sockindex); CURLcode Curl_polarssl_connect_nonblocking(struct connectdata *conn, @@ -43,8 +48,8 @@ size_t Curl_polarssl_version(char *buffer, size_t size); int Curl_polarssl_shutdown(struct connectdata *conn, int sockindex); /* API setup for PolarSSL */ -#define curlssl_init() (1) -#define curlssl_cleanup() Curl_nop_stmt +#define curlssl_init() polarssl_init() +#define curlssl_cleanup() polarssl_cleanup() #define curlssl_connect Curl_polarssl_connect #define curlssl_connect_nonblocking Curl_polarssl_connect_nonblocking #define curlssl_session_free(x) Curl_polarssl_session_free(x) diff --git a/plugins/FTPFileYM/curl/lib/polarssl_threadlock.c b/plugins/FTPFileYM/curl/lib/polarssl_threadlock.c new file mode 100644 index 0000000000..ad18715375 --- /dev/null +++ b/plugins/FTPFileYM/curl/lib/polarssl_threadlock.c @@ -0,0 +1,156 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 2010, 2011, Hoi-Ho Chan, <hoiho.chan@gmail.com> + * Copyright (C) 2013, 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 + * are also available at http://curl.haxx.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ***************************************************************************/ +#include "curl_setup.h" + +#if defined(USE_POLARSSL) && \ + (defined(USE_THREADS_POSIX) || defined(USE_THREADS_WIN32)) + +#if defined(USE_THREADS_POSIX) +# ifdef HAVE_PTHREAD_H +# include <pthread.h> +# endif +#elif defined(USE_THREADS_WIN32) +# ifdef HAVE_PROCESS_H +# include <process.h> +# endif +#endif + +#include "polarssl_threadlock.h" + +#define _MPRINTF_REPLACE /* use our functions only */ +#include <curl/mprintf.h> + +#include "curl_memory.h" +/* The last #include file should be: */ +#include "memdebug.h" + +/* number of thread locks */ +#define NUMT 2 + +/* This array will store all of the mutexes available to PolarSSL. */ +static POLARSSL_MUTEX_T *mutex_buf = NULL; + +int polarsslthreadlock_thread_setup(void) +{ + int i; + int ret; + + mutex_buf = malloc(NUMT * sizeof(POLARSSL_MUTEX_T)); + if(!mutex_buf) + return 0; /* error, no number of threads defined */ + +#ifdef HAVE_PTHREAD_H + for(i = 0; i < NUMT; i++) { + ret = pthread_mutex_init(&mutex_buf[i], NULL); + if(ret) + return 0; /* pthread_mutex_init failed */ + } +#elif defined(HAVE_PROCESS_H) + for(i = 0; i < NUMT; i++) { + mutex_buf[i] = CreateMutex(0, FALSE, 0); + if(mutex_buf[i] == 0) + return 0; /* CreateMutex failed */ + } +#endif /* HAVE_PTHREAD_H */ + + return 1; /* OK */ +} + +int polarsslthreadlock_thread_cleanup(void) +{ + int i; + int ret; + + if(!mutex_buf) + return 0; /* error, no threads locks defined */ + +#ifdef HAVE_PTHREAD_H + for(i = 0; i < NUMT; i++) { + ret = pthread_mutex_destroy(&mutex_buf[i]); + if(ret) + return 0; /* pthread_mutex_destroy failed */ + } +#elif defined(HAVE_PROCESS_H) + for(i = 0; i < NUMT; i++) { + ret = CloseHandle(mutex_buf[i]); + if(!ret) + return 0; /* CloseHandle failed */ + } +#endif /* HAVE_PTHREAD_H */ + free(mutex_buf); + mutex_buf = NULL; + + return 1; /* OK */ +} + +int polarsslthreadlock_lock_function(int n) +{ + int ret; +#ifdef HAVE_PTHREAD_H + if(n < NUMT) { + ret = pthread_mutex_lock(&mutex_buf[n]); + if(ret) { + DEBUGF(fprintf(stderr, + "Error: polarsslthreadlock_lock_function failed\n")); + return 0; /* pthread_mutex_lock failed */ + } + } +#elif defined(HAVE_PROCESS_H) + if(n < NUMT) { + ret = (WaitForSingleObject(mutex_buf[n], INFINITE)==WAIT_FAILED?1:0); + if(ret) { + DEBUGF(fprintf(stderr, + "Error: polarsslthreadlock_lock_function failed\n")); + return 0; /* pthread_mutex_lock failed */ + } + } +#endif /* HAVE_PTHREAD_H */ + return 1; /* OK */ +} + +int polarsslthreadlock_unlock_function(int n) +{ + int ret; +#ifdef HAVE_PTHREAD_H + if(n < NUMT) { + ret = pthread_mutex_unlock(&mutex_buf[n]); + if(ret) { + DEBUGF(fprintf(stderr, + "Error: polarsslthreadlock_unlock_function failed\n")); + return 0; /* pthread_mutex_unlock failed */ + } + } +#elif defined(HAVE_PROCESS_H) + if(n < NUMT) { + ret = ReleaseMutex(mutex_buf[n]); + if(!ret) { + DEBUGF(fprintf(stderr, + "Error: polarsslthreadlock_unlock_function failed\n")); + return 0; /* pthread_mutex_lock failed */ + } + } +#endif /* HAVE_PTHREAD_H */ + return 1; /* OK */ +} + +#endif /* USE_POLARSSL */ diff --git a/plugins/FTPFileYM/curl/lib/polarssl_threadlock.h b/plugins/FTPFileYM/curl/lib/polarssl_threadlock.h new file mode 100644 index 0000000000..b67b3f9abd --- /dev/null +++ b/plugins/FTPFileYM/curl/lib/polarssl_threadlock.h @@ -0,0 +1,53 @@ +#ifndef HEADER_CURL_POLARSSL_THREADLOCK_H +#define HEADER_CURL_POLARSSL_THREADLOCK_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 2010, Hoi-Ho Chan, <hoiho.chan@gmail.com> + * Copyright (C) 2013, 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 + * are also available at http://curl.haxx.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ***************************************************************************/ +#include "curl_setup.h" + +#ifdef USE_POLARSSL + +#if defined(USE_THREADS_POSIX) +# define POLARSSL_MUTEX_T pthread_mutex_t +#elif defined(USE_THREADS_WIN32) +# define POLARSSL_MUTEX_T HANDLE +#endif + +#if defined(USE_THREADS_POSIX) || defined(USE_THREADS_WIN32) + +int polarsslthreadlock_thread_setup(void); +int polarsslthreadlock_thread_cleanup(void); +int polarsslthreadlock_lock_function(int n); +int polarsslthreadlock_unlock_function(int n); + +#else + +#define polarsslthreadlock_thread_setup() 1 +#define polarsslthreadlock_thread_cleanup() 1 +#define polarsslthreadlock_lock_function(x) 1 +#define polarsslthreadlock_unlock_function(x) 1 + +#endif /* USE_THREADS_POSIX || USE_THREADS_WIN32 */ + +#endif /* USE_POLARSSL */ + +#endif /* HEADER_CURL_POLARSSL_THREADLOCK_H */ diff --git a/plugins/FTPFileYM/curl/lib/pop3.c b/plugins/FTPFileYM/curl/lib/pop3.c index f0c6155c02..a771933840 100644 --- a/plugins/FTPFileYM/curl/lib/pop3.c +++ b/plugins/FTPFileYM/curl/lib/pop3.c @@ -27,6 +27,8 @@ * RFC2831 DIGEST-MD5 authentication * RFC4422 Simple Authentication and Security Layer (SASL) * RFC4616 PLAIN authentication + * RFC5034 POP3 SASL Authentication Mechanism + * RFC6749 OAuth 2.0 Authorization Framework * ***************************************************************************/ @@ -89,8 +91,6 @@ #include "memdebug.h" /* Local API functions */ -static CURLcode pop3_parse_url_path(struct connectdata *conn); -static CURLcode pop3_parse_custom_request(struct connectdata *conn); static CURLcode pop3_regular_transfer(struct connectdata *conn, bool *done); static CURLcode pop3_do(struct connectdata *conn, bool *done); static CURLcode pop3_done(struct connectdata *conn, CURLcode status, @@ -102,7 +102,9 @@ static int pop3_getsock(struct connectdata *conn, curl_socket_t *socks, int numsocks); static CURLcode pop3_doing(struct connectdata *conn, bool *dophase_done); static CURLcode pop3_setup_connection(struct connectdata *conn); -static CURLcode pop3_state_upgrade_tls(struct connectdata *conn); +static CURLcode pop3_parse_url_options(struct connectdata *conn); +static CURLcode pop3_parse_url_path(struct connectdata *conn); +static CURLcode pop3_parse_custom_request(struct connectdata *conn); /* * POP3 protocol handler. @@ -162,7 +164,7 @@ const struct Curl_handler Curl_handler_pop3s = { static const struct Curl_handler Curl_handler_pop3_proxy = { "POP3", /* scheme */ - ZERO_NULL, /* setup_connection */ + Curl_http_setup_conn, /* setup_connection */ Curl_http, /* do_it */ Curl_http_done, /* done */ ZERO_NULL, /* do_more */ @@ -187,7 +189,7 @@ static const struct Curl_handler Curl_handler_pop3_proxy = { static const struct Curl_handler Curl_handler_pop3s_proxy = { "POP3S", /* scheme */ - ZERO_NULL, /* setup_connection */ + Curl_http_setup_conn, /* setup_connection */ Curl_http, /* do_it */ Curl_http_done, /* done */ ZERO_NULL, /* do_more */ @@ -207,15 +209,27 @@ static const struct Curl_handler Curl_handler_pop3s_proxy = { #endif #endif -/* Function that checks for an ending pop3 status code at the start of the - given string, but also detects the APOP timestamp from the server greeting - as well as the supported authentication types and allowed SASL mechanisms - from the CAPA response. */ -static int pop3_endofresp(struct pingpong *pp, int *resp) +#ifdef USE_SSL +static void pop3_to_pop3s(struct connectdata *conn) +{ + conn->handler = &Curl_handler_pop3s; +} +#else +#define pop3_to_pop3s(x) Curl_nop_stmt +#endif + +/*********************************************************************** + * + * pop3_endofresp() + * + * Checks for an ending POP3 status code at the start of the given string, but + * also detects the APOP timestamp from the server greeting and various + * capabilities from the CAPA response including the supported authentication + * types and allowed SASL mechanisms. + */ +static bool pop3_endofresp(struct connectdata *conn, char *line, size_t len, + int *resp) { - char *line = pp->linestart_resp; - size_t len = strlen(pp->linestart_resp); - struct connectdata *conn = pp->conn; struct pop3_conn *pop3c = &conn->proto.pop3c; size_t wordlen; size_t i; @@ -224,7 +238,7 @@ static int pop3_endofresp(struct pingpong *pp, int *resp) if(len >= 4 && !memcmp("-ERR", line, 4)) { *resp = '-'; - return FALSE; + return TRUE; } /* Are we processing servergreet responses? */ @@ -252,90 +266,95 @@ static int pop3_endofresp(struct pingpong *pp, int *resp) } /* Are we processing CAPA command responses? */ else if(pop3c->state == POP3_CAPA) { - - /* Do we have the terminating character? */ + /* Do we have the terminating line? */ if(len >= 1 && !memcmp(line, ".", 1)) { *resp = '+'; return TRUE; } + /* Does the server support the STLS capability? */ + if(len >= 4 && !memcmp(line, "STLS", 4)) + pop3c->tls_supported = TRUE; + /* Does the server support clear text authentication? */ - if(len >= 4 && !memcmp(line, "USER", 4)) { + else if(len >= 4 && !memcmp(line, "USER", 4)) pop3c->authtypes |= POP3_TYPE_CLEARTEXT; - return FALSE; - } /* Does the server support APOP authentication? */ - if(len >= 4 && !memcmp(line, "APOP", 4)) { + else if(len >= 4 && !memcmp(line, "APOP", 4)) pop3c->authtypes |= POP3_TYPE_APOP; - return FALSE; - } /* Does the server support SASL based authentication? */ - if(len < 4 || memcmp(line, "SASL", 4)) - return FALSE; + else if(len >= 5 && !memcmp(line, "SASL ", 5)) { + pop3c->authtypes |= POP3_TYPE_SASL; - pop3c->authtypes |= POP3_TYPE_SASL; + /* Advance past the SASL keyword */ + line += 5; + len -= 5; - /* Advance past the SASL keyword */ - line += 4; - len -= 4; + /* Loop through the data line */ + for(;;) { + while(len && + (*line == ' ' || *line == '\t' || + *line == '\r' || *line == '\n')) { - /* Loop through the data line */ - for(;;) { - while(len && - (*line == ' ' || *line == '\t' || - *line == '\r' || *line == '\n')) { + line++; + len--; + } - if(*line == '\n') - return FALSE; + if(!len) + break; - line++; - len--; + /* Extract the word */ + for(wordlen = 0; wordlen < len && line[wordlen] != ' ' && + line[wordlen] != '\t' && line[wordlen] != '\r' && + line[wordlen] != '\n';) + wordlen++; + + /* Test the word for a matching authentication mechanism */ + if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_LOGIN)) + pop3c->authmechs |= SASL_MECH_LOGIN; + else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_PLAIN)) + pop3c->authmechs |= SASL_MECH_PLAIN; + else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_CRAM_MD5)) + pop3c->authmechs |= SASL_MECH_CRAM_MD5; + else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_DIGEST_MD5)) + pop3c->authmechs |= SASL_MECH_DIGEST_MD5; + else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_GSSAPI)) + pop3c->authmechs |= SASL_MECH_GSSAPI; + else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_EXTERNAL)) + pop3c->authmechs |= SASL_MECH_EXTERNAL; + else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_NTLM)) + pop3c->authmechs |= SASL_MECH_NTLM; + else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_XOAUTH2)) + pop3c->authmechs |= SASL_MECH_XOAUTH2; + + line += wordlen; + len -= wordlen; } - - if(!len) - break; - - /* Extract the word */ - for(wordlen = 0; wordlen < len && line[wordlen] != ' ' && - line[wordlen] != '\t' && line[wordlen] != '\r' && - line[wordlen] != '\n';) - wordlen++; - - /* Test the word for a matching authentication mechanism */ - if(wordlen == 5 && !memcmp(line, "LOGIN", 5)) - pop3c->authmechs |= SASL_MECH_LOGIN; - else if(wordlen == 5 && !memcmp(line, "PLAIN", 5)) - pop3c->authmechs |= SASL_MECH_PLAIN; - else if(wordlen == 8 && !memcmp(line, "CRAM-MD5", 8)) - pop3c->authmechs |= SASL_MECH_CRAM_MD5; - else if(wordlen == 10 && !memcmp(line, "DIGEST-MD5", 10)) - pop3c->authmechs |= SASL_MECH_DIGEST_MD5; - else if(wordlen == 6 && !memcmp(line, "GSSAPI", 6)) - pop3c->authmechs |= SASL_MECH_GSSAPI; - else if(wordlen == 8 && !memcmp(line, "EXTERNAL", 8)) - pop3c->authmechs |= SASL_MECH_EXTERNAL; - else if(wordlen == 4 && !memcmp(line, "NTLM", 4)) - pop3c->authmechs |= SASL_MECH_NTLM; - - line += wordlen; - len -= wordlen; } + + return FALSE; } - if((len < 1 || memcmp("+", line, 1)) && - (len < 3 || memcmp("+OK", line, 3))) - return FALSE; /* Nothing for us */ + /* Do we have a command or continuation response? */ + if((len >= 3 && !memcmp("+OK", line, 3)) || + (len >= 1 && !memcmp("+", line, 1))) { + *resp = '+'; - /* Otherwise it's a positive response */ - *resp = '+'; + return TRUE; + } - return TRUE; + return FALSE; /* Nothing for us */ } -/* This is the ONLY way to change POP3 state! */ +/*********************************************************************** + * + * state() + * + * This is the ONLY way to change POP3 state! + */ static void state(struct connectdata *conn, pop3state newstate) { struct pop3_conn *pop3c = &conn->proto.pop3c; @@ -344,9 +363,9 @@ static void state(struct connectdata *conn, pop3state newstate) static const char * const names[] = { "STOP", "SERVERGREET", + "CAPA", "STARTTLS", "UPGRADETLS", - "CAPA", "AUTH_PLAIN", "AUTH_LOGIN", "AUTH_LOGIN_PASSWD", @@ -355,7 +374,8 @@ static void state(struct connectdata *conn, pop3state newstate) "AUTH_DIGESTMD5_RESP", "AUTH_NTLM", "AUTH_NTLM_TYPE2MSG", - "AUTH", + "AUTH_XOAUTH2", + "AUTH_FINAL", "APOP", "USER", "PASS", @@ -366,57 +386,118 @@ static void state(struct connectdata *conn, pop3state newstate) if(pop3c->state != newstate) infof(conn->data, "POP3 %p state change from %s to %s\n", - pop3c, names[pop3c->state], names[newstate]); + (void *)pop3c, names[pop3c->state], names[newstate]); #endif pop3c->state = newstate; } -static CURLcode pop3_state_capa(struct connectdata *conn) +/*********************************************************************** + * + * pop3_perform_capa() + * + * Sends the CAPA command in order to obtain a list of server side supported + * capabilities. + */ +static CURLcode pop3_perform_capa(struct connectdata *conn) { CURLcode result = CURLE_OK; struct pop3_conn *pop3c = &conn->proto.pop3c; pop3c->authmechs = 0; /* No known authentication mechanisms yet */ pop3c->authused = 0; /* Clear the authentication mechanism used */ + pop3c->tls_supported = FALSE; /* Clear the TLS capability */ - /* Check we have a username and password to authenticate with and end the - connect phase if we don't */ - if(!conn->bits.user_passwd) { - state(conn, POP3_STOP); + /* Send the CAPA command */ + result = Curl_pp_sendf(&pop3c->pp, "%s", "CAPA"); - return result; - } + if(!result) + state(conn, POP3_CAPA); - /* Send the CAPA command */ - result = Curl_pp_sendf(&pop3c->pp, "CAPA"); + return result; +} - if(result) - return result; +/*********************************************************************** + * + * pop3_perform_starttls() + * + * Sends the STLS command to start the upgrade to TLS. + */ +static CURLcode pop3_perform_starttls(struct connectdata *conn) +{ + CURLcode result = CURLE_OK; - state(conn, POP3_CAPA); + /* Send the STLS command */ + result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", "STLS"); - return CURLE_OK; + if(!result) + state(conn, POP3_STARTTLS); + + return result; } -static CURLcode pop3_state_user(struct connectdata *conn) +/*********************************************************************** + * + * pop3_perform_upgrade_tls() + * + * Performs the upgrade to TLS. + */ +static CURLcode pop3_perform_upgrade_tls(struct connectdata *conn) { - CURLcode result; - struct FTP *pop3 = conn->data->state.proto.pop3; + CURLcode result = CURLE_OK; + struct pop3_conn *pop3c = &conn->proto.pop3c; + + /* Start the SSL connection */ + result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, &pop3c->ssldone); + + if(!result) { + if(pop3c->state != POP3_UPGRADETLS) + state(conn, POP3_UPGRADETLS); + + if(pop3c->ssldone) { + pop3_to_pop3s(conn); + result = pop3_perform_capa(conn); + } + } + + return result; +} + +/*********************************************************************** + * + * pop3_perform_user() + * + * Sends a clear text USER command to authenticate with. + */ +static CURLcode pop3_perform_user(struct connectdata *conn) +{ + CURLcode result = CURLE_OK; + + /* Check we have a username and password to authenticate with and end the + connect phase if we don't */ + if(!conn->bits.user_passwd) { + state(conn, POP3_STOP); - /* Send the USER command */ - result = Curl_pp_sendf(&conn->proto.pop3c.pp, "USER %s", - pop3->user ? pop3->user : ""); - if(result) return result; + } - state(conn, POP3_USER); + /* Send the USER command */ + result = Curl_pp_sendf(&conn->proto.pop3c.pp, "USER %s", + conn->user ? conn->user : ""); + if(!result) + state(conn, POP3_USER); - return CURLE_OK; + return result; } #ifndef CURL_DISABLE_CRYPTO_AUTH -static CURLcode pop3_state_apop(struct connectdata *conn) +/*********************************************************************** + * + * pop3_perform_apop() + * + * Sends an APOP command to authenticate with. + */ +static CURLcode pop3_perform_apop(struct connectdata *conn) { CURLcode result = CURLE_OK; struct pop3_conn *pop3c = &conn->proto.pop3c; @@ -425,6 +506,15 @@ static CURLcode pop3_state_apop(struct connectdata *conn) unsigned char digest[MD5_DIGEST_LEN]; char secret[2 * MD5_DIGEST_LEN + 1]; + /* Check we have a username and password to authenticate with and end the + connect phase if we don't */ + if(!conn->bits.user_passwd) { + state(conn, POP3_STOP); + + return result; + } + + /* Create the digest */ ctxt = Curl_MD5_init(Curl_DIGEST_MD5); if(!ctxt) return CURLE_OUT_OF_MEMORY; @@ -451,88 +541,203 @@ static CURLcode pop3_state_apop(struct connectdata *conn) } #endif -static CURLcode pop3_authenticate(struct connectdata *conn) +/*********************************************************************** + * + * pop3_perform_authenticate() + * + * Sends an AUTH command allowing the client to login with the appropriate + * SASL authentication mechanism. + * + * Additionally, the function will perform fallback to APOP and USER commands + * should a common mechanism not be available between the client and server. + */ +static CURLcode pop3_perform_authenticate(struct connectdata *conn) { CURLcode result = CURLE_OK; + struct SessionHandle *data = conn->data; struct pop3_conn *pop3c = &conn->proto.pop3c; const char *mech = NULL; - pop3state authstate = POP3_STOP; + char *initresp = NULL; + size_t len = 0; + pop3state state1 = POP3_STOP; + pop3state state2 = POP3_STOP; + + /* Check we have a username and password to authenticate with and end the + connect phase if we don't */ + if(!conn->bits.user_passwd) { + state(conn, POP3_STOP); + + return result; + } /* Calculate the supported authentication mechanism by decreasing order of security */ if(pop3c->authtypes & POP3_TYPE_SASL) { #ifndef CURL_DISABLE_CRYPTO_AUTH - if(pop3c->authmechs & SASL_MECH_DIGEST_MD5) { - mech = "DIGEST-MD5"; - authstate = POP3_AUTH_DIGESTMD5; + if((pop3c->authmechs & SASL_MECH_DIGEST_MD5) && + (pop3c->prefmech & SASL_MECH_DIGEST_MD5)) { + mech = SASL_MECH_STRING_DIGEST_MD5; + state1 = POP3_AUTH_DIGESTMD5; pop3c->authused = SASL_MECH_DIGEST_MD5; } - else if(pop3c->authmechs & SASL_MECH_CRAM_MD5) { - mech = "CRAM-MD5"; - authstate = POP3_AUTH_CRAMMD5; + else if((pop3c->authmechs & SASL_MECH_CRAM_MD5) && + (pop3c->prefmech & SASL_MECH_CRAM_MD5)) { + mech = SASL_MECH_STRING_CRAM_MD5; + state1 = POP3_AUTH_CRAMMD5; pop3c->authused = SASL_MECH_CRAM_MD5; } else #endif #ifdef USE_NTLM - if(pop3c->authmechs & SASL_MECH_NTLM) { - mech = "NTLM"; - authstate = POP3_AUTH_NTLM; + if((pop3c->authmechs & SASL_MECH_NTLM) && + (pop3c->prefmech & SASL_MECH_NTLM)) { + mech = SASL_MECH_STRING_NTLM; + state1 = POP3_AUTH_NTLM; + state2 = POP3_AUTH_NTLM_TYPE2MSG; pop3c->authused = SASL_MECH_NTLM; + + if(data->set.sasl_ir) + result = Curl_sasl_create_ntlm_type1_message(conn->user, conn->passwd, + &conn->ntlm, + &initresp, &len); } else #endif - if(pop3c->authmechs & SASL_MECH_LOGIN) { - mech = "LOGIN"; - authstate = POP3_AUTH_LOGIN; + if(((pop3c->authmechs & SASL_MECH_XOAUTH2) && + (pop3c->prefmech & SASL_MECH_XOAUTH2) && + (pop3c->prefmech != SASL_AUTH_ANY)) || conn->xoauth2_bearer) { + mech = SASL_MECH_STRING_XOAUTH2; + state1 = POP3_AUTH_XOAUTH2; + state2 = POP3_AUTH_FINAL; + pop3c->authused = SASL_MECH_XOAUTH2; + + if(data->set.sasl_ir) + result = Curl_sasl_create_xoauth2_message(conn->data, conn->user, + conn->xoauth2_bearer, + &initresp, &len); + } + else if((pop3c->authmechs & SASL_MECH_LOGIN) && + (pop3c->prefmech & SASL_MECH_LOGIN)) { + mech = SASL_MECH_STRING_LOGIN; + state1 = POP3_AUTH_LOGIN; + state2 = POP3_AUTH_LOGIN_PASSWD; pop3c->authused = SASL_MECH_LOGIN; + + if(data->set.sasl_ir) + result = Curl_sasl_create_login_message(conn->data, conn->user, + &initresp, &len); } - else if(pop3c->authmechs & SASL_MECH_PLAIN) { - mech = "PLAIN"; - authstate = POP3_AUTH_PLAIN; + else if((pop3c->authmechs & SASL_MECH_PLAIN) && + (pop3c->prefmech & SASL_MECH_PLAIN)) { + mech = SASL_MECH_STRING_PLAIN; + state1 = POP3_AUTH_PLAIN; + state2 = POP3_AUTH_FINAL; pop3c->authused = SASL_MECH_PLAIN; + + if(data->set.sasl_ir) + result = Curl_sasl_create_plain_message(conn->data, conn->user, + conn->passwd, &initresp, + &len); } } - if(mech) { - /* Perform SASL based authentication */ - result = Curl_pp_sendf(&pop3c->pp, "AUTH %s", mech); + if(!result) { + if(mech && (pop3c->preftype & POP3_TYPE_SASL)) { + /* Perform SASL based authentication */ + if(initresp && + 8 + strlen(mech) + len <= 255) { /* AUTH <mech> ...<crlf> */ + result = Curl_pp_sendf(&pop3c->pp, "AUTH %s %s", mech, initresp); - if(!result) - state(conn, authstate); - } + if(!result) + state(conn, state2); + } + else { + result = Curl_pp_sendf(&pop3c->pp, "AUTH %s", mech); + + if(!result) + state(conn, state1); + } + + Curl_safefree(initresp); + } #ifndef CURL_DISABLE_CRYPTO_AUTH - else if(pop3c->authtypes & POP3_TYPE_APOP) - /* Perform APOP authentication */ - result = pop3_state_apop(conn); + else if((pop3c->authtypes & POP3_TYPE_APOP) && + (pop3c->preftype & POP3_TYPE_APOP)) + /* Perform APOP authentication */ + result = pop3_perform_apop(conn); #endif - else if(pop3c->authtypes & POP3_TYPE_CLEARTEXT) - /* Perform clear text authentication */ - result = pop3_state_user(conn); - else { - /* Other mechanisms not supported */ - infof(conn->data, "No known authentication mechanisms supported!\n"); - result = CURLE_LOGIN_DENIED; + else if((pop3c->authtypes & POP3_TYPE_CLEARTEXT) && + (pop3c->preftype & POP3_TYPE_CLEARTEXT)) + /* Perform clear text authentication */ + result = pop3_perform_user(conn); + else { + /* Other mechanisms not supported */ + infof(conn->data, "No known authentication mechanisms supported!\n"); + result = CURLE_LOGIN_DENIED; + } } return result; } -/* For the POP3 "protocol connect" and "doing" phases only */ -static int pop3_getsock(struct connectdata *conn, curl_socket_t *socks, - int numsocks) +/*********************************************************************** + * + * pop3_perform_command() + * + * Sends a POP3 based command. + */ +static CURLcode pop3_perform_command(struct connectdata *conn) { - return Curl_pp_getsock(&conn->proto.pop3c.pp, socks, numsocks); + CURLcode result = CURLE_OK; + struct SessionHandle *data = conn->data; + struct POP3 *pop3 = data->req.protop; + const char *command = NULL; + + /* Calculate the default command */ + if(pop3->id[0] == '\0' || conn->data->set.ftp_list_only) { + command = "LIST"; + + if(pop3->id[0] != '\0') + /* Message specific LIST so skip the BODY transfer */ + pop3->transfer = FTPTRANSFER_INFO; + } + else + command = "RETR"; + + /* Send the command */ + if(pop3->id[0] != '\0') + result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s %s", + (pop3->custom && pop3->custom[0] != '\0' ? + pop3->custom : command), pop3->id); + else + result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", + (pop3->custom && pop3->custom[0] != '\0' ? + pop3->custom : command)); + + if(!result) + state(conn, POP3_COMMAND); + + return result; } -#ifdef USE_SSL -static void pop3_to_pop3s(struct connectdata *conn) +/*********************************************************************** + * + * pop3_perform_quit() + * + * Performs the quit action prior to sclose() be called. + */ +static CURLcode pop3_perform_quit(struct connectdata *conn) { - conn->handler = &Curl_handler_pop3s; + CURLcode result = CURLE_OK; + + /* Send the QUIT command */ + result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", "QUIT"); + + if(!result) + state(conn, POP3_QUIT); + + return result; } -#else -#define pop3_to_pop3s(x) Curl_nop_stmt -#endif /* For the initial server greeting */ static CURLcode pop3_state_servergreet_resp(struct connectdata *conn, @@ -541,89 +746,75 @@ static CURLcode pop3_state_servergreet_resp(struct connectdata *conn, { CURLcode result = CURLE_OK; struct SessionHandle *data = conn->data; - struct pop3_conn *pop3c = &conn->proto.pop3c; (void)instate; /* no use for this yet */ if(pop3code != '+') { failf(data, "Got unexpected pop3-server response"); - return CURLE_FTP_WEIRD_SERVER_REPLY; - } - - if(data->set.use_ssl && !conn->ssl[FIRSTSOCKET].use) { - /* We don't have a SSL/TLS connection yet, but SSL is requested. Switch - to TLS connection now */ - result = Curl_pp_sendf(&pop3c->pp, "STLS"); - if(!result) - state(conn, POP3_STARTTLS); + result = CURLE_FTP_WEIRD_SERVER_REPLY; } else - result = pop3_state_capa(conn); + result = pop3_perform_capa(conn); return result; } -/* For STARTTLS responses */ -static CURLcode pop3_state_starttls_resp(struct connectdata *conn, - int pop3code, - pop3state instate) +/* For CAPA responses */ +static CURLcode pop3_state_capa_resp(struct connectdata *conn, int pop3code, + pop3state instate) { CURLcode result = CURLE_OK; struct SessionHandle *data = conn->data; + struct pop3_conn *pop3c = &conn->proto.pop3c; (void)instate; /* no use for this yet */ - if(pop3code != '+') { - if(data->set.use_ssl != CURLUSESSL_TRY) { - failf(data, "STARTTLS denied. %c", pop3code); + if(pop3code != '+') + result = pop3_perform_user(conn); + else if(data->set.use_ssl && !conn->ssl[FIRSTSOCKET].use) { + /* We don't have a SSL/TLS connection yet, but SSL is requested */ + if(pop3c->tls_supported) + /* Switch to TLS connection now */ + result = pop3_perform_starttls(conn); + else if(data->set.use_ssl == CURLUSESSL_TRY) + /* Fallback and carry on with authentication */ + result = pop3_perform_authenticate(conn); + else { + failf(data, "STLS not supported."); result = CURLE_USE_SSL_FAILED; } - else - result = pop3_state_capa(conn); } else - result = pop3_state_upgrade_tls(conn); + result = pop3_perform_authenticate(conn); return result; } -static CURLcode pop3_state_upgrade_tls(struct connectdata *conn) -{ - struct pop3_conn *pop3c = &conn->proto.pop3c; - CURLcode result; - - result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, &pop3c->ssldone); - - if(!result) { - if(pop3c->state != POP3_UPGRADETLS) - state(conn, POP3_UPGRADETLS); - - if(pop3c->ssldone) { - pop3_to_pop3s(conn); - result = pop3_state_capa(conn); - } - } - - return result; -} - -/* For CAPA responses */ -static CURLcode pop3_state_capa_resp(struct connectdata *conn, int pop3code, - pop3state instate) +/* For STARTTLS responses */ +static CURLcode pop3_state_starttls_resp(struct connectdata *conn, + int pop3code, + pop3state instate) { CURLcode result = CURLE_OK; + struct SessionHandle *data = conn->data; (void)instate; /* no use for this yet */ - if(pop3code == '+') - result = pop3_authenticate(conn); + if(pop3code != '+') { + if(data->set.use_ssl != CURLUSESSL_TRY) { + failf(data, "STARTTLS denied. %c", pop3code); + result = CURLE_USE_SSL_FAILED; + } + else + result = pop3_perform_authenticate(conn); + } else - result = pop3_state_user(conn); + result = pop3_perform_upgrade_tls(conn); return result; } -/* For AUTH PLAIN responses */ +/* For AUTH PLAIN (without initial response) responses */ static CURLcode pop3_state_auth_plain_resp(struct connectdata *conn, int pop3code, pop3state instate) @@ -650,7 +841,7 @@ static CURLcode pop3_state_auth_plain_resp(struct connectdata *conn, result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", plainauth); if(!result) - state(conn, POP3_AUTH); + state(conn, POP3_AUTH_FINAL); } Curl_safefree(plainauth); @@ -660,7 +851,7 @@ static CURLcode pop3_state_auth_plain_resp(struct connectdata *conn, return result; } -/* For AUTH LOGIN responses */ +/* For AUTH LOGIN (without initial response) responses */ static CURLcode pop3_state_auth_login_resp(struct connectdata *conn, int pop3code, pop3state instate) @@ -724,7 +915,7 @@ static CURLcode pop3_state_auth_login_password_resp(struct connectdata *conn, result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", authpasswd); if(!result) - state(conn, POP3_AUTH); + state(conn, POP3_AUTH_FINAL); } Curl_safefree(authpasswd); @@ -779,7 +970,7 @@ static CURLcode pop3_state_auth_cram_resp(struct connectdata *conn, result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", rplyb64); if(!result) - state(conn, POP3_AUTH); + state(conn, POP3_AUTH_FINAL); } Curl_safefree(rplyb64); @@ -846,10 +1037,10 @@ static CURLcode pop3_state_auth_digest_resp_resp(struct connectdata *conn, } else { /* Send an empty response */ - result = Curl_pp_sendf(&conn->proto.pop3c.pp, ""); + result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", ""); if(!result) - state(conn, POP3_AUTH); + state(conn, POP3_AUTH_FINAL); } return result; @@ -857,7 +1048,7 @@ static CURLcode pop3_state_auth_digest_resp_resp(struct connectdata *conn, #endif #ifdef USE_NTLM -/* For AUTH NTLM responses */ +/* For AUTH NTLM (without initial response) responses */ static CURLcode pop3_state_auth_ntlm_resp(struct connectdata *conn, int pop3code, pop3state instate) @@ -925,7 +1116,7 @@ static CURLcode pop3_state_auth_ntlm_type2msg_resp(struct connectdata *conn, result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", type3msg); if(!result) - state(conn, POP3_AUTH); + state(conn, POP3_AUTH_FINAL); } Curl_safefree(type3msg); @@ -936,6 +1127,43 @@ static CURLcode pop3_state_auth_ntlm_type2msg_resp(struct connectdata *conn, } #endif +/* For AUTH XOAUTH2 (without initial response) responses */ +static CURLcode pop3_state_auth_xoauth2_resp(struct connectdata *conn, + int pop3code, pop3state instate) +{ + CURLcode result = CURLE_OK; + struct SessionHandle *data = conn->data; + size_t len = 0; + char *xoauth = NULL; + + (void)instate; /* no use for this yet */ + + if(pop3code != '+') { + failf(data, "Access denied: %d", pop3code); + result = CURLE_LOGIN_DENIED; + } + else { + /* Create the authorisation message */ + result = Curl_sasl_create_xoauth2_message(conn->data, conn->user, + conn->xoauth2_bearer, + &xoauth, &len); + + /* Send the message */ + if(!result) { + if(xoauth) { + result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", xoauth); + + if(!result) + state(conn, POP3_AUTH_FINAL); + } + + Curl_safefree(xoauth); + } + } + + return result; +} + /* For final responses to the AUTH sequence */ static CURLcode pop3_state_auth_final_resp(struct connectdata *conn, int pop3code, @@ -950,14 +1178,15 @@ static CURLcode pop3_state_auth_final_resp(struct connectdata *conn, failf(data, "Authentication failed: %d", pop3code); result = CURLE_LOGIN_DENIED; } - - /* End of connect phase */ - state(conn, POP3_STOP); + else + /* End of connect phase */ + state(conn, POP3_STOP); return result; } #ifndef CURL_DISABLE_CRYPTO_AUTH +/* For APOP responses */ static CURLcode pop3_state_apop_resp(struct connectdata *conn, int pop3code, pop3state instate) { @@ -970,9 +1199,9 @@ static CURLcode pop3_state_apop_resp(struct connectdata *conn, int pop3code, failf(data, "Authentication failed: %d", pop3code); result = CURLE_LOGIN_DENIED; } - - /* End of connect phase */ - state(conn, POP3_STOP); + else + /* End of connect phase */ + state(conn, POP3_STOP); return result; } @@ -984,7 +1213,6 @@ static CURLcode pop3_state_user_resp(struct connectdata *conn, int pop3code, { CURLcode result = CURLE_OK; struct SessionHandle *data = conn->data; - struct FTP *pop3 = data->state.proto.pop3; (void)instate; /* no use for this yet */ @@ -995,11 +1223,9 @@ static CURLcode pop3_state_user_resp(struct connectdata *conn, int pop3code, else /* Send the PASS command */ result = Curl_pp_sendf(&conn->proto.pop3c.pp, "PASS %s", - pop3->passwd ? pop3->passwd : ""); - if(result) - return result; - - state(conn, POP3_PASS); + conn->passwd ? conn->passwd : ""); + if(!result) + state(conn, POP3_PASS); return result; } @@ -1017,47 +1243,9 @@ static CURLcode pop3_state_pass_resp(struct connectdata *conn, int pop3code, failf(data, "Access denied. %c", pop3code); result = CURLE_LOGIN_DENIED; } - - /* End of connect phase */ - state(conn, POP3_STOP); - - return result; -} - -/* Start the DO phase for the command */ -static CURLcode pop3_command(struct connectdata *conn) -{ - CURLcode result = CURLE_OK; - struct pop3_conn *pop3c = &conn->proto.pop3c; - const char *command = NULL; - - /* Calculate the default command */ - if(pop3c->mailbox[0] == '\0' || conn->data->set.ftp_list_only) { - command = "LIST"; - - if(pop3c->mailbox[0] != '\0') { - /* Message specific LIST so skip the BODY transfer */ - struct FTP *pop3 = conn->data->state.proto.pop3; - pop3->transfer = FTPTRANSFER_INFO; - } - } else - command = "RETR"; - - /* Send the command */ - if(pop3c->mailbox[0] != '\0') - result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s %s", - (pop3c->custom && pop3c->custom[0] != '\0' ? - pop3c->custom : command), pop3c->mailbox); - else - result = Curl_pp_sendf(&conn->proto.pop3c.pp, - (pop3c->custom && pop3c->custom[0] != '\0' ? - pop3c->custom : command)); - - if(result) - return result; - - state(conn, POP3_COMMAND); + /* End of connect phase */ + state(conn, POP3_STOP); return result; } @@ -1069,7 +1257,7 @@ static CURLcode pop3_state_command_resp(struct connectdata *conn, { CURLcode result = CURLE_OK; struct SessionHandle *data = conn->data; - struct FTP *pop3 = data->state.proto.pop3; + struct POP3 *pop3 = data->req.protop; struct pop3_conn *pop3c = &conn->proto.pop3c; struct pingpong *pp = &pop3c->pp; @@ -1090,29 +1278,30 @@ static CURLcode pop3_state_command_resp(struct connectdata *conn, the strip counter here so that these bytes won't be delivered. */ pop3c->strip = 2; - /* POP3 download */ - Curl_setup_transfer(conn, FIRSTSOCKET, -1, FALSE, pop3->bytecountp, - -1, NULL); /* no upload here */ + if(pop3->transfer == FTPTRANSFER_BODY) { + /* POP3 download */ + Curl_setup_transfer(conn, FIRSTSOCKET, -1, FALSE, NULL, -1, NULL); - if(pp->cache) { - /* The header "cache" contains a bunch of data that is actually body - content so send it as such. Note that there may even be additional - "headers" after the body */ + if(pp->cache) { + /* The header "cache" contains a bunch of data that is actually body + content so send it as such. Note that there may even be additional + "headers" after the body */ - if(!data->set.opt_no_body) { - result = Curl_pop3_write(conn, pp->cache, pp->cache_size); - if(result) - return result; - } + if(!data->set.opt_no_body) { + result = Curl_pop3_write(conn, pp->cache, pp->cache_size); + if(result) + return result; + } - /* Free the cache */ - Curl_safefree(pp->cache); + /* Free the cache */ + Curl_safefree(pp->cache); - /* Reset the cache size */ - pp->cache_size = 0; + /* Reset the cache size */ + pp->cache_size = 0; + } } - /* End of do phase */ + /* End of DO phase */ state(conn, POP3_STOP); return result; @@ -1120,7 +1309,7 @@ static CURLcode pop3_state_command_resp(struct connectdata *conn, static CURLcode pop3_statemach_act(struct connectdata *conn) { - CURLcode result; + CURLcode result = CURLE_OK; curl_socket_t sock = conn->sock[FIRSTSOCKET]; int pop3code; struct pop3_conn *pop3c = &conn->proto.pop3c; @@ -1129,7 +1318,7 @@ static CURLcode pop3_statemach_act(struct connectdata *conn) /* Busy upgrading the connection; right now all I/O is SSL/TLS, not POP3 */ if(pop3c->state == POP3_UPGRADETLS) - return pop3_state_upgrade_tls(conn); + return pop3_perform_upgrade_tls(conn); /* Flush any data that needs to be sent */ if(pp->sendleft) @@ -1147,14 +1336,14 @@ static CURLcode pop3_statemach_act(struct connectdata *conn) result = pop3_state_servergreet_resp(conn, pop3code, pop3c->state); break; - case POP3_STARTTLS: - result = pop3_state_starttls_resp(conn, pop3code, pop3c->state); - break; - case POP3_CAPA: result = pop3_state_capa_resp(conn, pop3code, pop3c->state); break; + case POP3_STARTTLS: + result = pop3_state_starttls_resp(conn, pop3code, pop3c->state); + break; + case POP3_AUTH_PLAIN: result = pop3_state_auth_plain_resp(conn, pop3code, pop3c->state); break; @@ -1193,7 +1382,11 @@ static CURLcode pop3_statemach_act(struct connectdata *conn) break; #endif - case POP3_AUTH: + case POP3_AUTH_XOAUTH2: + result = pop3_state_auth_xoauth2_resp(conn, pop3code, pop3c->state); + break; + + case POP3_AUTH_FINAL: result = pop3_state_auth_final_resp(conn, pop3code, pop3c->state); break; @@ -1230,30 +1423,28 @@ static CURLcode pop3_statemach_act(struct connectdata *conn) /* Called repeatedly until done from multi.c */ static CURLcode pop3_multi_statemach(struct connectdata *conn, bool *done) { + CURLcode result = CURLE_OK; struct pop3_conn *pop3c = &conn->proto.pop3c; - CURLcode result; - if((conn->handler->flags & PROTOPT_SSL) && !pop3c->ssldone) + if((conn->handler->flags & PROTOPT_SSL) && !pop3c->ssldone) { result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, &pop3c->ssldone); - else - result = Curl_pp_multi_statemach(&pop3c->pp); + if(result || !pop3c->ssldone) + return result; + } + result = Curl_pp_statemach(&pop3c->pp, FALSE); *done = (pop3c->state == POP3_STOP) ? TRUE : FALSE; return result; } -static CURLcode pop3_easy_statemach(struct connectdata *conn) +static CURLcode pop3_block_statemach(struct connectdata *conn) { - struct pop3_conn *pop3c = &conn->proto.pop3c; - struct pingpong *pp = &pop3c->pp; CURLcode result = CURLE_OK; + struct pop3_conn *pop3c = &conn->proto.pop3c; - while(pop3c->state != POP3_STOP) { - result = Curl_pp_easy_statemach(pp); - if(result) - break; - } + while(pop3c->state != POP3_STOP && !result) + result = Curl_pp_statemach(&pop3c->pp, TRUE); return result; } @@ -1262,26 +1453,22 @@ static CURLcode pop3_easy_statemach(struct connectdata *conn) required */ static CURLcode pop3_init(struct connectdata *conn) { + CURLcode result = CURLE_OK; struct SessionHandle *data = conn->data; - struct FTP *pop3 = data->state.proto.pop3; - - if(!pop3) { - pop3 = data->state.proto.pop3 = calloc(sizeof(struct FTP), 1); - if(!pop3) - return CURLE_OUT_OF_MEMORY; - } + struct POP3 *pop3; - /* Get some initial data into the pop3 struct */ - pop3->bytecountp = &data->req.bytecount; + pop3 = data->req.protop = calloc(sizeof(struct POP3), 1); + if(!pop3) + result = CURLE_OUT_OF_MEMORY; - /* No need to duplicate user+password, the connectdata struct won't change - during a session, but we re-init them here since on subsequent inits - since the conn struct may have changed or been replaced. - */ - pop3->user = conn->user; - pop3->passwd = conn->passwd; + return result; +} - return CURLE_OK; +/* For the POP3 "protocol connect" and "doing" phases only */ +static int pop3_getsock(struct connectdata *conn, curl_socket_t *socks, + int numsocks) +{ + return Curl_pp_getsock(&conn->proto.pop3c.pp, socks, numsocks); } /*********************************************************************** @@ -1292,44 +1479,37 @@ static CURLcode pop3_init(struct connectdata *conn) * connection phase. * * The variable 'done' points to will be TRUE if the protocol-layer connect - * phase is done when this function returns, or FALSE is not. When called as - * a part of the easy interface, it will always be TRUE. + * phase is done when this function returns, or FALSE if not. */ static CURLcode pop3_connect(struct connectdata *conn, bool *done) { - CURLcode result; + CURLcode result = CURLE_OK; struct pop3_conn *pop3c = &conn->proto.pop3c; struct pingpong *pp = &pop3c->pp; *done = FALSE; /* default to not done yet */ - /* If there already is a protocol-specific struct allocated for this - sessionhandle, deal with it */ - Curl_reset_reqproto(conn); - - result = pop3_init(conn); - if(CURLE_OK != result) - return result; - - /* We always support persistent connections on pop3 */ + /* We always support persistent connections in POP3 */ conn->bits.close = FALSE; - pp->response_time = RESP_TIMEOUT; /* set default response time-out */ + /* Set the default response time-out */ + pp->response_time = RESP_TIMEOUT; pp->statemach_act = pop3_statemach_act; pp->endofresp = pop3_endofresp; pp->conn = conn; - if(conn->handler->flags & PROTOPT_SSL) { - /* POP3S is simply pop3 with SSL for the control channel */ - /* so perform the SSL initialization for this socket */ - result = Curl_ssl_connect(conn, FIRSTSOCKET); - if(result) - return result; - } + /* Set the default preferred authentication type and mechanism */ + pop3c->preftype = POP3_TYPE_ANY; + pop3c->prefmech = SASL_AUTH_ANY; - /* Initialise the response reader stuff */ + /* Initialise the pingpong layer */ Curl_pp_init(pp); + /* Parse the URL options */ + result = pop3_parse_url_options(conn); + if(result) + return result; + /* Start off waiting for the server greeting response */ state(conn, POP3_SERVERGREET); @@ -1350,19 +1530,17 @@ static CURLcode pop3_connect(struct connectdata *conn, bool *done) static CURLcode pop3_done(struct connectdata *conn, CURLcode status, bool premature) { - struct SessionHandle *data = conn->data; - struct FTP *pop3 = data->state.proto.pop3; - struct pop3_conn *pop3c = &conn->proto.pop3c; CURLcode result = CURLE_OK; + struct SessionHandle *data = conn->data; + struct POP3 *pop3 = data->req.protop; (void)premature; if(!pop3) - /* When the easy handle is removed from the multi while libcurl is still - * trying to resolve the host name, it seems that the pop3 struct is not - * yet initialized, but the removal action calls Curl_done() which calls - * this function. So we simply return success if no pop3 pointer is set. - */ + /* When the easy handle is removed from the multi interface while libcurl + is still trying to resolve the host name, the POP3 struct is not yet + initialized. However, the removal action calls Curl_done() which in + turn calls this function, so we simply return success. */ return CURLE_OK; if(status) { @@ -1370,11 +1548,11 @@ static CURLcode pop3_done(struct connectdata *conn, CURLcode status, result = status; /* use the already set error code */ } - /* Cleanup our do based variables */ - Curl_safefree(pop3c->mailbox); - Curl_safefree(pop3c->custom); + /* Cleanup our per-request based variables */ + Curl_safefree(pop3->id); + Curl_safefree(pop3->custom); - /* Clear the transfer mode for the next connection */ + /* Clear the transfer mode for the next request */ pop3->transfer = FTPTRANSFER_BODY; return result; @@ -1384,7 +1562,7 @@ static CURLcode pop3_done(struct connectdata *conn, CURLcode status, * * pop3_perform() * - * This is the actual DO function for POP3. Get a file/directory according to + * This is the actual DO function for POP3. Get a message/listing according to * the options previously setup. */ static CURLcode pop3_perform(struct connectdata *conn, bool *connected, @@ -1397,14 +1575,14 @@ static CURLcode pop3_perform(struct connectdata *conn, bool *connected, if(conn->data->set.opt_no_body) { /* Requested no body means no transfer */ - struct FTP *pop3 = conn->data->state.proto.pop3; + struct POP3 *pop3 = conn->data->req.protop; pop3->transfer = FTPTRANSFER_INFO; } *dophase_done = FALSE; /* not done yet */ /* Start the first command in the DO phase */ - result = pop3_command(conn); + result = pop3_perform_command(conn); if(result) return result; @@ -1430,55 +1608,21 @@ static CURLcode pop3_perform(struct connectdata *conn, bool *connected, */ static CURLcode pop3_do(struct connectdata *conn, bool *done) { - CURLcode retcode = CURLE_OK; + CURLcode result = CURLE_OK; *done = FALSE; /* default to false */ - /* - Since connections can be re-used between SessionHandles, this might be a - connection already existing but on a fresh SessionHandle struct so we must - make sure we have a good 'struct POP3' to play with. For new connections, - the struct POP3 is allocated and setup in the pop3_connect() function. - */ - Curl_reset_reqproto(conn); - retcode = pop3_init(conn); - if(retcode) - return retcode; - /* Parse the URL path */ - retcode = pop3_parse_url_path(conn); - if(retcode) - return retcode; + result = pop3_parse_url_path(conn); + if(result) + return result; /* Parse the custom request */ - retcode = pop3_parse_custom_request(conn); - if(retcode) - return retcode; - - retcode = pop3_regular_transfer(conn, done); - - return retcode; -} - -/*********************************************************************** - * - * pop3_quit() - * - * This should be called before calling sclose(). We should then wait for the - * response from the server before returning. The calling code should then try - * to close the connection. - */ -static CURLcode pop3_quit(struct connectdata *conn) -{ - CURLcode result = CURLE_OK; - - result = Curl_pp_sendf(&conn->proto.pop3c.pp, "QUIT", NULL); + result = pop3_parse_custom_request(conn); if(result) return result; - state(conn, POP3_QUIT); - - result = pop3_easy_statemach(conn); + result = pop3_regular_transfer(conn, done); return result; } @@ -1497,12 +1641,13 @@ static CURLcode pop3_disconnect(struct connectdata *conn, /* We cannot send quit unconditionally. If this connection is stale or bad in any way, sending quit and waiting around here will make the - disconnect wait in vain and cause more problems than we need to */ + disconnect wait in vain and cause more problems than we need to. */ /* The POP3 session may or may not have been allocated/setup at this point! */ if(!dead_connection && pop3c->pp.conn) - (void)pop3_quit(conn); /* ignore errors on the LOGOUT */ + if(!pop3_perform_quit(conn)) + (void)pop3_block_statemach(conn); /* ignore errors on QUIT */ /* Disconnect from the server */ Curl_pp_disconnect(&pop3c->pp); @@ -1516,48 +1661,12 @@ static CURLcode pop3_disconnect(struct connectdata *conn, return CURLE_OK; } -/*********************************************************************** - * - * pop3_parse_url_path() - * - * Parse the URL path into separate path components. - */ -static CURLcode pop3_parse_url_path(struct connectdata *conn) -{ - /* The POP3 struct is already initialised in pop3_connect() */ - struct pop3_conn *pop3c = &conn->proto.pop3c; - struct SessionHandle *data = conn->data; - const char *path = data->state.path; - - /* URL decode the path and use this mailbox */ - return Curl_urldecode(data, path, 0, &pop3c->mailbox, NULL, TRUE); -} - -static CURLcode pop3_parse_custom_request(struct connectdata *conn) -{ - CURLcode result = CURLE_OK; - struct pop3_conn *pop3c = &conn->proto.pop3c; - struct SessionHandle *data = conn->data; - const char *custom = conn->data->set.str[STRING_CUSTOMREQUEST]; - - /* URL decode the custom request */ - if(custom) - result = Curl_urldecode(data, custom, 0, &pop3c->custom, NULL, TRUE); - - return result; -} - /* Call this when the DO phase has completed */ static CURLcode pop3_dophase_done(struct connectdata *conn, bool connected) { - struct FTP *pop3 = conn->data->state.proto.pop3; - + (void)conn; (void)connected; - if(pop3->transfer != FTPTRANSFER_BODY) - /* no data to transfer */ - Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL); - return CURLE_OK; } @@ -1568,12 +1677,10 @@ static CURLcode pop3_doing(struct connectdata *conn, bool *dophase_done) if(result) DEBUGF(infof(conn->data, "DO phase failed\n")); - else { - if(*dophase_done) { - result = pop3_dophase_done(conn, FALSE /* not connected */); + else if(*dophase_done) { + result = pop3_dophase_done(conn, FALSE /* not connected */); - DEBUGF(infof(conn->data, "DO phase is complete\n")); - } + DEBUGF(infof(conn->data, "DO phase is complete\n")); } return result; @@ -1598,30 +1705,33 @@ static CURLcode pop3_regular_transfer(struct connectdata *conn, /* Make sure size is unknown at this point */ data->req.size = -1; + /* Set the progress data */ Curl_pgrsSetUploadCounter(data, 0); Curl_pgrsSetDownloadCounter(data, 0); Curl_pgrsSetUploadSize(data, 0); Curl_pgrsSetDownloadSize(data, 0); + /* Carry out the perform */ result = pop3_perform(conn, &connected, dophase_done); - if(CURLE_OK == result) { - if(!*dophase_done) - /* The DO phase has not completed yet */ - return CURLE_OK; - + /* Perform post DO phase operations if necessary */ + if(!result && *dophase_done) result = pop3_dophase_done(conn, connected); - } return result; } -static CURLcode pop3_setup_connection(struct connectdata * conn) +static CURLcode pop3_setup_connection(struct connectdata *conn) { struct SessionHandle *data = conn->data; + /* Initialise the POP3 layer */ + CURLcode result = pop3_init(conn); + if(result) + return result; + if(conn->bits.httpproxy && !data->set.tunnel_thru_httpproxy) { - /* Unless we have asked to tunnel pop3 operations through the proxy, we + /* Unless we have asked to tunnel POP3 operations through the proxy, we switch and use HTTP operations only */ #ifndef CURL_DISABLE_HTTP if(conn->handler == &Curl_handler_pop3) @@ -1635,10 +1745,8 @@ static CURLcode pop3_setup_connection(struct connectdata * conn) #endif } - /* We explicitly mark this connection as persistent here as we're doing - POP3 over HTTP and thus we accidentally avoid setting this value - otherwise */ - conn->bits.close = FALSE; + /* set it up as an HTTP connection instead */ + return conn->handler->setup_connection(conn); #else failf(data, "POP3 over http proxy requires HTTP support built-in!"); return CURLE_UNSUPPORTED_PROTOCOL; @@ -1650,8 +1758,120 @@ static CURLcode pop3_setup_connection(struct connectdata * conn) return CURLE_OK; } -/* This function scans the body after the end-of-body and writes everything - until the end is found */ +/*********************************************************************** + * + * pop3_parse_url_options() + * + * Parse the URL login options. + */ +static CURLcode pop3_parse_url_options(struct connectdata *conn) +{ + CURLcode result = CURLE_OK; + struct pop3_conn *pop3c = &conn->proto.pop3c; + const char *options = conn->options; + const char *ptr = options; + + if(options) { + const char *key = ptr; + + while(*ptr && *ptr != '=') + ptr++; + + if(strnequal(key, "AUTH", 4)) { + const char *value = ptr + 1; + + if(strequal(value, "*")) { + pop3c->preftype = POP3_TYPE_ANY; + pop3c->prefmech = SASL_AUTH_ANY; + } + else if(strequal(value, "+APOP")) { + pop3c->preftype = POP3_TYPE_APOP; + pop3c->prefmech = SASL_AUTH_NONE; + } + else if(strequal(value, SASL_MECH_STRING_LOGIN)) { + pop3c->preftype = POP3_TYPE_SASL; + pop3c->prefmech = SASL_MECH_LOGIN; + } + else if(strequal(value, SASL_MECH_STRING_PLAIN)) { + pop3c->preftype = POP3_TYPE_SASL; + pop3c->prefmech = SASL_MECH_PLAIN; + } + else if(strequal(value, SASL_MECH_STRING_CRAM_MD5)) { + pop3c->preftype = POP3_TYPE_SASL; + pop3c->prefmech = SASL_MECH_CRAM_MD5; + } + else if(strequal(value, SASL_MECH_STRING_DIGEST_MD5)) { + pop3c->preftype = POP3_TYPE_SASL; + pop3c->prefmech = SASL_MECH_DIGEST_MD5; + } + else if(strequal(value, SASL_MECH_STRING_GSSAPI)) { + pop3c->preftype = POP3_TYPE_SASL; + pop3c->prefmech = SASL_MECH_GSSAPI; + } + else if(strequal(value, SASL_MECH_STRING_NTLM)) { + pop3c->preftype = POP3_TYPE_SASL; + pop3c->prefmech = SASL_MECH_NTLM; + } + else if(strequal(value, SASL_MECH_STRING_XOAUTH2)) { + pop3c->preftype = POP3_TYPE_SASL; + pop3c->prefmech = SASL_MECH_XOAUTH2; + } + else { + pop3c->preftype = POP3_TYPE_NONE; + pop3c->prefmech = SASL_AUTH_NONE; + } + } + else + result = CURLE_URL_MALFORMAT; + } + + return result; +} + +/*********************************************************************** + * + * pop3_parse_url_path() + * + * Parse the URL path into separate path components. + */ +static CURLcode pop3_parse_url_path(struct connectdata *conn) +{ + /* The POP3 struct is already initialised in pop3_connect() */ + struct SessionHandle *data = conn->data; + struct POP3 *pop3 = data->req.protop; + const char *path = data->state.path; + + /* URL decode the path for the message ID */ + return Curl_urldecode(data, path, 0, &pop3->id, NULL, TRUE); +} + +/*********************************************************************** + * + * pop3_parse_custom_request() + * + * Parse the custom request. + */ +static CURLcode pop3_parse_custom_request(struct connectdata *conn) +{ + CURLcode result = CURLE_OK; + struct SessionHandle *data = conn->data; + struct POP3 *pop3 = data->req.protop; + const char *custom = data->set.str[STRING_CUSTOMREQUEST]; + + /* URL decode the custom request */ + if(custom) + result = Curl_urldecode(data, custom, 0, &pop3->custom, NULL, TRUE); + + return result; +} + +/*********************************************************************** + * + * Curl_pop3_write() + * + * This function scans the body after the end-of-body and writes everything + * until the end is found. + */ CURLcode Curl_pop3_write(struct connectdata *conn, char *str, size_t nread) { /* This code could be made into a special function in the handler struct */ @@ -1668,7 +1888,7 @@ CURLcode Curl_pop3_write(struct connectdata *conn, char *str, size_t nread) 5 bytes (0d 0a 2e 0d 0a). Note that a line starting with a dot matches the eob so the server will have prefixed it with an extra dot which we need to strip out. Additionally the marker could of course be spread out - over 5 different data chunks */ + over 5 different data chunks. */ for(i = 0; i < nread; i++) { size_t prev = pop3c->eob; diff --git a/plugins/FTPFileYM/curl/lib/pop3.h b/plugins/FTPFileYM/curl/lib/pop3.h index b9d7bd4e66..7bc7744955 100644 --- a/plugins/FTPFileYM/curl/lib/pop3.h +++ b/plugins/FTPFileYM/curl/lib/pop3.h @@ -22,6 +22,8 @@ * ***************************************************************************/ +#include "pingpong.h" + /**************************************************************************** * POP3 unique setup ***************************************************************************/ @@ -29,10 +31,10 @@ typedef enum { POP3_STOP, /* do nothing state, stops the state machine */ POP3_SERVERGREET, /* waiting for the initial greeting immediately after a connect */ + POP3_CAPA, POP3_STARTTLS, POP3_UPGRADETLS, /* asynchronously upgrade the connection to SSL/TLS (multi mode only) */ - POP3_CAPA, POP3_AUTH_PLAIN, POP3_AUTH_LOGIN, POP3_AUTH_LOGIN_PASSWD, @@ -41,7 +43,8 @@ typedef enum { POP3_AUTH_DIGESTMD5_RESP, POP3_AUTH_NTLM, POP3_AUTH_NTLM_TYPE2MSG, - POP3_AUTH, + POP3_AUTH_XOAUTH2, + POP3_AUTH_FINAL, POP3_APOP, POP3_USER, POP3_PASS, @@ -50,31 +53,46 @@ typedef enum { POP3_LAST /* never used */ } pop3state; +/* This POP3 struct is used in the SessionHandle. All POP3 data that is + connection-oriented must be in pop3_conn to properly deal with the fact that + perhaps the SessionHandle is changed between the times the connection is + used. */ +struct POP3 { + curl_pp_transfer transfer; + char *id; /* Message ID */ + char *custom; /* Custom Request */ +}; + /* pop3_conn is used for struct connection-oriented data in the connectdata struct */ struct pop3_conn { struct pingpong pp; - char *mailbox; /* Message ID */ - char *custom; /* Custom Request */ + pop3state state; /* Always use pop3.c:state() to change state! */ + bool ssldone; /* Is connect() over SSL done? */ size_t eob; /* Number of bytes of the EOB (End Of Body) that have been received so far */ size_t strip; /* Number of bytes from the start to ignore as non-body */ - unsigned int authtypes; /* Supported authentication types */ + unsigned int authtypes; /* Accepted authentication types */ unsigned int authmechs; /* Accepted SASL authentication mechanisms */ + unsigned int preftype; /* Preferred authentication type */ + unsigned int prefmech; /* Preferred SASL authentication mechanism */ unsigned int authused; /* SASL auth mechanism used for the connection */ char *apoptimestamp; /* APOP timestamp from the server greeting */ - pop3state state; /* Always use pop3.c:state() to change state! */ - bool ssldone; /* Is connect() over SSL done? */ + bool tls_supported; /* StartTLS capability supported by server */ }; extern const struct Curl_handler Curl_handler_pop3; extern const struct Curl_handler Curl_handler_pop3s; /* Authentication type flags */ -#define POP3_TYPE_CLEARTEXT 0x0001 -#define POP3_TYPE_APOP 0x0002 -#define POP3_TYPE_SASL 0x0004 +#define POP3_TYPE_CLEARTEXT (1 << 0) +#define POP3_TYPE_APOP (1 << 1) +#define POP3_TYPE_SASL (1 << 2) + +/* Authentication type values */ +#define POP3_TYPE_NONE 0 +#define POP3_TYPE_ANY ~0 /* This is the 5-bytes End-Of-Body marker for POP3 */ #define POP3_EOB "\x0d\x0a\x2e\x0d\x0a" diff --git a/plugins/FTPFileYM/curl/lib/progress.c b/plugins/FTPFileYM/curl/lib/progress.c index 4cff2b76ec..dac7f8d6ec 100644 --- a/plugins/FTPFileYM/curl/lib/progress.c +++ b/plugins/FTPFileYM/curl/lib/progress.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2012, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2013, 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 @@ -357,12 +357,21 @@ int Curl_pgrsUpdate(struct connectdata *conn) } /* Calculations end */ if(!(data->progress.flags & PGRS_HIDE)) { - /* progress meter has not been shut off */ - if(data->set.fprogress) { - /* There's a callback set, so we call that instead of writing - anything ourselves. This really is the way to go. */ + if(data->set.fxferinfo) { + /* There's a callback set, call that */ + result= data->set.fxferinfo(data->set.progress_client, + data->progress.size_dl, + data->progress.downloaded, + data->progress.size_ul, + data->progress.uploaded); + if(result) + failf(data, "Callback aborted"); + return result; + } + else if(data->set.fprogress) { + /* The older deprecated callback is set, call that */ result= data->set.fprogress(data->set.progress_client, (double)data->progress.size_dl, (double)data->progress.downloaded, diff --git a/plugins/FTPFileYM/curl/lib/qssl.c b/plugins/FTPFileYM/curl/lib/qssl.c index 8ef6fec8d8..b8a8daecac 100644 --- a/plugins/FTPFileYM/curl/lib/qssl.c +++ b/plugins/FTPFileYM/curl/lib/qssl.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2011, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2013, 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 @@ -37,6 +37,7 @@ #include "sslgen.h" #include "connect.h" /* for the connect timeout */ #include "select.h" +#include "x509asn1.h" #include "curl_memory.h" /* The last #include file should be: */ #include "memdebug.h" @@ -169,10 +170,7 @@ static CURLcode Curl_qsossl_handshake(struct connectdata * conn, int sockindex) SSLHandle * h = connssl->handle; long timeout_ms; - h->exitPgm = NULL; - - if(!data->set.ssl.verifyhost) - h->exitPgm = Curl_qsossl_trap_cert; + h->exitPgm = data->set.ssl.verifypeer? NULL: Curl_qsossl_trap_cert; /* figure out how long time we should wait at maximum */ timeout_ms = Curl_timeleft(data, NULL, TRUE); @@ -208,6 +206,8 @@ static CURLcode Curl_qsossl_handshake(struct connectdata * conn, int sockindex) break; } + h->peerCert = NULL; + h->peerCertLen = 0; rc = SSL_Handshake(h, SSL_HANDSHAKE_AS_CLIENT); switch (rc) { @@ -238,6 +238,23 @@ static CURLcode Curl_qsossl_handshake(struct connectdata * conn, int sockindex) return CURLE_SSL_CONNECT_ERROR; } + /* Verify host. */ + rc = Curl_verifyhost(conn, h->peerCert, h->peerCert + h->peerCertLen); + if(rc != CURLE_OK) + return rc; + + /* Gather certificate info. */ + if(data->set.ssl.certinfo) { + if(Curl_ssl_init_certinfo(data, 1)) + return CURLE_OUT_OF_MEMORY; + if(h->peerCert) { + rc = Curl_extract_certinfo(conn, 0, h->peerCert, + h->peerCert + h->peerCertLen); + if(rc != CURLE_OK) + return rc; + } + } + return CURLE_OK; } @@ -257,19 +274,22 @@ CURLcode Curl_qsossl_connect(struct connectdata * conn, int sockindex) if(rc == CURLE_OK) { rc = Curl_qsossl_create(conn, sockindex); - if(rc == CURLE_OK) + if(rc == CURLE_OK) { rc = Curl_qsossl_handshake(conn, sockindex); - else { - SSL_Destroy(connssl->handle); - connssl->handle = NULL; - connssl->use = FALSE; - connssl->state = ssl_connection_none; + if(rc != CURLE_OK) + SSL_Destroy(connssl->handle); } } + if(rc == CURLE_OK) { - connssl->state = ssl_connection_complete; conn->recv[sockindex] = qsossl_recv; conn->send[sockindex] = qsossl_send; + connssl->state = ssl_connection_complete; + } + else { + connssl->handle = NULL; + connssl->use = FALSE; + connssl->state = ssl_connection_none; } return rc; diff --git a/plugins/FTPFileYM/curl/lib/rtsp.c b/plugins/FTPFileYM/curl/lib/rtsp.c index 9d7a9a94b0..f7c6562a7b 100644 --- a/plugins/FTPFileYM/curl/lib/rtsp.c +++ b/plugins/FTPFileYM/curl/lib/rtsp.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2012, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2013, 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 @@ -81,6 +81,8 @@ static CURLcode rtsp_rtp_readwrite(struct SessionHandle *data, ssize_t *nread, bool *readmore); +static CURLcode rtsp_setup_connection(struct connectdata *conn); + /* this returns the socket to wait for in the DO and DOING state for the multi interface and then we're always _sending_ a request and thus we wait for @@ -104,7 +106,7 @@ CURLcode rtp_client_write(struct connectdata *conn, char *ptr, size_t len); */ const struct Curl_handler Curl_handler_rtsp = { "RTSP", /* scheme */ - ZERO_NULL, /* setup_connection */ + rtsp_setup_connection, /* setup_connection */ rtsp_do, /* do_it */ rtsp_done, /* done */ ZERO_NULL, /* do_more */ @@ -122,6 +124,19 @@ const struct Curl_handler Curl_handler_rtsp = { PROTOPT_NONE /* flags */ }; + +static CURLcode rtsp_setup_connection(struct connectdata *conn) +{ + struct RTSP *rtsp; + + conn->data->req.protop = rtsp = calloc(1, sizeof(struct RTSP)); + if(!rtsp) + return CURLE_OUT_OF_MEMORY; + + return CURLE_OK; +} + + /* * The server may send us RTP data at any point, and RTSPREQ_RECEIVE does not * want to block the application forever while receiving a stream. Therefore, @@ -185,7 +200,7 @@ static CURLcode rtsp_done(struct connectdata *conn, CURLcode status, bool premature) { struct SessionHandle *data = conn->data; - struct RTSP *rtsp = data->state.proto.rtsp; + struct RTSP *rtsp = data->req.protop; CURLcode httpStatus; long CSeq_sent; long CSeq_recv; @@ -221,7 +236,7 @@ static CURLcode rtsp_do(struct connectdata *conn, bool *done) struct SessionHandle *data = conn->data; CURLcode result=CURLE_OK; Curl_RtspReq rtspreq = data->set.rtspreq; - struct RTSP *rtsp; + struct RTSP *rtsp = data->req.protop; struct HTTP *http; Curl_send_buffer *req_buffer; curl_off_t postsize = 0; /* for ANNOUNCE and SET_PARAMETER */ @@ -239,20 +254,6 @@ static CURLcode rtsp_do(struct connectdata *conn, bool *done) *done = TRUE; - Curl_reset_reqproto(conn); - - if(!data->state.proto.rtsp) { - /* Only allocate this struct if we don't already have it! */ - - rtsp = calloc(1, sizeof(struct RTSP)); - if(!rtsp) - return CURLE_OUT_OF_MEMORY; - data->state.proto.rtsp = rtsp; - } - else { - rtsp = data->state.proto.rtsp; - } - http = &(rtsp->http_wrapper); /* Assert that no one has changed the RTSP struct in an evil way */ DEBUGASSERT((void *)http == (void *)rtsp); @@ -668,7 +669,7 @@ static CURLcode rtsp_rtp_readwrite(struct SessionHandle *data, } if(rtp_dataleft != 0 && rtp[0] == '$') { - DEBUGF(infof(data, "RTP Rewinding %zu %s\n", rtp_dataleft, + DEBUGF(infof(data, "RTP Rewinding %zd %s\n", rtp_dataleft, *readmore ? "(READMORE)" : "")); /* Store the incomplete RTP packet for a "rewind" */ @@ -749,7 +750,8 @@ CURLcode Curl_rtsp_parseheader(struct connectdata *conn, /* Store the received CSeq. Match is verified in rtsp_done */ int nc = sscanf(&header[4], ": %ld", &CSeq); if(nc == 1) { - data->state.proto.rtsp->CSeq_recv = CSeq; /* mark the request */ + struct RTSP *rtsp = data->req.protop; + rtsp->CSeq_recv = CSeq; /* mark the request */ data->state.rtsp_CSeq_recv = CSeq; /* update the handle */ } else { diff --git a/plugins/FTPFileYM/curl/lib/security.c b/plugins/FTPFileYM/curl/lib/security.c index 3c46953cbf..508c7b41ec 100644 --- a/plugins/FTPFileYM/curl/lib/security.c +++ b/plugins/FTPFileYM/curl/lib/security.c @@ -7,10 +7,10 @@ * rewrite to work around the paragraph 2 in the BSD licenses as explained * below. * - * Copyright (c) 1998, 1999 Kungliga Tekniska Högskolan + * Copyright (c) 1998, 1999, 2013 Kungliga Tekniska Högskolan * (Royal Institute of Technology, Stockholm, Sweden). * - * Copyright (C) 2001 - 2011, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 2001 - 2013, Daniel Stenberg, <daniel@haxx.se>, et al. * * All rights reserved. * @@ -44,7 +44,7 @@ #include "curl_setup.h" #ifndef CURL_DISABLE_FTP -#if defined(HAVE_KRB4) || defined(HAVE_GSSAPI) +#ifdef HAVE_GSSAPI #ifdef HAVE_NETDB_H #include <netdb.h> @@ -57,7 +57,7 @@ #include "urldata.h" #include "curl_base64.h" #include "curl_memory.h" -#include "krb4.h" +#include "curl_sec.h" #include "ftp.h" #include "sendf.h" #include "rawstr.h" @@ -110,12 +110,9 @@ static char level_to_char(int level) { } static const struct Curl_sec_client_mech * const mechs[] = { -#if defined(HAVE_GSSAPI) +#ifdef HAVE_GSSAPI &Curl_krb5_client_mech, #endif -#if defined(HAVE_KRB4) - &Curl_krb4_client_mech, -#endif NULL }; @@ -199,7 +196,7 @@ socket_write(struct connectdata *conn, curl_socket_t fd, const void *to, static CURLcode read_data(struct connectdata *conn, curl_socket_t fd, - struct krb4buffer *buf) + struct krb5buffer *buf) { int len; void* tmp; @@ -225,7 +222,7 @@ static CURLcode read_data(struct connectdata *conn, } static size_t -buffer_read(struct krb4buffer *buf, void *data, size_t len) +buffer_read(struct krb5buffer *buf, void *data, size_t len) { if(buf->size - buf->index < len) len = buf->size - buf->index; @@ -498,7 +495,7 @@ static CURLcode choose_mech(struct connectdata *conn) /* We have no mechanism with a NULL name but keep this check */ DEBUGASSERT(mech_name != NULL); if(mech_name == NULL) { - infof(data, "Skipping mechanism with empty name (%p)\n", mech); + infof(data, "Skipping mechanism with empty name (%p)\n", (void *)mech); continue; } tmp_allocation = realloc(conn->app_data, (*mech)->size); @@ -599,6 +596,6 @@ Curl_sec_end(struct connectdata *conn) conn->mech = NULL; } -#endif /* HAVE_KRB4 || HAVE_GSSAPI */ +#endif /* HAVE_GSSAPI */ #endif /* CURL_DISABLE_FTP */ diff --git a/plugins/FTPFileYM/curl/lib/select.c b/plugins/FTPFileYM/curl/lib/select.c index d13e1228ad..db7fb6d43c 100644 --- a/plugins/FTPFileYM/curl/lib/select.c +++ b/plugins/FTPFileYM/curl/lib/select.c @@ -50,11 +50,8 @@ #define elapsed_ms (int)curlx_tvdiff(curlx_tvnow(), initial_tv) -#ifdef CURL_ACKNOWLEDGE_EINTR -#define error_not_EINTR (1) -#else -#define error_not_EINTR (error != EINTR) -#endif +int Curl_ack_eintr = 0; +#define error_not_EINTR (Curl_ack_eintr || error != EINTR) /* * Internal function used for waiting a specific amount of ms @@ -67,10 +64,6 @@ * Timeout resolution, accuracy, as well as maximum supported * value is system dependent, neither factor is a citical issue * for the intended use of this function in the library. - * On non-DOS and non-Winsock platforms, when compiled with - * CURL_ACKNOWLEDGE_EINTR defined, EINTR condition is honored - * and function might exit early without awaiting full timeout, - * otherwise EINTR will be ignored and full timeout will elapse. * * Return values: * -1 = system call error, invalid timeout value, or interrupted @@ -133,9 +126,6 @@ int Curl_wait_ms(int timeout_ms) * A negative timeout value makes this function wait indefinitely, * unles no valid file descriptor is given, when this happens the * negative timeout is ignored and the function times out immediately. - * When compiled with CURL_ACKNOWLEDGE_EINTR defined, EINTR condition - * is honored and function might exit early without awaiting timeout, - * otherwise EINTR will be ignored. * * Return values: * -1 = system call error or fd >= FD_SETSIZE @@ -351,9 +341,6 @@ int Curl_socket_check(curl_socket_t readfd0, /* two sockets to read from */ * A negative timeout value makes this function wait indefinitely, * unles no valid file descriptor is given, when this happens the * negative timeout is ignored and the function times out immediately. - * When compiled with CURL_ACKNOWLEDGE_EINTR defined, EINTR condition - * is honored and function might exit early without awaiting timeout, - * otherwise EINTR will be ignored. * * Return values: * -1 = system call error or fd >= FD_SETSIZE diff --git a/plugins/FTPFileYM/curl/lib/select.h b/plugins/FTPFileYM/curl/lib/select.h index 00789bb894..c00afe166f 100644 --- a/plugins/FTPFileYM/curl/lib/select.h +++ b/plugins/FTPFileYM/curl/lib/select.h @@ -81,6 +81,12 @@ int Curl_socket_check(curl_socket_t readfd, curl_socket_t readfd2, int Curl_poll(struct pollfd ufds[], unsigned int nfds, int timeout_ms); +/* On non-DOS and non-Winsock platforms, when Curl_ack_eintr is set, + * EINTR condition is honored and function might exit early without + * awaiting full timeout. Otherwise EINTR will be ignored and full + * timeout will elapse. */ +extern int Curl_ack_eintr; + int Curl_wait_ms(int timeout_ms); #ifdef TPF diff --git a/plugins/FTPFileYM/curl/lib/sendf.c b/plugins/FTPFileYM/curl/lib/sendf.c index c64d686b93..7b6fca6108 100644 --- a/plugins/FTPFileYM/curl/lib/sendf.c +++ b/plugins/FTPFileYM/curl/lib/sendf.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2012, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2013, 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 @@ -35,14 +35,6 @@ #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 "curl_memory.h" #include "strerror.h" @@ -529,8 +521,7 @@ CURLcode Curl_read(struct connectdata *conn, /* connection data */ ssize_t nread = 0; size_t bytesfromsocket = 0; char *buffertofill = NULL; - bool pipelining = (conn->data->multi && - Curl_multi_canPipeline(conn->data->multi)) ? TRUE : FALSE; + 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 diff --git a/plugins/FTPFileYM/curl/lib/setup-os400.h b/plugins/FTPFileYM/curl/lib/setup-os400.h index cdeefe3ddb..319efec929 100644 --- a/plugins/FTPFileYM/curl/lib/setup-os400.h +++ b/plugins/FTPFileYM/curl/lib/setup-os400.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2010, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2013, 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 @@ -38,6 +38,8 @@ typedef unsigned long u_int32_t; #include <sys/socket.h> #include <netdb.h> #include <qsossl.h> +#include <gskssl.h> +#include <qsoasync.h> #include <gssapi.h> extern int Curl_getaddrinfo_a(const char * nodename, const char * servname, @@ -68,6 +70,93 @@ extern char * Curl_SSL_Strerror_a(int sslreturnvalue, SSLErrorMsg * serrmsgp); #define SSL_Strerror Curl_SSL_Strerror_a +/* GSKit wrappers. */ + +extern int Curl_gsk_environment_open(gsk_handle * my_env_handle); +#define gsk_environment_open Curl_gsk_environment_open + +extern int Curl_gsk_secure_soc_open(gsk_handle my_env_handle, + gsk_handle * my_session_handle); +#define gsk_secure_soc_open Curl_gsk_secure_soc_open + +extern int Curl_gsk_environment_close(gsk_handle * my_env_handle); +#define gsk_environment_close Curl_gsk_environment_close + +extern int Curl_gsk_secure_soc_close(gsk_handle * my_session_handle); +#define gsk_secure_soc_close Curl_gsk_secure_soc_close + +extern int Curl_gsk_environment_init(gsk_handle my_env_handle); +#define gsk_environment_init Curl_gsk_environment_init + +extern int Curl_gsk_secure_soc_init(gsk_handle my_session_handle); +#define gsk_secure_soc_init Curl_gsk_secure_soc_init + +extern int Curl_gsk_attribute_set_buffer_a(gsk_handle my_gsk_handle, + GSK_BUF_ID bufID, + const char * buffer, + int bufSize); +#define gsk_attribute_set_buffer Curl_gsk_attribute_set_buffer_a + +extern int Curl_gsk_attribute_set_enum(gsk_handle my_gsk_handle, + GSK_ENUM_ID enumID, + GSK_ENUM_VALUE enumValue); +#define gsk_attribute_set_enum Curl_gsk_attribute_set_enum + +extern int Curl_gsk_attribute_set_numeric_value(gsk_handle my_gsk_handle, + GSK_NUM_ID numID, + int numValue); +#define gsk_attribute_set_numeric_value Curl_gsk_attribute_set_numeric_value + +extern int Curl_gsk_attribute_set_callback(gsk_handle my_gsk_handle, + GSK_CALLBACK_ID callBackID, + void * callBackAreaPtr); +#define gsk_attribute_set_callback Curl_gsk_attribute_set_callback + +extern int Curl_gsk_attribute_get_buffer_a(gsk_handle my_gsk_handle, + GSK_BUF_ID bufID, + const char * * buffer, + int * bufSize); +#define gsk_attribute_get_buffer Curl_gsk_attribute_get_buffer_a + +extern int Curl_gsk_attribute_get_enum(gsk_handle my_gsk_handle, + GSK_ENUM_ID enumID, + GSK_ENUM_VALUE * enumValue); +#define gsk_attribute_get_enum Curl_gsk_attribute_get_enum + +extern int Curl_gsk_attribute_get_numeric_value(gsk_handle my_gsk_handle, + GSK_NUM_ID numID, + int * numValue); +#define gsk_attribute_get_numeric_value Curl_gsk_attribute_get_numeric_value + +extern int Curl_gsk_attribute_get_cert_info(gsk_handle my_gsk_handle, + GSK_CERT_ID certID, + const gsk_cert_data_elem * * certDataElem, + int * certDataElementCount); +#define gsk_attribute_get_cert_info Curl_gsk_attribute_get_cert_info + +extern int Curl_gsk_secure_soc_misc(gsk_handle my_session_handle, + GSK_MISC_ID miscID); +#define gsk_secure_soc_misc Curl_gsk_secure_soc_misc + +extern int Curl_gsk_secure_soc_read(gsk_handle my_session_handle, + char * readBuffer, + int readBufSize, int * amtRead); +#define gsk_secure_soc_read Curl_gsk_secure_soc_read + +extern int Curl_gsk_secure_soc_write(gsk_handle my_session_handle, + char * writeBuffer, + int writeBufSize, int * amtWritten); +#define gsk_secure_soc_write Curl_gsk_secure_soc_write + +extern const char * Curl_gsk_strerror_a(int gsk_return_value); +#define gsk_strerror Curl_gsk_strerror_a + +extern int Curl_gsk_secure_soc_startInit(gsk_handle my_session_handle, + int IOCompletionPort, + Qso_OverlappedIO_t * communicationsArea); +#define gsk_secure_soc_startInit Curl_gsk_secure_soc_startInit + + /* GSSAPI wrappers. */ extern OM_uint32 Curl_gss_import_name_a(OM_uint32 * minor_status, @@ -107,6 +196,7 @@ extern OM_uint32 Curl_gss_delete_sec_context_a(OM_uint32 * minor_status, gss_buffer_t output_token); #define gss_delete_sec_context Curl_gss_delete_sec_context_a + /* LDAP wrappers. */ #define BerValue struct berval diff --git a/plugins/FTPFileYM/curl/lib/setup-vms.h b/plugins/FTPFileYM/curl/lib/setup-vms.h index 4eefbba49a..f5eedf7566 100644 --- a/plugins/FTPFileYM/curl/lib/setup-vms.h +++ b/plugins/FTPFileYM/curl/lib/setup-vms.h @@ -315,6 +315,7 @@ char * unix_path; #define d2i_PKCS12_fp D2I_PKCS12_FP #define i2t_ASN1_OBJECT I2T_ASN1_OBJECT #define sk_num SK_NUM +#define sk_pop SK_POP #define sk_pop_free SK_POP_FREE #define sk_value SK_VALUE diff --git a/plugins/FTPFileYM/curl/lib/slist.c b/plugins/FTPFileYM/curl/lib/slist.c index 92c699ec96..3cac6ca21c 100644 --- a/plugins/FTPFileYM/curl/lib/slist.c +++ b/plugins/FTPFileYM/curl/lib/slist.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2011, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2013, 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 @@ -46,6 +46,38 @@ static struct curl_slist *slist_get_last(struct curl_slist *list) } /* + * Curl_slist_append_nodup() appends a string to the linked list. Rather than + * copying the string in dynamic storage, it takes its ownership. The string + * should have been malloc()ated. Curl_slist_append_nodup always returns + * the address of the first record, so that you can use this function as an + * initialization function as well as an append function. + * If an error occurs, NULL is returned and the string argument is NOT + * released. + */ +struct curl_slist *Curl_slist_append_nodup(struct curl_slist *list, char *data) +{ + struct curl_slist *last; + struct curl_slist *new_item; + + DEBUGASSERT(data); + + new_item = malloc(sizeof(struct curl_slist)); + if(!new_item) + return NULL; + + new_item->next = NULL; + new_item->data = data; + + /* if this is the first item, then new_item *is* the list */ + if(!list) + return new_item; + + last = slist_get_last(list); + last->next = new_item; + return list; +} + +/* * curl_slist_append() appends a string to the linked list. It always returns * the address of the first record, so that you can use this function as an * initialization function as well as an append function. If you find this @@ -55,32 +87,16 @@ static struct curl_slist *slist_get_last(struct curl_slist *list) struct curl_slist *curl_slist_append(struct curl_slist *list, const char *data) { - struct curl_slist *last; - struct curl_slist *new_item; + char *dupdata = strdup(data); - new_item = malloc(sizeof(struct curl_slist)); - if(new_item) { - char *dupdata = strdup(data); - if(dupdata) { - new_item->next = NULL; - new_item->data = dupdata; - } - else { - free(new_item); - return NULL; - } - } - else + if(!dupdata) return NULL; - if(list) { - last = slist_get_last(list); - last->next = new_item; - return list; - } + list = Curl_slist_append_nodup(list, dupdata); + if(!list) + free(dupdata); - /* if this is the first item, then new_item *is* the list */ - return new_item; + return list; } /* diff --git a/plugins/FTPFileYM/curl/lib/slist.h b/plugins/FTPFileYM/curl/lib/slist.h index aede1f0418..ea7dcc48b6 100644 --- a/plugins/FTPFileYM/curl/lib/slist.h +++ b/plugins/FTPFileYM/curl/lib/slist.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2009, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2013, 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 @@ -29,5 +29,12 @@ */ struct curl_slist *Curl_slist_duplicate(struct curl_slist *inlist); +/* + * Curl_slist_append_nodup() takes ownership of the given string and appends + * it to the list. + */ +struct curl_slist *Curl_slist_append_nodup(struct curl_slist *list, + char *data); + #endif /* HEADER_CURL_SLIST_H */ diff --git a/plugins/FTPFileYM/curl/lib/smtp.c b/plugins/FTPFileYM/curl/lib/smtp.c index d2d4aeb141..9626a30d83 100644 --- a/plugins/FTPFileYM/curl/lib/smtp.c +++ b/plugins/FTPFileYM/curl/lib/smtp.c @@ -26,6 +26,8 @@ * RFC4616 PLAIN authentication * RFC4954 SMTP Authentication * RFC5321 SMTP protocol + * RFC6749 OAuth 2.0 Authorization Framework + * Draft SMTP URL Interface * ***************************************************************************/ @@ -99,7 +101,8 @@ static int smtp_getsock(struct connectdata *conn, curl_socket_t *socks, int numsocks); static CURLcode smtp_doing(struct connectdata *conn, bool *dophase_done); static CURLcode smtp_setup_connection(struct connectdata *conn); -static CURLcode smtp_state_upgrade_tls(struct connectdata *conn); +static CURLcode smtp_parse_url_options(struct connectdata *conn); +static CURLcode smtp_parse_url_path(struct connectdata *conn); /* * SMTP protocol handler. @@ -159,7 +162,7 @@ const struct Curl_handler Curl_handler_smtps = { static const struct Curl_handler Curl_handler_smtp_proxy = { "SMTP", /* scheme */ - ZERO_NULL, /* setup_connection */ + Curl_http_setup_conn, /* setup_connection */ Curl_http, /* do_it */ Curl_http_done, /* done */ ZERO_NULL, /* do_more */ @@ -184,7 +187,7 @@ static const struct Curl_handler Curl_handler_smtp_proxy = { static const struct Curl_handler Curl_handler_smtps_proxy = { "SMTPS", /* scheme */ - ZERO_NULL, /* setup_connection */ + Curl_http_setup_conn, /* setup_connection */ Curl_http, /* do_it */ Curl_http_done, /* done */ ZERO_NULL, /* do_more */ @@ -204,81 +207,108 @@ static const struct Curl_handler Curl_handler_smtps_proxy = { #endif #endif -/* Function that checks for an ending smtp status code at the start of the - given string, but also detects the supported authentication mechanisms - from the EHLO AUTH response. */ -static int smtp_endofresp(struct pingpong *pp, int *resp) +#ifdef USE_SSL +static void smtp_to_smtps(struct connectdata *conn) +{ + conn->handler = &Curl_handler_smtps; +} +#else +#define smtp_to_smtps(x) Curl_nop_stmt +#endif + +/*********************************************************************** + * + * pop3_endofresp() + * + * Checks for an ending SMTP status code at the start of the given string, but + * also detects various capabilities from the EHLO response including the + * supported authentication mechanisms. + */ +static bool smtp_endofresp(struct connectdata *conn, char *line, size_t len, + int *resp) { - char *line = pp->linestart_resp; - size_t len = pp->nread_resp; - struct connectdata *conn = pp->conn; struct smtp_conn *smtpc = &conn->proto.smtpc; - int result; + bool result = FALSE; size_t wordlen; if(len < 4 || !ISDIGIT(line[0]) || !ISDIGIT(line[1]) || !ISDIGIT(line[2])) return FALSE; /* Nothing for us */ - /* Extract the response code if necessary */ - if((result = (line[3] == ' ')) != 0) + /* Do we have a command response? */ + result = (line[3] == ' ') ? TRUE : FALSE; + if(result) *resp = curlx_sltosi(strtol(line, NULL, 10)); - line += 4; - len -= 4; - - /* Does the server support the SIZE capability? */ - if(smtpc->state == SMTP_EHLO && len >= 4 && !memcmp(line, "SIZE", 4)) { - DEBUGF(infof(conn->data, "Server supports SIZE extension.\n")); - smtpc->size_supported = true; - } - - /* Do we have the authentication mechanism list? */ - if(smtpc->state == SMTP_EHLO && len >= 5 && !memcmp(line, "AUTH ", 5)) { - line += 5; - len -= 5; - - for(;;) { - while(len && - (*line == ' ' || *line == '\t' || - *line == '\r' || *line == '\n')) { - line++; - len--; + /* Are we processing EHLO command data? */ + if(smtpc->state == SMTP_EHLO && (!result || (result && *resp/100 == 2))) { + line += 4; + len -= 4; + + /* Does the server support the STARTTLS capability? */ + if(len >= 8 && !memcmp(line, "STARTTLS", 8)) + smtpc->tls_supported = TRUE; + + /* Does the server support the SIZE capability? */ + else if(len >= 4 && !memcmp(line, "SIZE", 4)) + smtpc->size_supported = TRUE; + + /* Do we have the authentication mechanism list? */ + else if(len >= 5 && !memcmp(line, "AUTH ", 5)) { + line += 5; + len -= 5; + + /* Loop through the data line */ + for(;;) { + while(len && + (*line == ' ' || *line == '\t' || + *line == '\r' || *line == '\n')) { + + line++; + len--; + } + + if(!len) + break; + + /* Extract the word */ + for(wordlen = 0; wordlen < len && line[wordlen] != ' ' && + line[wordlen] != '\t' && line[wordlen] != '\r' && + line[wordlen] != '\n';) + wordlen++; + + /* Test the word for a matching authentication mechanism */ + if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_LOGIN)) + smtpc->authmechs |= SASL_MECH_LOGIN; + else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_PLAIN)) + smtpc->authmechs |= SASL_MECH_PLAIN; + else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_CRAM_MD5)) + smtpc->authmechs |= SASL_MECH_CRAM_MD5; + else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_DIGEST_MD5)) + smtpc->authmechs |= SASL_MECH_DIGEST_MD5; + else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_GSSAPI)) + smtpc->authmechs |= SASL_MECH_GSSAPI; + else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_EXTERNAL)) + smtpc->authmechs |= SASL_MECH_EXTERNAL; + else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_NTLM)) + smtpc->authmechs |= SASL_MECH_NTLM; + else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_XOAUTH2)) + smtpc->authmechs |= SASL_MECH_XOAUTH2; + + line += wordlen; + len -= wordlen; } - - if(!len) - break; - - /* Extract the word */ - for(wordlen = 0; wordlen < len && line[wordlen] != ' ' && - line[wordlen] != '\t' && line[wordlen] != '\r' && - line[wordlen] != '\n';) - wordlen++; - - /* Test the word for a matching authentication mechanism */ - if(wordlen == 5 && !memcmp(line, "LOGIN", 5)) - smtpc->authmechs |= SASL_MECH_LOGIN; - else if(wordlen == 5 && !memcmp(line, "PLAIN", 5)) - smtpc->authmechs |= SASL_MECH_PLAIN; - else if(wordlen == 8 && !memcmp(line, "CRAM-MD5", 8)) - smtpc->authmechs |= SASL_MECH_CRAM_MD5; - else if(wordlen == 10 && !memcmp(line, "DIGEST-MD5", 10)) - smtpc->authmechs |= SASL_MECH_DIGEST_MD5; - else if(wordlen == 6 && !memcmp(line, "GSSAPI", 6)) - smtpc->authmechs |= SASL_MECH_GSSAPI; - else if(wordlen == 8 && !memcmp(line, "EXTERNAL", 8)) - smtpc->authmechs |= SASL_MECH_EXTERNAL; - else if(wordlen == 4 && !memcmp(line, "NTLM", 4)) - smtpc->authmechs |= SASL_MECH_NTLM; - - line += wordlen; - len -= wordlen; } } return result; } -/* This is the ONLY way to change SMTP state! */ +/*********************************************************************** + * + * state() + * + * This is the ONLY way to change SMTP state! + */ static void state(struct connectdata *conn, smtpstate newstate) { struct smtp_conn *smtpc = &conn->proto.smtpc; @@ -293,13 +323,14 @@ static void state(struct connectdata *conn, smtpstate newstate) "UPGRADETLS", "AUTH_PLAIN", "AUTH_LOGIN", - "AUTH_PASSWD", + "AUTH_LOGIN_PASSWD", "AUTH_CRAMMD5", "AUTH_DIGESTMD5", "AUTH_DIGESTMD5_RESP", "AUTH_NTLM", "AUTH_NTLM_TYPE2MSG", - "AUTH", + "AUTH_XOAUTH2", + "AUTH_FINAL", "MAIL", "RCPT", "DATA", @@ -310,35 +341,47 @@ static void state(struct connectdata *conn, smtpstate newstate) if(smtpc->state != newstate) infof(conn->data, "SMTP %p state change from %s to %s\n", - smtpc, names[smtpc->state], names[newstate]); + (void *)smtpc, names[smtpc->state], names[newstate]); #endif smtpc->state = newstate; } -static CURLcode smtp_state_ehlo(struct connectdata *conn) +/*********************************************************************** + * + * smtp_perform_ehlo() + * + * Sends the EHLO command to not only initialise communication with the ESMTP + * server but to also obtain a list of server side supported capabilities. + */ +static CURLcode smtp_perform_ehlo(struct connectdata *conn) { - CURLcode result; + CURLcode result = CURLE_OK; struct smtp_conn *smtpc = &conn->proto.smtpc; smtpc->authmechs = 0; /* No known authentication mechanisms yet */ smtpc->authused = 0; /* Clear the authentication mechanism used for esmtp connections */ + smtpc->tls_supported = FALSE; /* Clear the TLS capability */ /* Send the EHLO command */ result = Curl_pp_sendf(&smtpc->pp, "EHLO %s", smtpc->domain); - if(result) - return result; + if(!result) + state(conn, SMTP_EHLO); - state(conn, SMTP_EHLO); - - return CURLE_OK; + return result; } -static CURLcode smtp_state_helo(struct connectdata *conn) +/*********************************************************************** + * + * smtp_perform_helo() + * + * Sends the HELO command to initialise communication with the SMTP server. + */ +static CURLcode smtp_perform_helo(struct connectdata *conn) { - CURLcode result; + CURLcode result = CURLE_OK; struct smtp_conn *smtpc = &conn->proto.smtpc; smtpc->authused = 0; /* No authentication mechanism used in smtp @@ -347,20 +390,72 @@ static CURLcode smtp_state_helo(struct connectdata *conn) /* Send the HELO command */ result = Curl_pp_sendf(&smtpc->pp, "HELO %s", smtpc->domain); - if(result) - return result; + if(!result) + state(conn, SMTP_HELO); - state(conn, SMTP_HELO); + return result; +} - return CURLE_OK; +/*********************************************************************** + * + * smtp_perform_starttls() + * + * Sends the STLS command to start the upgrade to TLS. + */ +static CURLcode smtp_perform_starttls(struct connectdata *conn) +{ + CURLcode result = CURLE_OK; + + /* Send the STARTTLS command */ + result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", "STARTTLS"); + + if(!result) + state(conn, SMTP_STARTTLS); + + return result; } -static CURLcode smtp_authenticate(struct connectdata *conn) +/*********************************************************************** + * + * smtp_perform_upgrade_tls() + * + * Performs the upgrade to TLS. + */ +static CURLcode smtp_perform_upgrade_tls(struct connectdata *conn) { CURLcode result = CURLE_OK; struct smtp_conn *smtpc = &conn->proto.smtpc; - char *initresp = NULL; + + /* Start the SSL connection */ + result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, &smtpc->ssldone); + + if(!result) { + if(smtpc->state != SMTP_UPGRADETLS) + state(conn, SMTP_UPGRADETLS); + + if(smtpc->ssldone) { + smtp_to_smtps(conn); + result = smtp_perform_ehlo(conn); + } + } + + return result; +} + +/*********************************************************************** + * + * smtp_perform_authenticate() + * + * Sends an AUTH command allowing the client to login with the appropriate + * SASL authentication mechanism. + */ +static CURLcode smtp_perform_authenticate(struct connectdata *conn) +{ + CURLcode result = CURLE_OK; + struct SessionHandle *data = conn->data; + struct smtp_conn *smtpc = &conn->proto.smtpc; const char *mech = NULL; + char *initresp = NULL; size_t len = 0; smtpstate state1 = SMTP_STOP; smtpstate state2 = SMTP_STOP; @@ -376,89 +471,223 @@ static CURLcode smtp_authenticate(struct connectdata *conn) /* Calculate the supported authentication mechanism, by decreasing order of security, as well as the initial response where appropriate */ #ifndef CURL_DISABLE_CRYPTO_AUTH - if(smtpc->authmechs & SASL_MECH_DIGEST_MD5) { - mech = "DIGEST-MD5"; + if((smtpc->authmechs & SASL_MECH_DIGEST_MD5) && + (smtpc->prefmech & SASL_MECH_DIGEST_MD5)) { + mech = SASL_MECH_STRING_DIGEST_MD5; state1 = SMTP_AUTH_DIGESTMD5; smtpc->authused = SASL_MECH_DIGEST_MD5; } - else if(smtpc->authmechs & SASL_MECH_CRAM_MD5) { - mech = "CRAM-MD5"; + else if((smtpc->authmechs & SASL_MECH_CRAM_MD5) && + (smtpc->prefmech & SASL_MECH_CRAM_MD5)) { + mech = SASL_MECH_STRING_CRAM_MD5; state1 = SMTP_AUTH_CRAMMD5; smtpc->authused = SASL_MECH_CRAM_MD5; } else #endif #ifdef USE_NTLM - if(smtpc->authmechs & SASL_MECH_NTLM) { - mech = "NTLM"; + if((smtpc->authmechs & SASL_MECH_NTLM) && + (smtpc->prefmech & SASL_MECH_NTLM)) { + mech = SASL_MECH_STRING_NTLM; state1 = SMTP_AUTH_NTLM; state2 = SMTP_AUTH_NTLM_TYPE2MSG; smtpc->authused = SASL_MECH_NTLM; - result = Curl_sasl_create_ntlm_type1_message(conn->user, conn->passwd, - &conn->ntlm, - &initresp, &len); - } + + if(data->set.sasl_ir) + result = Curl_sasl_create_ntlm_type1_message(conn->user, conn->passwd, + &conn->ntlm, + &initresp, &len); + } else #endif - if(smtpc->authmechs & SASL_MECH_LOGIN) { - mech = "LOGIN"; + if(((smtpc->authmechs & SASL_MECH_XOAUTH2) && + (smtpc->prefmech & SASL_MECH_XOAUTH2) && + (smtpc->prefmech != SASL_AUTH_ANY)) || conn->xoauth2_bearer) { + mech = SASL_MECH_STRING_XOAUTH2; + state1 = SMTP_AUTH_XOAUTH2; + state2 = SMTP_AUTH_FINAL; + smtpc->authused = SASL_MECH_XOAUTH2; + + if(data->set.sasl_ir) + result = Curl_sasl_create_xoauth2_message(conn->data, conn->user, + conn->xoauth2_bearer, + &initresp, &len); + } + else if((smtpc->authmechs & SASL_MECH_LOGIN) && + (smtpc->prefmech & SASL_MECH_LOGIN)) { + mech = SASL_MECH_STRING_LOGIN; state1 = SMTP_AUTH_LOGIN; - state2 = SMTP_AUTH_PASSWD; + state2 = SMTP_AUTH_LOGIN_PASSWD; smtpc->authused = SASL_MECH_LOGIN; - result = Curl_sasl_create_login_message(conn->data, conn->user, - &initresp, &len); + + if(data->set.sasl_ir) + result = Curl_sasl_create_login_message(conn->data, conn->user, + &initresp, &len); } - else if(smtpc->authmechs & SASL_MECH_PLAIN) { - mech = "PLAIN"; + else if((smtpc->authmechs & SASL_MECH_PLAIN) && + (smtpc->prefmech & SASL_MECH_PLAIN)) { + mech = SASL_MECH_STRING_PLAIN; state1 = SMTP_AUTH_PLAIN; - state2 = SMTP_AUTH; + state2 = SMTP_AUTH_FINAL; smtpc->authused = SASL_MECH_PLAIN; - result = Curl_sasl_create_plain_message(conn->data, conn->user, - conn->passwd, &initresp, &len); - } - else { - /* Other mechanisms not supported */ - infof(conn->data, "No known authentication mechanisms supported!\n"); - result = CURLE_LOGIN_DENIED; + + if(data->set.sasl_ir) + result = Curl_sasl_create_plain_message(conn->data, conn->user, + conn->passwd, &initresp, &len); } if(!result) { - /* Perform SASL based authentication */ - if(initresp && - strlen(mech) + len <= 512 - 8) { /* AUTH <mech> ...<crlf> */ - result = Curl_pp_sendf(&smtpc->pp, "AUTH %s %s", mech, initresp); + if(mech) { + /* Perform SASL based authentication */ + if(initresp && + 8 + strlen(mech) + len <= 512) { /* AUTH <mech> ...<crlf> */ + result = Curl_pp_sendf(&smtpc->pp, "AUTH %s %s", mech, initresp); - if(!result) - state(conn, state2); + if(!result) + state(conn, state2); + } + else { + result = Curl_pp_sendf(&smtpc->pp, "AUTH %s", mech); + + if(!result) + state(conn, state1); + } + + Curl_safefree(initresp); } else { - result = Curl_pp_sendf(&smtpc->pp, "AUTH %s", mech); + /* Other mechanisms not supported */ + infof(conn->data, "No known authentication mechanisms supported!\n"); + result = CURLE_LOGIN_DENIED; + } + } - if(!result) - state(conn, state1); + return result; +} + +/*********************************************************************** + * + * smtp_perform_mail() + * + * Sends an MAIL command to initiate the upload of a message. + */ +static CURLcode smtp_perform_mail(struct connectdata *conn) +{ + char *from = NULL; + char *auth = NULL; + char *size = NULL; + CURLcode result = CURLE_OK; + struct SessionHandle *data = conn->data; + + /* Calculate the FROM parameter */ + if(!data->set.str[STRING_MAIL_FROM]) + /* Null reverse-path, RFC-5321, sect. 3.6.3 */ + from = strdup("<>"); + else if(data->set.str[STRING_MAIL_FROM][0] == '<') + from = aprintf("%s", data->set.str[STRING_MAIL_FROM]); + else + from = aprintf("<%s>", data->set.str[STRING_MAIL_FROM]); + + if(!from) + return CURLE_OUT_OF_MEMORY; + + /* Calculate the optional AUTH parameter */ + if(data->set.str[STRING_MAIL_AUTH] && conn->proto.smtpc.authused) { + if(data->set.str[STRING_MAIL_AUTH][0] != '\0') + auth = aprintf("%s", data->set.str[STRING_MAIL_AUTH]); + else + /* Empty AUTH, RFC-2554, sect. 5 */ + auth = strdup("<>"); + + if(!auth) { + Curl_safefree(from); + + return CURLE_OUT_OF_MEMORY; } + } - Curl_safefree(initresp); + /* Calculate the optional SIZE parameter */ + if(conn->proto.smtpc.size_supported && conn->data->set.infilesize > 0) { + size = aprintf("%" FORMAT_OFF_T, data->set.infilesize); + + if(!size) { + Curl_safefree(from); + Curl_safefree(auth); + + return CURLE_OUT_OF_MEMORY; + } } + /* Send the MAIL command */ + if(!auth && !size) + result = Curl_pp_sendf(&conn->proto.smtpc.pp, + "MAIL FROM:%s", from); + else if(auth && !size) + result = Curl_pp_sendf(&conn->proto.smtpc.pp, + "MAIL FROM:%s AUTH=%s", from, auth); + else if(auth && size) + result = Curl_pp_sendf(&conn->proto.smtpc.pp, + "MAIL FROM:%s AUTH=%s SIZE=%s", from, auth, size); + else + result = Curl_pp_sendf(&conn->proto.smtpc.pp, + "MAIL FROM:%s SIZE=%s", from, size); + + Curl_safefree(from); + Curl_safefree(auth); + Curl_safefree(size); + + if(!result) + state(conn, SMTP_MAIL); + return result; } -/* For the SMTP "protocol connect" and "doing" phases only */ -static int smtp_getsock(struct connectdata *conn, curl_socket_t *socks, - int numsocks) +/*********************************************************************** + * + * smtp_perform_rcpt_to() + * + * Sends a RCPT TO command for a given recipient as part of the message upload + * process. + */ +static CURLcode smtp_perform_rcpt_to(struct connectdata *conn) { - return Curl_pp_getsock(&conn->proto.smtpc.pp, socks, numsocks); + CURLcode result = CURLE_OK; + struct SessionHandle *data = conn->data; + struct SMTP *smtp = data->req.protop; + + /* Send the RCPT TO command */ + if(smtp->rcpt) { + if(smtp->rcpt->data[0] == '<') + result = Curl_pp_sendf(&conn->proto.smtpc.pp, "RCPT TO:%s", + smtp->rcpt->data); + else + result = Curl_pp_sendf(&conn->proto.smtpc.pp, "RCPT TO:<%s>", + smtp->rcpt->data); + if(!result) + state(conn, SMTP_RCPT); + } + + return result; } -#ifdef USE_SSL -static void smtp_to_smtps(struct connectdata *conn) +/*********************************************************************** + * + * smtp_perform_quit() + * + * Performs the quit action prior to sclose() being called. + */ +static CURLcode smtp_perform_quit(struct connectdata *conn) { - conn->handler = &Curl_handler_smtps; + CURLcode result = CURLE_OK; + + /* Send the QUIT command */ + result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", "QUIT"); + + if(!result) + state(conn, SMTP_QUIT); + + return result; } -#else -#define smtp_to_smtps(x) Curl_nop_stmt -#endif /* For the initial server greeting */ static CURLcode smtp_state_servergreet_resp(struct connectdata *conn, @@ -472,10 +701,10 @@ static CURLcode smtp_state_servergreet_resp(struct connectdata *conn, if(smtpcode/100 != 2) { failf(data, "Got unexpected smtp-server response: %d", smtpcode); - return CURLE_FTP_WEIRD_SERVER_REPLY; + result = CURLE_FTP_WEIRD_SERVER_REPLY; } - - result = smtp_state_ehlo(conn); + else + result = smtp_perform_ehlo(conn); return result; } @@ -496,30 +725,10 @@ static CURLcode smtp_state_starttls_resp(struct connectdata *conn, result = CURLE_USE_SSL_FAILED; } else - result = smtp_authenticate(conn); + result = smtp_perform_authenticate(conn); } else - result = smtp_state_upgrade_tls(conn); - - return result; -} - -static CURLcode smtp_state_upgrade_tls(struct connectdata *conn) -{ - struct smtp_conn *smtpc = &conn->proto.smtpc; - CURLcode result; - - result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, &smtpc->ssldone); - - if(!result) { - if(smtpc->state != SMTP_UPGRADETLS) - state(conn, SMTP_UPGRADETLS); - - if(smtpc->ssldone) { - smtp_to_smtps(conn); - result = smtp_state_ehlo(conn); - } - } + result = smtp_perform_upgrade_tls(conn); return result; } @@ -530,27 +739,34 @@ static CURLcode smtp_state_ehlo_resp(struct connectdata *conn, int smtpcode, { CURLcode result = CURLE_OK; struct SessionHandle *data = conn->data; + struct smtp_conn *smtpc = &conn->proto.smtpc; (void)instate; /* no use for this yet */ if(smtpcode/100 != 2) { if((data->set.use_ssl <= CURLUSESSL_TRY || conn->ssl[FIRSTSOCKET].use) && !conn->bits.user_passwd) - result = smtp_state_helo(conn); + result = smtp_perform_helo(conn); else { failf(data, "Remote access denied: %d", smtpcode); result = CURLE_REMOTE_ACCESS_DENIED; } } else if(data->set.use_ssl && !conn->ssl[FIRSTSOCKET].use) { - /* We don't have a SSL/TLS connection yet, but SSL is requested. Switch - to TLS connection now */ - result = Curl_pp_sendf(&conn->proto.smtpc.pp, "STARTTLS"); - if(!result) - state(conn, SMTP_STARTTLS); + /* We don't have a SSL/TLS connection yet, but SSL is requested */ + if(smtpc->tls_supported) + /* Switch to TLS connection now */ + result = smtp_perform_starttls(conn); + else if(data->set.use_ssl == CURLUSESSL_TRY) + /* Fallback and carry on with authentication */ + result = smtp_perform_authenticate(conn); + else { + failf(data, "STARTTLS not supported."); + result = CURLE_USE_SSL_FAILED; + } } else - result = smtp_authenticate(conn); + result = smtp_perform_authenticate(conn); return result; } @@ -602,7 +818,7 @@ static CURLcode smtp_state_auth_plain_resp(struct connectdata *conn, result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", plainauth); if(!result) - state(conn, SMTP_AUTH); + state(conn, SMTP_AUTH_FINAL); } Curl_safefree(plainauth); @@ -639,7 +855,7 @@ static CURLcode smtp_state_auth_login_resp(struct connectdata *conn, result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", authuser); if(!result) - state(conn, SMTP_AUTH_PASSWD); + state(conn, SMTP_AUTH_LOGIN_PASSWD); } Curl_safefree(authuser); @@ -649,10 +865,10 @@ static CURLcode smtp_state_auth_login_resp(struct connectdata *conn, return result; } -/* For responses to user entry of AUTH LOGIN */ -static CURLcode smtp_state_auth_passwd_resp(struct connectdata *conn, - int smtpcode, - smtpstate instate) +/* For AUTH LOGIN user entry responses */ +static CURLcode smtp_state_auth_login_password_resp(struct connectdata *conn, + int smtpcode, + smtpstate instate) { CURLcode result = CURLE_OK; struct SessionHandle *data = conn->data; @@ -676,7 +892,7 @@ static CURLcode smtp_state_auth_passwd_resp(struct connectdata *conn, result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", authpasswd); if(!result) - state(conn, SMTP_AUTH); + state(conn, SMTP_AUTH_FINAL); } Curl_safefree(authpasswd); @@ -731,7 +947,7 @@ static CURLcode smtp_state_auth_cram_resp(struct connectdata *conn, result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", rplyb64); if(!result) - state(conn, SMTP_AUTH); + state(conn, SMTP_AUTH_FINAL); } Curl_safefree(rplyb64); @@ -798,10 +1014,10 @@ static CURLcode smtp_state_auth_digest_resp_resp(struct connectdata *conn, } else { /* Send an empty response */ - result = Curl_pp_sendf(&conn->proto.smtpc.pp, ""); + result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", ""); if(!result) - state(conn, SMTP_AUTH); + state(conn, SMTP_AUTH_FINAL); } return result; @@ -878,7 +1094,7 @@ static CURLcode smtp_state_auth_ntlm_type2msg_resp(struct connectdata *conn, result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", type3msg); if(!result) - state(conn, SMTP_AUTH); + state(conn, SMTP_AUTH_FINAL); } Curl_safefree(type3msg); @@ -889,116 +1105,60 @@ static CURLcode smtp_state_auth_ntlm_type2msg_resp(struct connectdata *conn, } #endif -/* For the final responses to the AUTH sequence */ -static CURLcode smtp_state_auth_resp(struct connectdata *conn, int smtpcode, - smtpstate instate) +/* For AUTH XOAUTH2 (without initial response) responses */ +static CURLcode smtp_state_auth_xoauth2_resp(struct connectdata *conn, + int smtpcode, smtpstate instate) { CURLcode result = CURLE_OK; struct SessionHandle *data = conn->data; + size_t len = 0; + char *xoauth = NULL; (void)instate; /* no use for this yet */ - if(smtpcode != 235) { - failf(data, "Authentication failed: %d", smtpcode); + if(smtpcode != 334) { + failf(data, "Access denied: %d", smtpcode); result = CURLE_LOGIN_DENIED; } - else - /* End of connect phase */ - state(conn, SMTP_STOP); - - return result; -} - -/* Start the DO phase */ -static CURLcode smtp_mail(struct connectdata *conn) -{ - char *from = NULL; - char *auth = NULL; - char *size = NULL; - CURLcode result = CURLE_OK; - struct SessionHandle *data = conn->data; - - /* Calculate the FROM parameter */ - if(!data->set.str[STRING_MAIL_FROM]) - /* Null reverse-path, RFC-2821, sect. 3.7 */ - from = strdup("<>"); - else if(data->set.str[STRING_MAIL_FROM][0] == '<') - from = aprintf("%s", data->set.str[STRING_MAIL_FROM]); - else - from = aprintf("<%s>", data->set.str[STRING_MAIL_FROM]); - - if(!from) - return CURLE_OUT_OF_MEMORY; + else { + /* Create the authorisation message */ + result = Curl_sasl_create_xoauth2_message(conn->data, conn->user, + conn->xoauth2_bearer, + &xoauth, &len); - /* Calculate the optional AUTH parameter */ - if(data->set.str[STRING_MAIL_AUTH] && conn->proto.smtpc.authused) { - if(data->set.str[STRING_MAIL_AUTH][0] != '\0') - auth = aprintf("%s", data->set.str[STRING_MAIL_AUTH]); - else - /* Empty AUTH, RFC-2554, sect. 5 */ - auth = strdup("<>"); + /* Send the message */ + if(!result) { + if(xoauth) { + result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", xoauth); - if(!auth) { - Curl_safefree(from); + if(!result) + state(conn, SMTP_AUTH_FINAL); + } - return CURLE_OUT_OF_MEMORY; + Curl_safefree(xoauth); } } - /* calculate the optional SIZE parameter */ - if(conn->proto.smtpc.size_supported && conn->data->set.infilesize > 0) { - size = aprintf("%" FORMAT_OFF_T, data->set.infilesize); - - if(!size) { - Curl_safefree(from); - Curl_safefree(auth); - - return CURLE_OUT_OF_MEMORY; - } - } - - /* Send the MAIL command */ - if(!auth && !size) - result = Curl_pp_sendf(&conn->proto.smtpc.pp, - "MAIL FROM:%s", from); - else if(auth && !size) - result = Curl_pp_sendf(&conn->proto.smtpc.pp, - "MAIL FROM:%s AUTH=%s", from, auth); - else if(auth && size) - result = Curl_pp_sendf(&conn->proto.smtpc.pp, - "MAIL FROM:%s AUTH=%s SIZE=%s", from, auth, size); - else - result = Curl_pp_sendf(&conn->proto.smtpc.pp, - "MAIL FROM:%s SIZE=%s", from, size); - - Curl_safefree(from); - Curl_safefree(auth); - Curl_safefree(size); - - if(result) - return result; - - state(conn, SMTP_MAIL); - return result; } -static CURLcode smtp_rcpt_to(struct connectdata *conn) +/* For the final responses to the AUTH sequence */ +static CURLcode smtp_state_auth_final_resp(struct connectdata *conn, + int smtpcode, + smtpstate instate) { CURLcode result = CURLE_OK; - struct smtp_conn *smtpc = &conn->proto.smtpc; + struct SessionHandle *data = conn->data; - /* Send the RCPT TO command */ - if(smtpc->rcpt) { - if(smtpc->rcpt->data[0] == '<') - result = Curl_pp_sendf(&conn->proto.smtpc.pp, "RCPT TO:%s", - smtpc->rcpt->data); - else - result = Curl_pp_sendf(&conn->proto.smtpc.pp, "RCPT TO:<%s>", - smtpc->rcpt->data); - if(!result) - state(conn, SMTP_RCPT); + (void)instate; /* no use for this yet */ + + if(smtpcode != 235) { + failf(data, "Authentication failed: %d", smtpcode); + result = CURLE_LOGIN_DENIED; } + else + /* End of connect phase */ + state(conn, SMTP_STOP); return result; } @@ -1009,6 +1169,7 @@ static CURLcode smtp_state_mail_resp(struct connectdata *conn, int smtpcode, { CURLcode result = CURLE_OK; struct SessionHandle *data = conn->data; + struct SMTP *smtp = data->req.protop; (void)instate; /* no use for this yet */ @@ -1018,10 +1179,9 @@ static CURLcode smtp_state_mail_resp(struct connectdata *conn, int smtpcode, state(conn, SMTP_STOP); } else { - struct smtp_conn *smtpc = &conn->proto.smtpc; - smtpc->rcpt = data->set.mail_rcpt; + smtp->rcpt = data->set.mail_rcpt; - result = smtp_rcpt_to(conn); + result = smtp_perform_rcpt_to(conn); } return result; @@ -1033,6 +1193,7 @@ static CURLcode smtp_state_rcpt_resp(struct connectdata *conn, int smtpcode, { CURLcode result = CURLE_OK; struct SessionHandle *data = conn->data; + struct SMTP *smtp = data->req.protop; (void)instate; /* no use for this yet */ @@ -1042,24 +1203,20 @@ static CURLcode smtp_state_rcpt_resp(struct connectdata *conn, int smtpcode, state(conn, SMTP_STOP); } else { - struct smtp_conn *smtpc = &conn->proto.smtpc; - - if(smtpc->rcpt) { - smtpc->rcpt = smtpc->rcpt->next; - result = smtp_rcpt_to(conn); + if(smtp->rcpt) { + smtp->rcpt = smtp->rcpt->next; + result = smtp_perform_rcpt_to(conn); /* If we failed or still are sending RCPT data then return */ - if(result || smtpc->rcpt) + if(result || smtp->rcpt) return result; } /* Send the DATA command */ - result = Curl_pp_sendf(&conn->proto.smtpc.pp, "DATA"); + result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", "DATA"); - if(result) - return result; - - state(conn, SMTP_DATA); + if(!result) + state(conn, SMTP_DATA); } return result; @@ -1070,7 +1227,6 @@ static CURLcode smtp_state_data_resp(struct connectdata *conn, int smtpcode, smtpstate instate) { struct SessionHandle *data = conn->data; - struct FTP *smtp = data->state.proto.smtp; (void)instate; /* no use for this yet */ @@ -1079,11 +1235,13 @@ static CURLcode smtp_state_data_resp(struct connectdata *conn, int smtpcode, return CURLE_SEND_ERROR; } + /* Set the progress upload size */ + Curl_pgrsSetUploadSize(data, data->set.infilesize); + /* SMTP upload */ - Curl_setup_transfer(conn, -1, -1, FALSE, NULL, /* no download */ - FIRSTSOCKET, smtp->bytecountp); + Curl_setup_transfer(conn, -1, -1, FALSE, NULL, FIRSTSOCKET, NULL); - /* End of do phase */ + /* End of DO phase */ state(conn, SMTP_STOP); return CURLE_OK; @@ -1102,7 +1260,7 @@ static CURLcode smtp_state_postdata_resp(struct connectdata *conn, if(smtpcode != 250) result = CURLE_RECV_ERROR; - /* End of done phase */ + /* End of DONE phase */ state(conn, SMTP_STOP); return result; @@ -1110,7 +1268,7 @@ static CURLcode smtp_state_postdata_resp(struct connectdata *conn, static CURLcode smtp_statemach_act(struct connectdata *conn) { - CURLcode result; + CURLcode result = CURLE_OK; curl_socket_t sock = conn->sock[FIRSTSOCKET]; struct SessionHandle *data = conn->data; int smtpcode; @@ -1120,7 +1278,7 @@ static CURLcode smtp_statemach_act(struct connectdata *conn) /* Busy upgrading the connection; right now all I/O is SSL/TLS, not SMTP */ if(smtpc->state == SMTP_UPGRADETLS) - return smtp_state_upgrade_tls(conn); + return smtp_perform_upgrade_tls(conn); /* Flush any data that needs to be sent */ if(pp->sendleft) @@ -1162,8 +1320,9 @@ static CURLcode smtp_statemach_act(struct connectdata *conn) result = smtp_state_auth_login_resp(conn, smtpcode, smtpc->state); break; - case SMTP_AUTH_PASSWD: - result = smtp_state_auth_passwd_resp(conn, smtpcode, smtpc->state); + case SMTP_AUTH_LOGIN_PASSWD: + result = smtp_state_auth_login_password_resp(conn, smtpcode, + smtpc->state); break; #ifndef CURL_DISABLE_CRYPTO_AUTH @@ -1191,8 +1350,12 @@ static CURLcode smtp_statemach_act(struct connectdata *conn) break; #endif - case SMTP_AUTH: - result = smtp_state_auth_resp(conn, smtpcode, smtpc->state); + case SMTP_AUTH_XOAUTH2: + result = smtp_state_auth_xoauth2_resp(conn, smtpcode, smtpc->state); + break; + + case SMTP_AUTH_FINAL: + result = smtp_state_auth_final_resp(conn, smtpcode, smtpc->state); break; case SMTP_MAIL: @@ -1226,30 +1389,28 @@ static CURLcode smtp_statemach_act(struct connectdata *conn) /* Called repeatedly until done from multi.c */ static CURLcode smtp_multi_statemach(struct connectdata *conn, bool *done) { + CURLcode result = CURLE_OK; struct smtp_conn *smtpc = &conn->proto.smtpc; - CURLcode result; - if((conn->handler->flags & PROTOPT_SSL) && !smtpc->ssldone) + if((conn->handler->flags & PROTOPT_SSL) && !smtpc->ssldone) { result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, &smtpc->ssldone); - else - result = Curl_pp_multi_statemach(&smtpc->pp); + if(result || !smtpc->ssldone) + return result; + } + result = Curl_pp_statemach(&smtpc->pp, FALSE); *done = (smtpc->state == SMTP_STOP) ? TRUE : FALSE; return result; } -static CURLcode smtp_easy_statemach(struct connectdata *conn) +static CURLcode smtp_block_statemach(struct connectdata *conn) { - struct smtp_conn *smtpc = &conn->proto.smtpc; - struct pingpong *pp = &smtpc->pp; CURLcode result = CURLE_OK; + struct smtp_conn *smtpc = &conn->proto.smtpc; - while(smtpc->state != SMTP_STOP) { - result = Curl_pp_easy_statemach(pp); - if(result) - break; - } + while(smtpc->state != SMTP_STOP && !result) + result = Curl_pp_statemach(&smtpc->pp, TRUE); return result; } @@ -1258,26 +1419,22 @@ static CURLcode smtp_easy_statemach(struct connectdata *conn) required */ static CURLcode smtp_init(struct connectdata *conn) { + CURLcode result = CURLE_OK; struct SessionHandle *data = conn->data; - struct FTP *smtp = data->state.proto.smtp; + struct SMTP *smtp; - if(!smtp) { - smtp = data->state.proto.smtp = calloc(sizeof(struct FTP), 1); - if(!smtp) - return CURLE_OUT_OF_MEMORY; - } - - /* Get some initial data into the smtp struct */ - smtp->bytecountp = &data->req.bytecount; + smtp = data->req.protop = calloc(sizeof(struct SMTP), 1); + if(!smtp) + result = CURLE_OUT_OF_MEMORY; - /* No need to duplicate user+password, the connectdata struct won't change - during a session, but we re-init them here since on subsequent inits - since the conn struct may have changed or been replaced. - */ - smtp->user = conn->user; - smtp->passwd = conn->passwd; + return result; +} - return CURLE_OK; +/* For the SMTP "protocol connect" and "doing" phases only */ +static int smtp_getsock(struct connectdata *conn, curl_socket_t *socks, + int numsocks) +{ + return Curl_pp_getsock(&conn->proto.smtpc.pp, socks, numsocks); } /*********************************************************************** @@ -1288,54 +1445,38 @@ static CURLcode smtp_init(struct connectdata *conn) * the connection phase. * * The variable pointed to by 'done' will be TRUE if the protocol-layer - * connect phase is done when this function returns, or FALSE if not. When - * called as a part of the easy interface, it will always be TRUE. + * connect phase is done when this function returns, or FALSE if not. */ static CURLcode smtp_connect(struct connectdata *conn, bool *done) { - CURLcode result; + CURLcode result = CURLE_OK; struct smtp_conn *smtpc = &conn->proto.smtpc; struct pingpong *pp = &smtpc->pp; - const char *path = conn->data->state.path; - char localhost[HOSTNAME_MAX + 1]; *done = FALSE; /* default to not done yet */ - /* If there already is a protocol-specific struct allocated for this - sessionhandle, deal with it */ - Curl_reset_reqproto(conn); - - result = smtp_init(conn); - if(CURLE_OK != result) - return result; - - /* We always support persistent connections on smtp */ + /* We always support persistent connections in SMTP */ conn->bits.close = FALSE; - pp->response_time = RESP_TIMEOUT; /* set default response time-out */ - pp->statemach_act = smtp_statemach_act; - pp->endofresp = smtp_endofresp; - pp->conn = conn; - - /* Initialise the response reader stuff */ - Curl_pp_init(pp); - /* Set the default response time-out */ pp->response_time = RESP_TIMEOUT; pp->statemach_act = smtp_statemach_act; pp->endofresp = smtp_endofresp; pp->conn = conn; - /* Calculate the path if necessary */ - if(!*path) { - if(!Curl_gethostname(localhost, sizeof(localhost))) - path = localhost; - else - path = "localhost"; - } + /* Set the default preferred authentication mechanism */ + smtpc->prefmech = SASL_AUTH_ANY; - /* URL decode the path and use it as the domain in our EHLO */ - result = Curl_urldecode(conn->data, path, 0, &smtpc->domain, NULL, TRUE); + /* Initialise the pingpong layer */ + Curl_pp_init(pp); + + /* Parse the URL options */ + result = smtp_parse_url_options(conn); + if(result) + return result; + + /* Parse the URL path */ + result = smtp_parse_url_path(conn); if(result) return result; @@ -1359,19 +1500,21 @@ static CURLcode smtp_connect(struct connectdata *conn, bool *done) static CURLcode smtp_done(struct connectdata *conn, CURLcode status, bool premature) { - struct SessionHandle *data = conn->data; - struct FTP *smtp = data->state.proto.smtp; CURLcode result = CURLE_OK; + struct SessionHandle *data = conn->data; + struct SMTP *smtp = data->req.protop; + struct pingpong *pp = &conn->proto.smtpc.pp; + const char *eob; + ssize_t len; ssize_t bytes_written; (void)premature; if(!smtp) - /* When the easy handle is removed from the multi while libcurl is still - * trying to resolve the host name, it seems that the smtp struct is not - * yet initialized, but the removal action calls Curl_done() which calls - * this function. So we simply return success if no smtp pointer is set. - */ + /* When the easy handle is removed from the multi interface while libcurl + is still trying to resolve the host name, the SMTP struct is not yet + initialized. However, the removal action calls Curl_done() which in + turn calls this function, so we simply return success. */ return CURLE_OK; if(status) { @@ -1379,25 +1522,27 @@ static CURLcode smtp_done(struct connectdata *conn, CURLcode status, result = status; /* use the already set error code */ } else if(!data->set.connect_only) { - struct smtp_conn *smtpc = &conn->proto.smtpc; - struct pingpong *pp = &smtpc->pp; + /* Calculate the EOB taking into account any terminating CRLF from the + previous line of the email or the CRLF of the DATA command when there + is "no mail data". RFC-5321, sect. 4.1.1.4. */ + eob = SMTP_EOB; + len = SMTP_EOB_LEN; + if(smtp->trailing_crlf || !conn->data->set.infilesize) { + eob += 2; + len -= 2; + } /* Send the end of block data */ - result = Curl_write(conn, - conn->writesockfd, /* socket to send to */ - SMTP_EOB, /* buffer pointer */ - SMTP_EOB_LEN, /* buffer size */ - &bytes_written); /* actually sent away */ - + result = Curl_write(conn, conn->writesockfd, eob, len, &bytes_written); if(result) return result; - if(bytes_written != SMTP_EOB_LEN) { + if(bytes_written != len) { /* The whole chunk was not sent so keep it around and adjust the pingpong structure accordingly */ - pp->sendthis = strdup(SMTP_EOB); - pp->sendsize = SMTP_EOB_LEN; - pp->sendleft = SMTP_EOB_LEN - bytes_written; + pp->sendthis = strdup(eob); + pp->sendsize = len; + pp->sendleft = len - bytes_written; } else /* Successfully sent so adjust the response timeout relative to now */ @@ -1412,10 +1557,10 @@ static CURLcode smtp_done(struct connectdata *conn, CURLcode status, non-blocking DONE operations, not in the multi state machine and with Curl_done() invokes on several places in the code! */ - result = smtp_easy_statemach(conn); + result = smtp_block_statemach(conn); } - /* Clear the transfer mode for the next connection */ + /* Clear the transfer mode for the next request */ smtp->transfer = FTPTRANSFER_BODY; return result; @@ -1425,8 +1570,8 @@ static CURLcode smtp_done(struct connectdata *conn, CURLcode status, * * smtp_perform() * - * This is the actual DO function for SMTP. Get a file/directory according to - * the options previously setup. + * This is the actual DO function for SMTP. Send a mail according to the + * options previously setup. */ static CURLcode smtp_perform(struct connectdata *conn, bool *connected, bool *dophase_done) @@ -1438,14 +1583,14 @@ static CURLcode smtp_perform(struct connectdata *conn, bool *connected, if(conn->data->set.opt_no_body) { /* Requested no body means no transfer */ - struct FTP *smtp = conn->data->state.proto.smtp; + struct SMTP *smtp = conn->data->req.protop; smtp->transfer = FTPTRANSFER_INFO; } *dophase_done = FALSE; /* not done yet */ /* Start the first command in the DO phase */ - result = smtp_mail(conn); + result = smtp_perform_mail(conn); if(result) return result; @@ -1471,45 +1616,11 @@ static CURLcode smtp_perform(struct connectdata *conn, bool *connected, */ static CURLcode smtp_do(struct connectdata *conn, bool *done) { - CURLcode retcode = CURLE_OK; - - *done = FALSE; /* default to false */ - - /* - Since connections can be re-used between SessionHandles, this might be a - connection already existing but on a fresh SessionHandle struct so we must - make sure we have a good 'struct SMTP' to play with. For new connections, - the struct SMTP is allocated and setup in the smtp_connect() function. - */ - Curl_reset_reqproto(conn); - retcode = smtp_init(conn); - if(retcode) - return retcode; - - retcode = smtp_regular_transfer(conn, done); - - return retcode; -} - -/*********************************************************************** - * - * smtp_quit() - * - * This should be called before calling sclose(). We should then wait for the - * response from the server before returning. The calling code should then try - * to close the connection. - */ -static CURLcode smtp_quit(struct connectdata *conn) -{ CURLcode result = CURLE_OK; - result = Curl_pp_sendf(&conn->proto.smtpc.pp, "QUIT"); - if(result) - return result; - - state(conn, SMTP_QUIT); + *done = FALSE; /* default to false */ - result = smtp_easy_statemach(conn); + result = smtp_regular_transfer(conn, done); return result; } @@ -1528,12 +1639,13 @@ static CURLcode smtp_disconnect(struct connectdata *conn, /* We cannot send quit unconditionally. If this connection is stale or bad in any way, sending quit and waiting around here will make the - disconnect wait in vain and cause more problems than we need to */ + disconnect wait in vain and cause more problems than we need to. */ /* The SMTP session may or may not have been allocated/setup at this point! */ if(!dead_connection && smtpc->pp.conn) - (void)smtp_quit(conn); /* ignore errors on the LOGOUT */ + if(!smtp_perform_quit(conn)) + (void)smtp_block_statemach(conn); /* ignore errors on QUIT */ /* Disconnect from the server */ Curl_pp_disconnect(&smtpc->pp); @@ -1550,7 +1662,7 @@ static CURLcode smtp_disconnect(struct connectdata *conn, /* Call this when the DO phase has completed */ static CURLcode smtp_dophase_done(struct connectdata *conn, bool connected) { - struct FTP *smtp = conn->data->state.proto.smtp; + struct SMTP *smtp = conn->data->req.protop; (void)connected; @@ -1568,12 +1680,10 @@ static CURLcode smtp_doing(struct connectdata *conn, bool *dophase_done) if(result) DEBUGF(infof(conn->data, "DO phase failed\n")); - else { - if(*dophase_done) { - result = smtp_dophase_done(conn, FALSE /* not connected */); + else if(*dophase_done) { + result = smtp_dophase_done(conn, FALSE /* not connected */); - DEBUGF(infof(conn->data, "DO phase is complete\n")); - } + DEBUGF(infof(conn->data, "DO phase is complete\n")); } return result; @@ -1598,20 +1708,18 @@ static CURLcode smtp_regular_transfer(struct connectdata *conn, /* Make sure size is unknown at this point */ data->req.size = -1; + /* Set the progress data */ Curl_pgrsSetUploadCounter(data, 0); Curl_pgrsSetDownloadCounter(data, 0); Curl_pgrsSetUploadSize(data, 0); Curl_pgrsSetDownloadSize(data, 0); + /* Carry out the perform */ result = smtp_perform(conn, &connected, dophase_done); - if(CURLE_OK == result) { - if(!*dophase_done) - /* The DO phase has not completed yet */ - return CURLE_OK; - + /* Perform post DO phase operations if necessary */ + if(!result && *dophase_done) result = smtp_dophase_done(conn, connected); - } return result; } @@ -1619,9 +1727,10 @@ static CURLcode smtp_regular_transfer(struct connectdata *conn, static CURLcode smtp_setup_connection(struct connectdata *conn) { struct SessionHandle *data = conn->data; + CURLcode result; if(conn->bits.httpproxy && !data->set.tunnel_thru_httpproxy) { - /* Unless we have asked to tunnel smtp operations through the proxy, we + /* Unless we have asked to tunnel SMTP operations through the proxy, we switch and use HTTP operations only */ #ifndef CURL_DISABLE_HTTP if(conn->handler == &Curl_handler_smtp) @@ -1634,34 +1743,111 @@ static CURLcode smtp_setup_connection(struct connectdata *conn) return CURLE_UNSUPPORTED_PROTOCOL; #endif } + /* set it up as a HTTP connection instead */ + return conn->handler->setup_connection(conn); - /* We explicitly mark this connection as persistent here as we're doing - SMTP over HTTP and thus we accidentally avoid setting this value - otherwise */ - conn->bits.close = FALSE; #else failf(data, "SMTP over http proxy requires HTTP support built-in!"); return CURLE_UNSUPPORTED_PROTOCOL; #endif } + /* Initialise the SMTP layer */ + result = smtp_init(conn); + if(result) + return result; + data->state.path++; /* don't include the initial slash */ return CURLE_OK; } +/*********************************************************************** + * + * smtp_parse_url_options() + * + * Parse the URL login options. + */ +static CURLcode smtp_parse_url_options(struct connectdata *conn) +{ + CURLcode result = CURLE_OK; + struct smtp_conn *smtpc = &conn->proto.smtpc; + const char *options = conn->options; + const char *ptr = options; + + if(options) { + const char *key = ptr; + + while(*ptr && *ptr != '=') + ptr++; + + if(strnequal(key, "AUTH", 4)) { + const char *value = ptr + 1; + + if(strequal(value, "*")) + smtpc->prefmech = SASL_AUTH_ANY; + else if(strequal(value, SASL_MECH_STRING_LOGIN)) + smtpc->prefmech = SASL_MECH_LOGIN; + else if(strequal(value, SASL_MECH_STRING_PLAIN)) + smtpc->prefmech = SASL_MECH_PLAIN; + else if(strequal(value, SASL_MECH_STRING_CRAM_MD5)) + smtpc->prefmech = SASL_MECH_CRAM_MD5; + else if(strequal(value, SASL_MECH_STRING_DIGEST_MD5)) + smtpc->prefmech = SASL_MECH_DIGEST_MD5; + else if(strequal(value, SASL_MECH_STRING_GSSAPI)) + smtpc->prefmech = SASL_MECH_GSSAPI; + else if(strequal(value, SASL_MECH_STRING_NTLM)) + smtpc->prefmech = SASL_MECH_NTLM; + else if(strequal(value, SASL_MECH_STRING_XOAUTH2)) + smtpc->prefmech = SASL_MECH_XOAUTH2; + else + smtpc->prefmech = SASL_AUTH_NONE; + } + else + result = CURLE_URL_MALFORMAT; + } + + return result; +} + +/*********************************************************************** + * + * smtp_parse_url_path() + * + * Parse the URL path into separate path components. + */ +static CURLcode smtp_parse_url_path(struct connectdata *conn) +{ + /* The SMTP struct is already initialised in smtp_connect() */ + struct SessionHandle *data = conn->data; + struct smtp_conn *smtpc = &conn->proto.smtpc; + const char *path = data->state.path; + char localhost[HOSTNAME_MAX + 1]; + + /* Calculate the path if necessary */ + if(!*path) { + if(!Curl_gethostname(localhost, sizeof(localhost))) + path = localhost; + else + path = "localhost"; + } + + /* URL decode the path and use it as the domain in our EHLO */ + return Curl_urldecode(conn->data, path, 0, &smtpc->domain, NULL, TRUE); +} + CURLcode Curl_smtp_escape_eob(struct connectdata *conn, ssize_t nread) { /* When sending a SMTP payload we must detect CRLF. sequences making sure they are sent as CRLF.. instead, as a . on the beginning of a line will be deleted by the server when not part of an EOB terminator and a genuine CRLF.CRLF which isn't escaped will wrongly be detected as end of - data by the server. + data by the server */ ssize_t i; ssize_t si; - struct smtp_conn *smtpc = &conn->proto.smtpc; struct SessionHandle *data = conn->data; + struct SMTP *smtp = data->req.protop; /* Do we need to allocate the scatch buffer? */ if(!data->state.scratch) { @@ -1676,36 +1862,46 @@ CURLcode Curl_smtp_escape_eob(struct connectdata *conn, ssize_t nread) /* This loop can be improved by some kind of Boyer-Moore style of approach but that is saved for later... */ for(i = 0, si = 0; i < nread; i++) { - if(SMTP_EOB[smtpc->eob] == data->req.upload_fromhere[i]) - smtpc->eob++; - else if(smtpc->eob) { + if(SMTP_EOB[smtp->eob] == data->req.upload_fromhere[i]) { + smtp->eob++; + + /* Is the EOB potentially the terminating CRLF? */ + if(2 == smtp->eob || SMTP_EOB_LEN == smtp->eob) + smtp->trailing_crlf = TRUE; + else + smtp->trailing_crlf = FALSE; + } + else if(smtp->eob) { /* A previous substring matched so output that first */ - memcpy(&data->state.scratch[si], SMTP_EOB, smtpc->eob); - si += smtpc->eob; + memcpy(&data->state.scratch[si], SMTP_EOB, smtp->eob); + si += smtp->eob; /* Then compare the first byte */ if(SMTP_EOB[0] == data->req.upload_fromhere[i]) - smtpc->eob = 1; + smtp->eob = 1; else - smtpc->eob = 0; + smtp->eob = 0; + + /* Reset the trailing CRLF flag as there was more data */ + smtp->trailing_crlf = FALSE; } - /* Do we have a match for CRLF. as per RFC-2821, sect. 4.5.2 */ - if(SMTP_EOB_FIND_LEN == smtpc->eob) { + /* Do we have a match for CRLF. as per RFC-5321, sect. 4.5.2 */ + if(SMTP_EOB_FIND_LEN == smtp->eob) { /* Copy the replacement data to the target buffer */ memcpy(&data->state.scratch[si], SMTP_EOB_REPL, SMTP_EOB_REPL_LEN); si += SMTP_EOB_REPL_LEN; - smtpc->eob = 0; + smtp->eob = 0; } - else if(!smtpc->eob) + else if(!smtp->eob) data->state.scratch[si++] = data->req.upload_fromhere[i]; } - if(smtpc->eob) { + if(smtp->eob) { /* A substring matched before processing ended so output that now */ - memcpy(&data->state.scratch[si], SMTP_EOB, smtpc->eob); - si += smtpc->eob; - smtpc->eob = 0; + memcpy(&data->state.scratch[si], SMTP_EOB, smtp->eob); + si += smtp->eob; + smtp->eob = 0; } if(si != nread) { diff --git a/plugins/FTPFileYM/curl/lib/smtp.h b/plugins/FTPFileYM/curl/lib/smtp.h index d926795f5f..14429a5e7e 100644 --- a/plugins/FTPFileYM/curl/lib/smtp.h +++ b/plugins/FTPFileYM/curl/lib/smtp.h @@ -38,13 +38,14 @@ typedef enum { (multi mode only) */ SMTP_AUTH_PLAIN, SMTP_AUTH_LOGIN, - SMTP_AUTH_PASSWD, + SMTP_AUTH_LOGIN_PASSWD, SMTP_AUTH_CRAMMD5, SMTP_AUTH_DIGESTMD5, SMTP_AUTH_DIGESTMD5_RESP, SMTP_AUTH_NTLM, SMTP_AUTH_NTLM_TYPE2MSG, - SMTP_AUTH, + SMTP_AUTH_XOAUTH2, + SMTP_AUTH_FINAL, SMTP_MAIL, /* MAIL FROM */ SMTP_RCPT, /* RCPT TO */ SMTP_DATA, @@ -53,18 +54,29 @@ typedef enum { SMTP_LAST /* never used */ } smtpstate; +/* This SMTP struct is used in the SessionHandle. All SMTP data that is + connection-oriented must be in smtp_conn to properly deal with the fact that + perhaps the SessionHandle is changed between the times the connection is + used. */ +struct SMTP { + curl_pp_transfer transfer; + struct curl_slist *rcpt; /* Recipient list */ + size_t eob; /* Number of bytes of the EOB (End Of Body) that + have been received so far */ + bool trailing_crlf; /* Specifies if the tailing CRLF is present */ +}; + /* smtp_conn is used for struct connection-oriented data in the connectdata struct */ struct smtp_conn { struct pingpong pp; + smtpstate state; /* Always use smtp.c:state() to change state! */ + bool ssldone; /* Is connect() over SSL done? */ char *domain; /* Client address/name to send in the EHLO */ - size_t eob; /* Number of bytes of the EOB (End Of Body) that - have been received so far */ unsigned int authmechs; /* Accepted authentication mechanisms */ + unsigned int prefmech; /* Preferred authentication mechanism */ unsigned int authused; /* Auth mechanism used for the connection */ - smtpstate state; /* Always use smtp.c:state() to change state! */ - struct curl_slist *rcpt; /* Recipient list */ - bool ssldone; /* Is connect() over SSL done? */ + bool tls_supported; /* StartTLS capability supported by server */ bool size_supported; /* If server supports SIZE extension according to RFC 1870 */ }; diff --git a/plugins/FTPFileYM/curl/lib/socks.c b/plugins/FTPFileYM/curl/lib/socks.c index 51bb946643..b101a0de48 100644 --- a/plugins/FTPFileYM/curl/lib/socks.c +++ b/plugins/FTPFileYM/curl/lib/socks.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2012, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2013, 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 @@ -196,8 +196,15 @@ CURLcode Curl_SOCKS4(const char *proxy_name, * This is currently not supporting "Identification Protocol (RFC1413)". */ socksreq[8] = 0; /* ensure empty userid is NUL-terminated */ - if(proxy_name) - strlcat((char*)socksreq + 8, proxy_name, sizeof(socksreq) - 8); + if(proxy_name) { + size_t plen = strlen(proxy_name); + if(plen >= sizeof(socksreq) - 8) { + failf(data, "Too long SOCKS proxy name, can't use!\n"); + return CURLE_COULDNT_CONNECT; + } + /* copy the proxy name WITH trailing zero */ + memcpy(socksreq + 8, proxy_name, plen+1); + } /* * Make connection diff --git a/plugins/FTPFileYM/curl/lib/socks_sspi.c b/plugins/FTPFileYM/curl/lib/socks_sspi.c index b4b23e0149..0313de3334 100644 --- a/plugins/FTPFileYM/curl/lib/socks_sspi.c +++ b/plugins/FTPFileYM/curl/lib/socks_sspi.c @@ -6,7 +6,7 @@ * \___|\___/|_| \_\_____| * * Copyright (C) 2009, 2011, Markus Moeller, <markus_moeller@compuserve.com> - * Copyright (C) 2012, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 2012 - 2013, 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 @@ -260,8 +260,8 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex, /* ignore the first (VER) byte */ if(socksreq[1] == 255) { /* status / message type */ - failf(data, "User was rejected by the SOCKS5 server (%d %d).", - socksreq[0], socksreq[1]); + failf(data, "User was rejected by the SOCKS5 server (%u %u).", + (unsigned int)socksreq[0], (unsigned int)socksreq[1]); Curl_safefree(service_name); s_pSecFn->FreeCredentialsHandle(&cred_handle); s_pSecFn->DeleteSecurityContext(&sspi_context); @@ -269,8 +269,8 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex, } if(socksreq[1] != 1) { /* status / messgae type */ - failf(data, "Invalid SSPI authentication response type (%d %d).", - socksreq[0], socksreq[1]); + failf(data, "Invalid SSPI authentication response type (%u %u).", + (unsigned int)socksreq[0], (unsigned int)socksreq[1]); Curl_safefree(service_name); s_pSecFn->FreeCredentialsHandle(&cred_handle); s_pSecFn->DeleteSecurityContext(&sspi_context); @@ -494,15 +494,15 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex, /* ignore the first (VER) byte */ if(socksreq[1] == 255) { /* status / message type */ - failf(data, "User was rejected by the SOCKS5 server (%d %d).", - socksreq[0], socksreq[1]); + failf(data, "User was rejected by the SOCKS5 server (%u %u).", + (unsigned int)socksreq[0], (unsigned int)socksreq[1]); s_pSecFn->DeleteSecurityContext(&sspi_context); return CURLE_COULDNT_CONNECT; } if(socksreq[1] != 2) { /* status / message type */ - failf(data, "Invalid SSPI encryption response type (%d %d).", - socksreq[0], socksreq[1]); + failf(data, "Invalid SSPI encryption response type (%u %u).", + (unsigned int)socksreq[0], (unsigned int)socksreq[1]); s_pSecFn->DeleteSecurityContext(&sspi_context); return CURLE_COULDNT_CONNECT; } @@ -549,8 +549,8 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex, } if(sspi_w_token[1].cbBuffer != 1) { - failf(data, "Invalid SSPI encryption response length (%d).", - sspi_w_token[1].cbBuffer); + failf(data, "Invalid SSPI encryption response length (%lu).", + (unsigned long)sspi_w_token[1].cbBuffer); s_pSecFn->FreeContextBuffer(sspi_w_token[0].pvBuffer); s_pSecFn->FreeContextBuffer(sspi_w_token[1].pvBuffer); s_pSecFn->DeleteSecurityContext(&sspi_context); @@ -563,8 +563,8 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex, } else { if(sspi_w_token[0].cbBuffer != 1) { - failf(data, "Invalid SSPI encryption response length (%d).", - sspi_w_token[0].cbBuffer); + failf(data, "Invalid SSPI encryption response length (%lu).", + (unsigned long)sspi_w_token[0].cbBuffer); s_pSecFn->FreeContextBuffer(sspi_w_token[0].pvBuffer); s_pSecFn->DeleteSecurityContext(&sspi_context); return CURLE_COULDNT_CONNECT; diff --git a/plugins/FTPFileYM/curl/lib/ssh.c b/plugins/FTPFileYM/curl/lib/ssh.c index 0c7d36ecc9..c213225db4 100644 --- a/plugins/FTPFileYM/curl/lib/ssh.c +++ b/plugins/FTPFileYM/curl/lib/ssh.c @@ -149,13 +149,15 @@ static int ssh_perform_getsock(const struct connectdata *conn, number of sockets */ int numsocks); +static CURLcode ssh_setup_connection(struct connectdata *conn); + /* * SCP protocol handler. */ const struct Curl_handler Curl_handler_scp = { "SCP", /* scheme */ - ZERO_NULL, /* setup_connection */ + ssh_setup_connection, /* setup_connection */ ssh_do, /* do_it */ scp_done, /* done */ ZERO_NULL, /* do_more */ @@ -181,7 +183,7 @@ const struct Curl_handler Curl_handler_scp = { const struct Curl_handler Curl_handler_sftp = { "SFTP", /* scheme */ - ZERO_NULL, /* setup_connection */ + ssh_setup_connection, /* setup_connection */ ssh_do, /* do_it */ sftp_done, /* done */ ZERO_NULL, /* do_more */ @@ -200,7 +202,6 @@ const struct Curl_handler Curl_handler_sftp = { | PROTOPT_NOURLQUERY /* flags */ }; - static void kbd_callback(const char *name, int name_len, const char *instruction, int instruction_len, int num_prompts, @@ -392,7 +393,7 @@ static void state(struct connectdata *conn, sshstate nowstate) #if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS) if(sshc->state != nowstate) { infof(conn->data, "SFTP %p state change from %s to %s\n", - sshc, names[sshc->state], names[nowstate]); + (void *)sshc, names[sshc->state], names[nowstate]); } #endif @@ -687,7 +688,7 @@ static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block) { CURLcode result = CURLE_OK; struct SessionHandle *data = conn->data; - struct SSHPROTO *sftp_scp = data->state.proto.ssh; + struct SSHPROTO *sftp_scp = data->req.protop; struct ssh_conn *sshc = &conn->proto.sshc; curl_socket_t sock = conn->sock[FIRSTSOCKET]; char *new_readdir_line; @@ -2287,6 +2288,10 @@ static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block) sshc->actualcode = result; } else { + /* store this original bitmask setup to use later on if we can't + figure out a "real" bitmask */ + sshc->orig_waitfor = data->req.keepon; + /* we want to use the _sending_ function even when the socket turns out readable as the underlying libssh2 scp send function will deal with both accordingly */ @@ -2603,9 +2608,7 @@ static void ssh_block2waitfor(struct connectdata *conn, bool block) { struct ssh_conn *sshc = &conn->proto.sshc; int dir; - if(!block) - conn->waitfor = 0; - else if((dir = libssh2_session_block_directions(sshc->ssh_session))) { + if(block && (dir = libssh2_session_block_directions(sshc->ssh_session))) { /* translate the libssh2 define bits into our own bit defines */ conn->waitfor = ((dir&LIBSSH2_SESSION_BLOCK_INBOUND)?KEEP_RECV:0) | ((dir&LIBSSH2_SESSION_BLOCK_OUTBOUND)?KEEP_SEND:0); @@ -2635,7 +2638,7 @@ static CURLcode ssh_multi_statemach(struct connectdata *conn, bool *done) return result; } -static CURLcode ssh_easy_statemach(struct connectdata *conn, +static CURLcode ssh_block_statemach(struct connectdata *conn, bool duringconnect) { struct ssh_conn *sshc = &conn->proto.sshc; @@ -2689,25 +2692,14 @@ static CURLcode ssh_easy_statemach(struct connectdata *conn, /* * SSH setup and connection */ -static CURLcode ssh_init(struct connectdata *conn) +static CURLcode ssh_setup_connection(struct connectdata *conn) { - struct SessionHandle *data = conn->data; struct SSHPROTO *ssh; - struct ssh_conn *sshc = &conn->proto.sshc; - - sshc->actualcode = CURLE_OK; /* reset error code */ - sshc->secondCreateDirs =0; /* reset the create dir attempt state - variable */ - - if(data->state.proto.ssh) - return CURLE_OK; - ssh = calloc(1, sizeof(struct SSHPROTO)); + conn->data->req.protop = ssh = calloc(1, sizeof(struct SSHPROTO)); if(!ssh) return CURLE_OUT_OF_MEMORY; - data->state.proto.ssh = ssh; - return CURLE_OK; } @@ -2731,14 +2723,6 @@ static CURLcode ssh_connect(struct connectdata *conn, bool *done) function to make the re-use checks properly be able to check this bit. */ conn->bits.close = FALSE; - /* If there already is a protocol-specific struct allocated for this - sessionhandle, deal with it */ - Curl_reset_reqproto(conn); - - result = ssh_init(conn); - if(result) - return result; - if(conn->handler->protocol & CURLPROTO_SCP) { conn->recv[FIRSTSOCKET] = scp_recv; conn->send[FIRSTSOCKET] = scp_send; @@ -2856,23 +2840,16 @@ static CURLcode ssh_do(struct connectdata *conn, bool *done) CURLcode res; bool connected = 0; struct SessionHandle *data = conn->data; + struct ssh_conn *sshc = &conn->proto.sshc; *done = FALSE; /* default to false */ - /* - Since connections can be re-used between SessionHandles, this might be a - connection already existing but on a fresh SessionHandle struct so we must - make sure we have a good 'struct SSHPROTO' to play with. For new - connections, the struct SSHPROTO is allocated and setup in the - ssh_connect() function. - */ - Curl_reset_reqproto(conn); - res = ssh_init(conn); - if(res) - return res; - data->req.size = -1; /* make sure this is unknown at this point */ + sshc->actualcode = CURLE_OK; /* reset error code */ + sshc->secondCreateDirs =0; /* reset the create dir attempt state + variable */ + Curl_pgrsSetUploadCounter(data, 0); Curl_pgrsSetDownloadCounter(data, 0); Curl_pgrsSetUploadSize(data, 0); @@ -2895,14 +2872,14 @@ static CURLcode scp_disconnect(struct connectdata *conn, bool dead_connection) struct ssh_conn *ssh = &conn->proto.sshc; (void) dead_connection; - Curl_safefree(conn->data->state.proto.ssh); + Curl_safefree(conn->data->req.protop); if(ssh->ssh_session) { /* only if there's a session still around to use! */ state(conn, SSH_SESSION_DISCONNECT); - result = ssh_easy_statemach(conn, FALSE); + result = ssh_block_statemach(conn, FALSE); } return result; @@ -2913,7 +2890,7 @@ static CURLcode scp_disconnect(struct connectdata *conn, bool dead_connection) static CURLcode ssh_done(struct connectdata *conn, CURLcode status) { CURLcode result = CURLE_OK; - struct SSHPROTO *sftp_scp = conn->data->state.proto.ssh; + struct SSHPROTO *sftp_scp = conn->data->req.protop; if(status == CURLE_OK) { /* run the state-machine @@ -2923,7 +2900,7 @@ static CURLcode ssh_done(struct connectdata *conn, CURLcode status) non-blocking DONE operations, not in the multi state machine and with Curl_done() invokes on several places in the code! */ - result = ssh_easy_statemach(conn, FALSE); + result = ssh_block_statemach(conn, FALSE); } else result = status; @@ -3060,12 +3037,12 @@ static CURLcode sftp_disconnect(struct connectdata *conn, bool dead_connection) DEBUGF(infof(conn->data, "SSH DISCONNECT starts now\n")); - Curl_safefree(conn->data->state.proto.ssh); + Curl_safefree(conn->data->req.protop); if(conn->proto.sshc.ssh_session) { /* only if there's a session still around to use! */ state(conn, SSH_SFTP_SHUTDOWN); - result = ssh_easy_statemach(conn, FALSE); + result = ssh_block_statemach(conn, FALSE); } DEBUGF(infof(conn->data, "SSH DISCONNECT is done\n")); diff --git a/plugins/FTPFileYM/curl/lib/sslgen.c b/plugins/FTPFileYM/curl/lib/sslgen.c index 48758742a2..d2d0e303ec 100644 --- a/plugins/FTPFileYM/curl/lib/sslgen.c +++ b/plugins/FTPFileYM/curl/lib/sslgen.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2012, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2013, 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 @@ -31,6 +31,8 @@ Curl_ossl_ - prefix for OpenSSL ones Curl_gtls_ - prefix for GnuTLS ones Curl_nss_ - prefix for NSS ones + Curl_qssl_ - prefix for QsoSSL ones + Curl_gskit_ - prefix for GSKit ones Curl_polarssl_ - prefix for PolarSSL ones Curl_cyassl_ - prefix for CyaSSL ones Curl_schannel_ - prefix for Schannel SSPI ones @@ -45,6 +47,16 @@ #include "curl_setup.h" +#ifdef HAVE_SYS_TYPES_H +#include <sys/types.h> +#endif +#ifdef HAVE_SYS_STAT_H +#include <sys/stat.h> +#endif +#ifdef HAVE_FCNTL_H +#include <fcntl.h> +#endif + #include "urldata.h" #define SSLGEN_C #include "sslgen.h" /* generic SSL protos etc */ @@ -52,17 +64,24 @@ #include "gtls.h" /* GnuTLS versions */ #include "nssg.h" /* NSS versions */ #include "qssl.h" /* QSOSSL versions */ +#include "gskit.h" /* Global Secure ToolKit versions */ #include "polarssl.h" /* PolarSSL versions */ #include "axtls.h" /* axTLS versions */ #include "cyassl.h" /* CyaSSL versions */ #include "curl_schannel.h" /* Schannel SSPI version */ #include "curl_darwinssl.h" /* SecureTransport (Darwin) version */ +#include "slist.h" #include "sendf.h" #include "rawstr.h" #include "url.h" #include "curl_memory.h" #include "progress.h" #include "share.h" +#include "timeval.h" + +#define _MPRINTF_REPLACE /* use our functions only */ +#include <curl/mprintf.h> + /* The last #include file should be: */ #include "memdebug.h" @@ -159,6 +178,62 @@ void Curl_free_ssl_config(struct ssl_config_data* sslc) Curl_safefree(sslc->random_file); } + +/* + * Curl_rand() returns a random unsigned integer, 32bit. + * + * This non-SSL function is put here only because this file is the only one + * with knowledge of what the underlying SSL libraries provide in terms of + * randomizers. + * + * NOTE: 'data' may be passed in as NULL when coming from external API without + * easy handle! + * + */ + +unsigned int Curl_rand(struct SessionHandle *data) +{ + unsigned int r; + static unsigned int randseed; + static bool seeded = FALSE; + +#ifndef have_curlssl_random + (void)data; +#else + if(data) { + Curl_ssl_random(data, (unsigned char *)&r, sizeof(r)); + return r; + } +#endif + +#ifdef RANDOM_FILE + if(!seeded) { + /* if there's a random file to read a seed from, use it */ + int fd = open(RANDOM_FILE, O_RDONLY); + if(fd > -1) { + /* read random data into the randseed variable */ + ssize_t nread = read(fd, &randseed, sizeof(randseed)); + if(nread == sizeof(randseed)) + seeded = TRUE; + close(fd); + } + } +#endif + + if(!seeded) { + struct timeval now = curlx_tvnow(); + randseed += (unsigned int)now.tv_usec + (unsigned int)now.tv_sec; + randseed = randseed * 1103515245 + 12345; + randseed = randseed * 1103515245 + 12345; + randseed = randseed * 1103515245 + 12345; + seeded = TRUE; + } + + /* Return an unsigned 32-bit pseudo-random number. */ + r = randseed = randseed * 1103515245 + 12345; + return (r << 16) | ((r >> 16) & 0xFFFF); +} + #ifdef USE_SSL /* "global" init done? */ @@ -518,17 +593,77 @@ void Curl_ssl_free_certinfo(struct SessionHandle *data) } } -#if defined(USE_SSLEAY) || defined(USE_GNUTLS) || defined(USE_NSS) || \ - defined(USE_DARWINSSL) -/* these functions are only used by some SSL backends */ +int Curl_ssl_init_certinfo(struct SessionHandle * data, + int num) +{ + struct curl_certinfo * ci = &data->info.certs; + struct curl_slist * * table; + + /* Initialize the certificate information structures. Return 0 if OK, else 1. + */ + Curl_ssl_free_certinfo(data); + ci->num_of_certs = num; + table = calloc((size_t) num, sizeof(struct curl_slist *)); + if(!table) + return 1; + + ci->certinfo = table; + return 0; +} + +CURLcode Curl_ssl_push_certinfo_len(struct SessionHandle *data, + int certnum, + const char *label, + const char *value, + size_t valuelen) +{ + struct curl_certinfo * ci = &data->info.certs; + char * output; + struct curl_slist * nl; + CURLcode res = CURLE_OK; + + /* Add an information record for a particular certificate. */ + output = curl_maprintf("%s:%.*s", label, valuelen, value); + if(!output) + return CURLE_OUT_OF_MEMORY; + + nl = Curl_slist_append_nodup(ci->certinfo[certnum], output); + if(!nl) { + free(output); + curl_slist_free_all(ci->certinfo[certnum]); + res = CURLE_OUT_OF_MEMORY; + } + + ci->certinfo[certnum] = nl; + return res; +} + +/* + * This is a convenience function for push_certinfo_len that takes a zero + * terminated value. + */ +CURLcode Curl_ssl_push_certinfo(struct SessionHandle *data, + int certnum, + const char *label, + const char *value) +{ + size_t valuelen = strlen(value); + + return Curl_ssl_push_certinfo_len(data, certnum, label, value, valuelen); +} + +/* these functions are only provided by some SSL backends */ +#ifdef have_curlssl_random void Curl_ssl_random(struct SessionHandle *data, unsigned char *entropy, size_t length) { curlssl_random(data, entropy, length); } +#endif +#ifdef have_curlssl_md5sum void Curl_ssl_md5sum(unsigned char *tmp, /* input */ size_t tmplen, unsigned char *md5sum, /* output */ @@ -536,6 +671,6 @@ void Curl_ssl_md5sum(unsigned char *tmp, /* input */ { curlssl_md5sum(tmp, tmplen, md5sum, md5len); } -#endif /* USE_SSLEAY || USE_GNUTLS || USE_NSS || USE_DARWINSSL */ +#endif #endif /* USE_SSL */ diff --git a/plugins/FTPFileYM/curl/lib/sslgen.h b/plugins/FTPFileYM/curl/lib/sslgen.h index 17ad8e343d..c7f5f0092d 100644 --- a/plugins/FTPFileYM/curl/lib/sslgen.h +++ b/plugins/FTPFileYM/curl/lib/sslgen.h @@ -33,6 +33,8 @@ bool Curl_clone_ssl_config(struct ssl_config_data* source, struct ssl_config_data* dest); void Curl_free_ssl_config(struct ssl_config_data* sslc); +unsigned int Curl_rand(struct SessionHandle *); + #ifdef USE_SSL int Curl_ssl_init(void); void Curl_ssl_cleanup(void); @@ -56,7 +58,16 @@ size_t Curl_ssl_version(char *buffer, size_t size); bool Curl_ssl_data_pending(const struct connectdata *conn, int connindex); int Curl_ssl_check_cxn(struct connectdata *conn); + +/* Certificate information list handling. */ + void Curl_ssl_free_certinfo(struct SessionHandle *data); +int Curl_ssl_init_certinfo(struct SessionHandle * data, int num); +CURLcode Curl_ssl_push_certinfo_len(struct SessionHandle * data, int certnum, + const char * label, const char * value, + size_t valuelen); +CURLcode Curl_ssl_push_certinfo(struct SessionHandle * data, int certnum, + const char * label, const char * value); /* Functions to be used by SSL library adaptation functions */ @@ -83,6 +94,13 @@ void Curl_ssl_md5sum(unsigned char *tmp, /* input */ #define SSL_SHUTDOWN_TIMEOUT 10000 /* ms */ +#ifdef have_curlssl_random +#define HAVE_CURL_SSL_RANDOM +#endif +#ifdef have_curlssl_md5sum +#define HAVE_CURL_SSL_MD5SUM +#endif + #else /* When SSL support is not present, just define away these function calls */ #define Curl_ssl_init() 1 diff --git a/plugins/FTPFileYM/curl/lib/ssluse.c b/plugins/FTPFileYM/curl/lib/ssluse.c index 4a0dba734d..c747420f60 100644 --- a/plugins/FTPFileYM/curl/lib/ssluse.c +++ b/plugins/FTPFileYM/curl/lib/ssluse.c @@ -43,6 +43,7 @@ #include "inet_pton.h" #include "ssluse.h" #include "connect.h" +#include "slist.h" #include "strequal.h" #include "select.h" #include "sslgen.h" @@ -236,27 +237,12 @@ static int ossl_seed(struct SessionHandle *data) /* If we get here, it means we need to seed the PRNG using a "silly" approach! */ - { - int len; - char *area; - - /* Changed call to RAND_seed to use the underlying RAND_add implementation - * directly. Do this in a loop, with the amount of additional entropy - * being dependent upon the algorithm used by Curl_FormBoundary(): N bytes - * of a 7-bit ascii set. -- Richard Gorton, March 11 2003. - */ - - do { - area = Curl_FormBoundary(); - if(!area) - return 3; /* out of memory */ - - len = curlx_uztosi(strlen(area)); - RAND_add(area, len, (len >> 1)); - - free(area); /* now remove the random junk */ - } while(!RAND_status()); - } + do { + unsigned char randb[64]; + int len = sizeof(randb); + RAND_bytes(randb, len); + RAND_add(randb, len, (len >> 1)); + } while(!RAND_status()); /* generates a default path for the random seed file */ buf[0]=0; /* blank it first */ @@ -308,6 +294,49 @@ static int do_file_type(const char *type) return -1; } +#if defined(HAVE_OPENSSL_ENGINE_H) && defined(HAVE_ENGINE_LOAD_FOUR_ARGS) +/* + * Supply default password to the engine user interface conversation. + * The password is passed by OpenSSL engine from ENGINE_load_private_key() + * last argument to the ui and can be obtained by UI_get0_user_data(ui) here. + */ +static int ssl_ui_reader(UI *ui, UI_STRING *uis) +{ + const char *password; + switch(UI_get_string_type(uis)) { + case UIT_PROMPT: + case UIT_VERIFY: + password = (const char*)UI_get0_user_data(ui); + if(NULL != password && + UI_get_input_flags(uis) & UI_INPUT_FLAG_DEFAULT_PWD) { + UI_set_result(ui, uis, password); + return 1; + } + default: + break; + } + return (UI_method_get_reader(UI_OpenSSL()))(ui, uis); +} + +/* + * Suppress interactive request for a default password if available. + */ +static int ssl_ui_writer(UI *ui, UI_STRING *uis) +{ + switch(UI_get_string_type(uis)) { + case UIT_PROMPT: + case UIT_VERIFY: + if(NULL != UI_get0_user_data(ui) && + UI_get_input_flags(uis) & UI_INPUT_FLAG_DEFAULT_PWD) { + return 1; + } + default: + break; + } + return (UI_method_get_writer(UI_OpenSSL()))(ui, uis); +} +#endif + static int cert_stuff(struct connectdata *conn, SSL_CTX* ctx, @@ -450,7 +479,7 @@ int cert_stuff(struct connectdata *conn, PKCS12_PBE_add(); if(!PKCS12_parse(p12, data->set.str[STRING_KEY_PASSWD], &pri, &x509, - &ca)) { + &ca)) { failf(data, "could not parse PKCS12 file, check password, OpenSSL error %s", ERR_error_string(ERR_get_error(), NULL) ); @@ -462,54 +491,53 @@ int cert_stuff(struct connectdata *conn, if(SSL_CTX_use_certificate(ctx, x509) != 1) { failf(data, SSL_CLIENT_CERT_ERR); - EVP_PKEY_free(pri); - X509_free(x509); - sk_X509_pop_free(ca, X509_free); - return 0; + goto fail; } if(SSL_CTX_use_PrivateKey(ctx, pri) != 1) { failf(data, "unable to use private key from PKCS12 file '%s'", cert_file); - EVP_PKEY_free(pri); - X509_free(x509); - sk_X509_pop_free(ca, X509_free); - return 0; + goto fail; } if(!SSL_CTX_check_private_key (ctx)) { failf(data, "private key from PKCS12 file '%s' " "does not match certificate in same file", cert_file); - EVP_PKEY_free(pri); - X509_free(x509); - sk_X509_pop_free(ca, X509_free); - return 0; + goto fail; } /* Set Certificate Verification chain */ if(ca && sk_X509_num(ca)) { for(i = 0; i < sk_X509_num(ca); i++) { - if(!SSL_CTX_add_extra_chain_cert(ctx,sk_X509_value(ca, i))) { + /* + * Note that sk_X509_pop() is used below to make sure the cert is + * removed from the stack properly before getting passed to + * SSL_CTX_add_extra_chain_cert(). Previously we used + * sk_X509_value() instead, but then we'd clean it in the subsequent + * sk_X509_pop_free() call. + */ + X509 *x = sk_X509_pop(ca); + if(!SSL_CTX_add_extra_chain_cert(ctx, x)) { failf(data, "cannot add certificate to certificate chain"); - EVP_PKEY_free(pri); - X509_free(x509); - sk_X509_pop_free(ca, X509_free); - return 0; + goto fail; } - if(!SSL_CTX_add_client_CA(ctx, sk_X509_value(ca, i))) { + /* SSL_CTX_add_client_CA() seems to work with either sk_* function, + * presumably because it duplicates what we pass to it. + */ + if(!SSL_CTX_add_client_CA(ctx, x)) { failf(data, "cannot add certificate to client CA list"); - EVP_PKEY_free(pri); - X509_free(x509); - sk_X509_pop_free(ca, X509_free); - return 0; + goto fail; } } } + cert_done = 1; + fail: EVP_PKEY_free(pri); X509_free(x509); sk_X509_pop_free(ca, X509_free); - cert_done = 1; - break; + + if(!cert_done) + return 0; /* failure! */ #else failf(data, "file type P12 for certificate not supported"); return 0; @@ -542,7 +570,16 @@ int cert_stuff(struct connectdata *conn, EVP_PKEY *priv_key = NULL; if(data->state.engine) { #ifdef HAVE_ENGINE_LOAD_FOUR_ARGS - UI_METHOD *ui_method = UI_OpenSSL(); + UI_METHOD *ui_method = + UI_create_method((char *)"cURL user interface"); + if(NULL == ui_method) { + failf(data, "unable do create OpenSSL user-interface method"); + return 0; + } + UI_method_set_opener(ui_method, UI_method_get_opener(UI_OpenSSL())); + UI_method_set_closer(ui_method, UI_method_get_closer(UI_OpenSSL())); + UI_method_set_reader(ui_method, ssl_ui_reader); + UI_method_set_writer(ui_method, ssl_ui_writer); #endif /* the typecast below was added to please mingw32 */ priv_key = (EVP_PKEY *) @@ -551,6 +588,9 @@ int cert_stuff(struct connectdata *conn, ui_method, #endif data->set.str[STRING_KEY_PASSWD]); +#ifdef HAVE_ENGINE_LOAD_FOUR_ARGS + UI_destroy_method(ui_method); +#endif if(!priv_key) { failf(data, "failed to load private key from crypto engine"); return 0; @@ -1152,6 +1192,8 @@ static CURLcode verifyhost(struct connectdata *conn, /* an alternative name field existed, but didn't match and then we MUST fail */ infof(data, "\t subjectAltName does not match %s\n", conn->host.dispname); + failf(data, "SSL: no alternative certificate subject name matches " + "target host name '%s'", conn->host.dispname); res = CURLE_PEER_FAILED_VERIFICATION; } else { @@ -1770,7 +1812,7 @@ ossl_connect_step2(struct connectdata *conn, int sockindex) */ if(CURLE_SSL_CONNECT_ERROR == rc && errdetail == 0) { failf(data, "Unknown SSL protocol error in connection to %s:%ld ", - conn->host.name, conn->port); + conn->host.name, conn->remote_port); return rc; } /* Could be a CERT problem */ @@ -1806,60 +1848,6 @@ static int asn1_object_dump(ASN1_OBJECT *a, char *buf, size_t len) return 0; } -static CURLcode push_certinfo_len(struct SessionHandle *data, - int certnum, - const char *label, - const char *value, - size_t valuelen) -{ - struct curl_certinfo *ci = &data->info.certs; - char *output; - struct curl_slist *nl; - CURLcode res = CURLE_OK; - size_t labellen = strlen(label); - size_t outlen = labellen + 1 + valuelen + 1; /* label:value\0 */ - - output = malloc(outlen); - if(!output) - return CURLE_OUT_OF_MEMORY; - - /* sprintf the label and colon */ - snprintf(output, outlen, "%s:", label); - - /* memcpy the value (it might not be zero terminated) */ - memcpy(&output[labellen+1], value, valuelen); - - /* zero terminate the output */ - output[labellen + 1 + valuelen] = 0; - - /* TODO: we should rather introduce an internal API that can do the - equivalent of curl_slist_append but doesn't strdup() the given data as - like in this place the extra malloc/free is totally pointless */ - nl = curl_slist_append(ci->certinfo[certnum], output); - free(output); - if(!nl) { - curl_slist_free_all(ci->certinfo[certnum]); - ci->certinfo[certnum] = NULL; - res = CURLE_OUT_OF_MEMORY; - } - else - ci->certinfo[certnum] = nl; - - return res; -} - -/* this is a convenience function for push_certinfo_len that takes a zero - terminated value */ -static CURLcode push_certinfo(struct SessionHandle *data, - int certnum, - const char *label, - const char *value) -{ - size_t valuelen = strlen(value); - - return push_certinfo_len(data, certnum, label, value, valuelen); -} - static void pubkey_show(struct SessionHandle *data, int num, const char *type, @@ -1883,7 +1871,7 @@ static void pubkey_show(struct SessionHandle *data, left -= 3; } infof(data, " %s: %s\n", namebuf, buffer); - push_certinfo(data, num, namebuf, buffer); + Curl_ssl_push_certinfo(data, num, namebuf, buffer); free(buffer); } } @@ -1952,7 +1940,7 @@ static int X509V3_ext(struct SessionHandle *data, } infof(data, " %s\n", buf); - push_certinfo(data, certnum, namebuf, buf); + Curl_ssl_push_certinfo(data, certnum, namebuf, buf); BIO_free(bio_out); @@ -1972,7 +1960,7 @@ static void X509_signature(struct SessionHandle *data, ptr+=snprintf(ptr, sizeof(buf)-(ptr-buf), "%02x:", sig->data[i]); infof(data, " Signature: %s\n", buf); - push_certinfo(data, numcert, "Signature", buf); + Curl_ssl_push_certinfo(data, numcert, "Signature", buf); } static void dumpcert(struct SessionHandle *data, X509 *x, int numcert) @@ -1988,30 +1976,13 @@ static void dumpcert(struct SessionHandle *data, X509 *x, int numcert) infof(data, "%s\n", biomem->data); - push_certinfo_len(data, numcert, "Cert", biomem->data, biomem->length); + Curl_ssl_push_certinfo_len(data, numcert, + "Cert", biomem->data, biomem->length); BIO_free(bio_out); } - -static int init_certinfo(struct SessionHandle *data, - int num) -{ - struct curl_certinfo *ci = &data->info.certs; - struct curl_slist **table; - - Curl_ssl_free_certinfo(data); - - ci->num_of_certs = num; - table = calloc((size_t)num, sizeof(struct curl_slist *)); - if(!table) - return 1; - - ci->certinfo = table; - return 0; -} - /* * This size was previously 512 which has been reported "too small" without * any specifics, so it was enlarged to allow more data to get shown uncut. @@ -2040,7 +2011,7 @@ static CURLcode get_cert_chain(struct connectdata *conn, } numcerts = sk_X509_num(sk); - if(init_certinfo(data, numcerts)) { + if(Curl_ssl_init_certinfo(data, numcerts)) { free(bufp); return CURLE_OUT_OF_MEMORY; } @@ -2065,16 +2036,16 @@ static CURLcode get_cert_chain(struct connectdata *conn, (void)x509_name_oneline(X509_get_subject_name(x), bufp, CERTBUFFERSIZE); infof(data, "%2d Subject: %s\n", i, bufp); - push_certinfo(data, i, "Subject", bufp); + Curl_ssl_push_certinfo(data, i, "Subject", bufp); (void)x509_name_oneline(X509_get_issuer_name(x), bufp, CERTBUFFERSIZE); infof(data, " Issuer: %s\n", bufp); - push_certinfo(data, i, "Issuer", bufp); + Curl_ssl_push_certinfo(data, i, "Issuer", bufp); value = X509_get_version(x); infof(data, " Version: %lu (0x%lx)\n", value+1, value); snprintf(bufp, CERTBUFFERSIZE, "%lx", value); - push_certinfo(data, i, "Version", bufp); /* hex */ + Curl_ssl_push_certinfo(data, i, "Version", bufp); /* hex */ num=X509_get_serialNumber(x); if(num->length <= 4) { @@ -2103,30 +2074,30 @@ static CURLcode get_cert_chain(struct connectdata *conn, bufp[0]=0; } if(bufp[0]) - push_certinfo(data, i, "Serial Number", bufp); /* hex */ + Curl_ssl_push_certinfo(data, i, "Serial Number", bufp); /* hex */ cinf = x->cert_info; j = asn1_object_dump(cinf->signature->algorithm, bufp, CERTBUFFERSIZE); if(!j) { infof(data, " Signature Algorithm: %s\n", bufp); - push_certinfo(data, i, "Signature Algorithm", bufp); + Curl_ssl_push_certinfo(data, i, "Signature Algorithm", bufp); } certdate = X509_get_notBefore(x); asn1_output(certdate, bufp, CERTBUFFERSIZE); infof(data, " Start date: %s\n", bufp); - push_certinfo(data, i, "Start date", bufp); + Curl_ssl_push_certinfo(data, i, "Start date", bufp); certdate = X509_get_notAfter(x); asn1_output(certdate, bufp, CERTBUFFERSIZE); infof(data, " Expire date: %s\n", bufp); - push_certinfo(data, i, "Expire date", bufp); + Curl_ssl_push_certinfo(data, i, "Expire date", bufp); j = asn1_object_dump(cinf->key->algor->algorithm, bufp, CERTBUFFERSIZE); if(!j) { infof(data, " Public Key Algorithm: %s\n", bufp); - push_certinfo(data, i, "Public Key Algorithm", bufp); + Curl_ssl_push_certinfo(data, i, "Public Key Algorithm", bufp); } pubkey = X509_get_pubkey(x); @@ -2138,7 +2109,7 @@ static CURLcode get_cert_chain(struct connectdata *conn, infof(data, " RSA Public Key (%d bits)\n", BN_num_bits(pubkey->pkey.rsa->n)); snprintf(bufp, CERTBUFFERSIZE, "%d", BN_num_bits(pubkey->pkey.rsa->n)); - push_certinfo(data, i, "RSA Public Key", bufp); + Curl_ssl_push_certinfo(data, i, "RSA Public Key", bufp); print_pubkey_BN(rsa, n, i); print_pubkey_BN(rsa, e, i); @@ -2220,14 +2191,7 @@ static CURLcode servercert(struct connectdata *conn, rc = x509_name_oneline(X509_get_subject_name(connssl->server_cert), buffer, BUFSIZE); - if(rc) { - if(strict) - failf(data, "SSL: couldn't get X509-subject!"); - X509_free(connssl->server_cert); - connssl->server_cert = NULL; - return CURLE_SSL_CONNECT_ERROR; - } - infof(data, "\t subject: %s\n", buffer); + infof(data, "\t subject: %s\n", rc?"[NONE]":buffer); certdate = X509_get_notBefore(connssl->server_cert); asn1_output(certdate, buffer, BUFSIZE); @@ -2389,7 +2353,7 @@ ossl_connect_step3(struct connectdata *conn, * operations. */ - if(!data->set.ssl.verifypeer) + if(!data->set.ssl.verifypeer && !data->set.ssl.verifyhost) (void)servercert(conn, connssl, FALSE); else retcode = servercert(conn, connssl, TRUE); @@ -2568,7 +2532,7 @@ static ssize_t ossl_send(struct connectdata *conn, memlen = (len > (size_t)INT_MAX) ? INT_MAX : (int)len; rc = SSL_write(conn->ssl[sockindex].handle, mem, memlen); - if(rc < 0) { + if(rc <= 0) { err = SSL_get_error(conn->ssl[sockindex].handle, rc); switch(err) { @@ -2617,7 +2581,7 @@ static ssize_t ossl_recv(struct connectdata *conn, /* connection data */ buffsize = (buffersize > (size_t)INT_MAX) ? INT_MAX : (int)buffersize; nread = (ssize_t)SSL_read(conn->ssl[num].handle, buf, buffsize); - if(nread < 0) { + if(nread <= 0) { /* failed SSL_read */ int err = SSL_get_error(conn->ssl[num].handle, (int)nread); @@ -2631,13 +2595,19 @@ static ssize_t ossl_recv(struct connectdata *conn, /* connection data */ *curlcode = CURLE_AGAIN; return -1; default: - /* openssl/ssl.h says "look at error stack/return value/errno" */ + /* openssl/ssl.h for SSL_ERROR_SYSCALL says "look at error stack/return + value/errno" */ + /* http://www.openssl.org/docs/crypto/ERR_get_error.html */ sslerror = ERR_get_error(); - failf(conn->data, "SSL read: %s, errno %d", - ERR_error_string(sslerror, error_buffer), - SOCKERRNO); - *curlcode = CURLE_RECV_ERROR; - return -1; + if((nread < 0) || sslerror) { + /* If the return code was negative or there actually is an error in the + queue */ + failf(conn->data, "SSL read: %s, errno %d", + ERR_error_string(sslerror, error_buffer), + SOCKERRNO); + *curlcode = CURLE_RECV_ERROR; + return -1; + } } } return nread; diff --git a/plugins/FTPFileYM/curl/lib/ssluse.h b/plugins/FTPFileYM/curl/lib/ssluse.h index bdec2b513e..d6efcb2717 100644 --- a/plugins/FTPFileYM/curl/lib/ssluse.h +++ b/plugins/FTPFileYM/curl/lib/ssluse.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2012, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2013, 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 @@ -73,6 +73,10 @@ void Curl_ossl_md5sum(unsigned char *tmp, /* input */ unsigned char *md5sum /* output */, size_t unused); +/* this backend provides these functions: */ +#define have_curlssl_random 1 +#define have_curlssl_md5sum 1 + /* API setup for OpenSSL */ #define curlssl_init Curl_ossl_init #define curlssl_cleanup Curl_ossl_cleanup diff --git a/plugins/FTPFileYM/curl/lib/strequal.c b/plugins/FTPFileYM/curl/lib/strequal.c index f69c5bf9e7..5f2f508e21 100644 --- a/plugins/FTPFileYM/curl/lib/strequal.c +++ b/plugins/FTPFileYM/curl/lib/strequal.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2011, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2013, 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 @@ -77,48 +77,3 @@ int curl_strnequal(const char *first, const char *second, size_t max) return toupper(*first) == toupper(*second); #endif } - -#ifndef HAVE_STRLCAT -/* - * The strlcat() function appends the NUL-terminated string src to the end - * of dst. It will append at most size - strlen(dst) - 1 bytes, NUL-termi- - * nating the result. - * - * The strlcpy() and strlcat() functions return the total length of the - * string they tried to create. For strlcpy() that means the length of src. - * For strlcat() that means the initial length of dst plus the length of - * src. While this may seem somewhat confusing it was done to make trunca- - * tion detection simple. - * - * - */ -size_t Curl_strlcat(char *dst, const char *src, size_t siz) -{ - char *d = dst; - const char *s = src; - size_t n = siz; - union { - ssize_t sig; - size_t uns; - } dlen; - - /* Find the end of dst and adjust bytes left but don't go past end */ - while(n-- != 0 && *d != '\0') - d++; - dlen.sig = d - dst; - n = siz - dlen.uns; - - if(n == 0) - return(dlen.uns + strlen(s)); - while(*s != '\0') { - if(n != 1) { - *d++ = *s; - n--; - } - s++; - } - *d = '\0'; - - return(dlen.uns + (s - src)); /* count does not include NUL */ -} -#endif diff --git a/plugins/FTPFileYM/curl/lib/strequal.h b/plugins/FTPFileYM/curl/lib/strequal.h index 287e042f4e..117a305b76 100644 --- a/plugins/FTPFileYM/curl/lib/strequal.h +++ b/plugins/FTPFileYM/curl/lib/strequal.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2008, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2013, 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 @@ -27,10 +27,5 @@ #define strequal(a,b) curl_strequal(a,b) #define strnequal(a,b,c) curl_strnequal(a,b,c) -#ifndef HAVE_STRLCAT -#define strlcat(x,y,z) Curl_strlcat(x,y,z) -#endif -size_t strlcat(char *dst, const char *src, size_t siz); - #endif /* HEADER_CURL_STREQUAL_H */ diff --git a/plugins/FTPFileYM/curl/lib/strerror.c b/plugins/FTPFileYM/curl/lib/strerror.c index 58c6dc310b..9e8c83f726 100644 --- a/plugins/FTPFileYM/curl/lib/strerror.c +++ b/plugins/FTPFileYM/curl/lib/strerror.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 2004 - 2012, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 2004 - 2013, 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 @@ -292,6 +292,9 @@ curl_easy_strerror(CURLcode error) case CURLE_CHUNK_FAILED: return "Chunk callback failed"; + case CURLE_NO_CONNECTION_AVAILABLE: + return "The max connection limit is reached"; + /* error codes not used by current libcurl */ case CURLE_OBSOLETE16: case CURLE_OBSOLETE20: @@ -358,6 +361,9 @@ curl_multi_strerror(CURLMcode error) case CURLM_UNKNOWN_OPTION: return "Unknown option"; + case CURLM_ADDED_ALREADY: + return "The easy handle is already added to a multi handle"; + case CURLM_LAST: break; } diff --git a/plugins/FTPFileYM/curl/lib/telnet.c b/plugins/FTPFileYM/curl/lib/telnet.c index 77d8b7b6b5..3206ee1137 100644 --- a/plugins/FTPFileYM/curl/lib/telnet.c +++ b/plugins/FTPFileYM/curl/lib/telnet.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2012, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2013, 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 @@ -247,7 +247,7 @@ CURLcode init_telnet(struct connectdata *conn) if(!tn) return CURLE_OUT_OF_MEMORY; - conn->data->state.proto.telnet = (void *)tn; /* make us known */ + conn->data->req.protop = tn; /* make us known */ tn->telrcv_state = CURL_TS_DATA; @@ -292,7 +292,7 @@ CURLcode init_telnet(struct connectdata *conn) static void negotiate(struct connectdata *conn) { int i; - struct TELNET *tn = (struct TELNET *) conn->data->state.proto.telnet; + struct TELNET *tn = (struct TELNET *) conn->data->req.protop; for(i = 0;i < CURL_NTELOPTS;i++) { if(i==CURL_TELOPT_ECHO) @@ -366,7 +366,7 @@ static void send_negotiation(struct connectdata *conn, int cmd, int option) static void set_remote_option(struct connectdata *conn, int option, int newstate) { - struct TELNET *tn = (struct TELNET *)conn->data->state.proto.telnet; + struct TELNET *tn = (struct TELNET *)conn->data->req.protop; if(newstate == CURL_YES) { switch(tn->him[option]) { case CURL_NO: @@ -440,7 +440,7 @@ void set_remote_option(struct connectdata *conn, int option, int newstate) static void rec_will(struct connectdata *conn, int option) { - struct TELNET *tn = (struct TELNET *)conn->data->state.proto.telnet; + struct TELNET *tn = (struct TELNET *)conn->data->req.protop; switch(tn->him[option]) { case CURL_NO: if(tn->him_preferred[option] == CURL_YES) { @@ -488,7 +488,7 @@ void rec_will(struct connectdata *conn, int option) static void rec_wont(struct connectdata *conn, int option) { - struct TELNET *tn = (struct TELNET *)conn->data->state.proto.telnet; + struct TELNET *tn = (struct TELNET *)conn->data->req.protop; switch(tn->him[option]) { case CURL_NO: /* Already disabled */ @@ -530,7 +530,7 @@ void rec_wont(struct connectdata *conn, int option) static void set_local_option(struct connectdata *conn, int option, int newstate) { - struct TELNET *tn = (struct TELNET *)conn->data->state.proto.telnet; + struct TELNET *tn = (struct TELNET *)conn->data->req.protop; if(newstate == CURL_YES) { switch(tn->us[option]) { case CURL_NO: @@ -604,7 +604,7 @@ set_local_option(struct connectdata *conn, int option, int newstate) static void rec_do(struct connectdata *conn, int option) { - struct TELNET *tn = (struct TELNET *)conn->data->state.proto.telnet; + struct TELNET *tn = (struct TELNET *)conn->data->req.protop; switch(tn->us[option]) { case CURL_NO: if(tn->us_preferred[option] == CURL_YES) { @@ -664,7 +664,7 @@ void rec_do(struct connectdata *conn, int option) static void rec_dont(struct connectdata *conn, int option) { - struct TELNET *tn = (struct TELNET *)conn->data->state.proto.telnet; + struct TELNET *tn = (struct TELNET *)conn->data->req.protop; switch(tn->us[option]) { case CURL_NO: /* Already disabled */ @@ -825,7 +825,7 @@ static CURLcode check_telnet_options(struct connectdata *conn) char option_keyword[128]; char option_arg[256]; struct SessionHandle *data = conn->data; - struct TELNET *tn = (struct TELNET *)conn->data->state.proto.telnet; + struct TELNET *tn = (struct TELNET *)conn->data->req.protop; CURLcode result = CURLE_OK; int binary_option; @@ -935,7 +935,7 @@ static void suboption(struct connectdata *conn) char varname[128]; char varval[128]; struct SessionHandle *data = conn->data; - struct TELNET *tn = (struct TELNET *)data->state.proto.telnet; + struct TELNET *tn = (struct TELNET *)data->req.protop; printsub(data, '<', (unsigned char *)tn->subbuffer, CURL_SB_LEN(tn)+2); switch (CURL_SB_GET(tn)) { @@ -1009,7 +1009,7 @@ static void sendsuboption(struct connectdata *conn, int option) unsigned char*uc1, *uc2; struct SessionHandle *data = conn->data; - struct TELNET *tn = (struct TELNET *)data->state.proto.telnet; + struct TELNET *tn = (struct TELNET *)data->req.protop; switch (option) { case CURL_TELOPT_NAWS: @@ -1067,7 +1067,7 @@ CURLcode telrcv(struct connectdata *conn, int in = 0; int startwrite=-1; struct SessionHandle *data = conn->data; - struct TELNET *tn = (struct TELNET *)data->state.proto.telnet; + struct TELNET *tn = (struct TELNET *)data->req.protop; #define startskipping() \ if(startwrite >= 0) { \ @@ -1264,7 +1264,7 @@ static CURLcode send_telnet_data(struct connectdata *conn, static CURLcode telnet_done(struct connectdata *conn, CURLcode status, bool premature) { - struct TELNET *tn = (struct TELNET *)conn->data->state.proto.telnet; + struct TELNET *tn = (struct TELNET *)conn->data->req.protop; (void)status; /* unused */ (void)premature; /* not used */ @@ -1274,7 +1274,7 @@ static CURLcode telnet_done(struct connectdata *conn, curl_slist_free_all(tn->telnet_vars); tn->telnet_vars = NULL; - Curl_safefree(conn->data->state.proto.telnet); + Curl_safefree(conn->data->req.protop); return CURLE_OK; } @@ -1318,7 +1318,7 @@ static CURLcode telnet_do(struct connectdata *conn, bool *done) if(code) return code; - tn = (struct TELNET *)data->state.proto.telnet; + tn = (struct TELNET *)data->req.protop; code = check_telnet_options(conn); if(code) diff --git a/plugins/FTPFileYM/curl/lib/tftp.c b/plugins/FTPFileYM/curl/lib/tftp.c index ef740b8569..f1ffd2a3ad 100644 --- a/plugins/FTPFileYM/curl/lib/tftp.c +++ b/plugins/FTPFileYM/curl/lib/tftp.c @@ -56,6 +56,7 @@ #include "multiif.h" #include "url.h" #include "rawstr.h" +#include "speedcheck.h" #define _MPRINTF_REPLACE /* use our functions only */ #include <curl/mprintf.h> @@ -947,10 +948,6 @@ static CURLcode tftp_connect(struct connectdata *conn, bool *done) blksize = TFTP_BLKSIZE_DEFAULT; - /* If there already is a protocol-specific struct allocated for this - sessionhandle, deal with it */ - Curl_reset_reqproto(conn); - state = conn->proto.tftpc = calloc(1, sizeof(tftp_state_data_t)); if(!state) return CURLE_OUT_OF_MEMORY; @@ -1259,6 +1256,15 @@ static CURLcode tftp_doing(struct connectdata *conn, bool *dophase_done) if(*dophase_done) { DEBUGF(infof(conn->data, "DO phase is complete\n")); } + else { + /* The multi code doesn't have this logic for the DOING state so we + provide it for TFTP since it may do the entire transfer in this + state. */ + if(Curl_pgrsUpdate(conn)) + result = CURLE_ABORTED_BY_CALLBACK; + else + result = Curl_speedcheck(conn->data, Curl_tvnow()); + } return result; } @@ -1307,14 +1313,6 @@ static CURLcode tftp_do(struct connectdata *conn, bool *done) *done = FALSE; - /* - Since connections can be re-used between SessionHandles, this might be a - connection already existing but on a fresh SessionHandle struct so we must - make sure we have a good 'struct TFTP' to play with. For new connections, - the struct TFTP is allocated and setup in the tftp_connect() function. - */ - Curl_reset_reqproto(conn); - if(!conn->proto.tftpc) { code = tftp_connect(conn, done); if(code) diff --git a/plugins/FTPFileYM/curl/lib/transfer.c b/plugins/FTPFileYM/curl/lib/transfer.c index 330b37a2b1..533e394b6b 100644 --- a/plugins/FTPFileYM/curl/lib/transfer.c +++ b/plugins/FTPFileYM/curl/lib/transfer.c @@ -101,11 +101,13 @@ CURLcode Curl_fillreadbuffer(struct connectdata *conn, int bytes, int *nreadp) #ifdef CURL_DOES_CONVERSIONS bool sending_http_headers = FALSE; - if((conn->handler->protocol&(CURLPROTO_HTTP|CURLPROTO_RTSP)) && - (data->state.proto.http->sending == HTTPSEND_REQUEST)) { - /* We're sending the HTTP request headers, not the data. - Remember that so we don't re-translate them into garbage. */ - sending_http_headers = TRUE; + if(conn->handler->protocol&(CURLPROTO_HTTP|CURLPROTO_RTSP)) { + const struct HTTP *http = data->req.protop; + + if(http->sending == HTTPSEND_REQUEST) + /* We're sending the HTTP request headers, not the data. + Remember that so we don't re-translate them into garbage. */ + sending_http_headers = TRUE; } #endif @@ -473,7 +475,7 @@ static CURLcode readwrite_data(struct SessionHandle *data, /* We've stopped dealing with input, get out of the do-while loop */ if(nread > 0) { - if(conn->data->multi && Curl_multi_canPipeline(conn->data->multi)) { + if(Curl_multi_pipeline_enabled(conn->data->multi)) { infof(data, "Rewinding stream by : %zd" " bytes on url %s (zero-length body)\n", @@ -540,6 +542,10 @@ static CURLcode readwrite_data(struct SessionHandle *data, if(!Curl_meets_timecondition(data, k->timeofdoc)) { *done = TRUE; + /* We're simulating a http 304 from server so we return + what should have been returned from the server */ + data->info.httpcode = 304; + infof(data, "Simulate a HTTP 304 response!\n"); /* we abort the transfer before it is completed == we ruin the re-use ability. Close the connection */ conn->bits.close = TRUE; @@ -602,8 +608,7 @@ static CURLcode readwrite_data(struct SessionHandle *data, if(dataleft != 0) { infof(conn->data, "Leftovers after chunking: %zu bytes\n", dataleft); - if(conn->data->multi && - Curl_multi_canPipeline(conn->data->multi)) { + if(Curl_multi_pipeline_enabled(conn->data->multi)) { /* only attempt the rewind if we truly are pipelining */ infof(conn->data, "Rewinding %zu bytes\n",dataleft); read_rewind(conn, dataleft); @@ -626,7 +631,7 @@ static CURLcode readwrite_data(struct SessionHandle *data, excess = (size_t)(k->bytecount + nread - k->maxdownload); if(excess > 0 && !k->ignorebody) { - if(conn->data->multi && Curl_multi_canPipeline(conn->data->multi)) { + if(Curl_multi_pipeline_enabled(conn->data->multi)) { /* The 'excess' amount below can't be more than BUFSIZE which always will fit in a size_t */ infof(data, @@ -811,9 +816,10 @@ static CURLcode readwrite_upload(struct SessionHandle *data, /* HTTP pollution, this should be written nicer to become more protocol agnostic. */ int fillcount; + struct HTTP *http = data->req.protop; if((k->exp100 == EXP100_SENDING_REQUEST) && - (data->state.proto.http->sending == HTTPSEND_BODY)) { + (http->sending == HTTPSEND_BODY)) { /* If this call is to send body data, we must take some action: We have sent off the full HTTP 1.1 request, and we shall now go into the Expect: 100 state and await such a header */ @@ -828,7 +834,7 @@ static CURLcode readwrite_upload(struct SessionHandle *data, } if(conn->handler->protocol&(CURLPROTO_HTTP|CURLPROTO_RTSP)) { - if(data->state.proto.http->sending == HTTPSEND_REQUEST) + if(http->sending == HTTPSEND_REQUEST) /* We're sending the HTTP request headers, not the data. Remember that so we don't change the line endings. */ sending_http_headers = TRUE; @@ -1853,7 +1859,7 @@ CURLcode Curl_retry_request(struct connectdata *conn, data->req.headerbytecount == 0) && conn->bits.reuse && !data->set.opt_no_body && - data->set.rtspreq != RTSPREQ_RECEIVE)) { + data->set.rtspreq != RTSPREQ_RECEIVE)) { /* We got no data, we attempted to re-use a connection and yet we want a "body". This might happen if the connection was left alive when we were done using it before, but that was closed when we wanted to read from @@ -1871,9 +1877,11 @@ CURLcode Curl_retry_request(struct connectdata *conn, transferred! */ - if((conn->handler->protocol&CURLPROTO_HTTP) && - data->state.proto.http->writebytecount) - return Curl_readrewind(conn); + if(conn->handler->protocol&CURLPROTO_HTTP) { + struct HTTP *http = data->req.protop; + if(http->writebytecount) + return Curl_readrewind(conn); + } } return CURLE_OK; } @@ -1931,6 +1939,7 @@ Curl_setup_transfer( k->keepon |= KEEP_RECV; if(conn->writesockfd != CURL_SOCKET_BAD) { + struct HTTP *http = data->req.protop; /* HTTP 1.1 magic: Even if we require a 100-return code before uploading data, we might @@ -1941,13 +1950,16 @@ Curl_setup_transfer( state info where we wait for the 100-return code */ if((data->state.expect100header) && - (data->state.proto.http->sending == HTTPSEND_BODY)) { + (conn->handler->protocol&CURLPROTO_HTTP) && + (http->sending == HTTPSEND_BODY)) { /* wait with write until we either got 100-continue or a timeout */ k->exp100 = EXP100_AWAITING_CONTINUE; k->start100 = Curl_tvnow(); - /* set a timeout for the multi interface */ - Curl_expire(data, CURL_TIMEOUT_EXPECT_100); + /* Set a timeout for the multi interface. Add the inaccuracy margin so + that we don't fire slightly too early and get denied to run. */ + Curl_expire(data, CURL_TIMEOUT_EXPECT_100 + + MULTI_TIMEOUT_INACCURACY / 1000); } else { if(data->state.expect100header) diff --git a/plugins/FTPFileYM/curl/lib/url.c b/plugins/FTPFileYM/curl/lib/url.c index 918ce58c3f..c1672d08bc 100644 --- a/plugins/FTPFileYM/curl/lib/url.c +++ b/plugins/FTPFileYM/curl/lib/url.c @@ -123,6 +123,8 @@ int curl_win32_idn_to_ascii(const char *in, char **out); #include "bundles.h" #include "conncache.h" #include "multihandle.h" +#include "pipeline.h" +#include "dotdot.h" #define _MPRINTF_REPLACE /* use our functions only */ #include <curl/mprintf.h> @@ -134,12 +136,19 @@ int curl_win32_idn_to_ascii(const char *in, char **out); /* Local static prototypes */ static struct connectdata * find_oldest_idle_connection(struct SessionHandle *data); +static struct connectdata * +find_oldest_idle_connection_in_bundle(struct SessionHandle *data, + struct connectbundle *bundle); static void conn_free(struct connectdata *conn); static void signalPipeClose(struct curl_llist *pipeline, bool pipe_broke); static CURLcode do_init(struct connectdata *conn); -static CURLcode parse_url_userpass(struct SessionHandle *data, - struct connectdata *conn, - char *user, char *passwd); +static CURLcode parse_url_login(struct SessionHandle *data, + struct connectdata *conn, + char **userptr, char **passwdptr, + char **optionsptr); +static CURLcode parse_login_details(const char *login, const size_t len, + char **userptr, char **passwdptr, + char **optionsptr); /* * Protocol table. */ @@ -257,7 +266,7 @@ static const struct Curl_handler Curl_handler_dummy = { PROTOPT_NONE /* flags */ }; -void Curl_freeset(struct SessionHandle * data) +void Curl_freeset(struct SessionHandle *data) { /* Free all dynamic strings stored in the data->set substructure. */ enum dupstring i; @@ -271,7 +280,7 @@ void Curl_freeset(struct SessionHandle * data) data->change.referer = NULL; } -static CURLcode setstropt(char **charp, char * s) +static CURLcode setstropt(char **charp, char *s) { /* Release the previous storage at `charp' and replace by a dynamic storage copy of `s'. Return CURLE_OK or CURLE_OUT_OF_MEMORY. */ @@ -290,48 +299,54 @@ static CURLcode setstropt(char **charp, char * s) return CURLE_OK; } -static CURLcode setstropt_userpwd(char *option, char **user_storage, - char **pwd_storage) +static CURLcode setstropt_userpwd(char *option, char **userp, char **passwdp, + char **optionsp) { - char* separator; CURLcode result = CURLE_OK; - - if(!option) { - /* we treat a NULL passed in as a hint to clear existing info */ - Curl_safefree(*user_storage); - *user_storage = (char *) NULL; - Curl_safefree(*pwd_storage); - *pwd_storage = (char *) NULL; - return CURLE_OK; + char *user = NULL; + char *passwd = NULL; + char *options = NULL; + + /* Parse the login details if specified. It not then we treat NULL as a hint + to clear the existing data */ + if(option) { + result = parse_login_details(option, strlen(option), + (userp ? &user : NULL), + (passwdp ? &passwd : NULL), + (optionsp ? &options : NULL)); } - separator = strchr(option, ':'); - if(separator != NULL) { + if(!result) { + /* Store the username part of option if required */ + if(userp) { + if(!user && option && option[0] == ':') { + /* Allocate an empty string instead of returning NULL as user name */ + user = strdup(""); + if(!user) + result = CURLE_OUT_OF_MEMORY; + } - /* store username part of option */ - char * p; - size_t username_len = (size_t)(separator-option); - p = malloc(username_len+1); - if(!p) - result = CURLE_OUT_OF_MEMORY; - else { - memcpy(p, option, username_len); - p[username_len] = '\0'; - Curl_safefree(*user_storage); - *user_storage = p; + Curl_safefree(*userp); + *userp = user; } - /* store password part of option */ - if(result == CURLE_OK) - result = setstropt(pwd_storage, separator+1); - } - else { - result = setstropt(user_storage, option); + /* Store the password part of option if required */ + if(passwdp) { + Curl_safefree(*passwdp); + *passwdp = passwd; + } + + /* Store the options part of option if required */ + if(optionsp) { + Curl_safefree(*optionsp); + *optionsp = options; + } } + return result; } -CURLcode Curl_dupset(struct SessionHandle * dst, struct SessionHandle * src) +CURLcode Curl_dupset(struct SessionHandle *dst, struct SessionHandle *src) { CURLcode r = CURLE_OK; enum dupstring i; @@ -403,7 +418,8 @@ CURLcode Curl_close(struct SessionHandle *data) Curl_safefree(data->state.pathbuffer); data->state.path = NULL; - Curl_safefree(data->state.proto.generic); + /* freed here just in case DONE wasn't called */ + Curl_free_request_state(data); /* Close down all open SSL info and sessions */ Curl_ssl_close_all(data); @@ -597,7 +613,6 @@ CURLcode Curl_open(struct SessionHandle **curl) res = CURLE_OUT_OF_MEMORY; } else { - Curl_easy_initHandleData(data); res = Curl_init_userdefined(&data->set); data->state.headersize=HEADERSIZE; @@ -613,12 +628,9 @@ CURLcode Curl_open(struct SessionHandle **curl) data->wildcard.state = CURLWC_INIT; data->wildcard.filelist = NULL; data->set.fnmatch = ZERO_NULL; - /* This no longer creates a connection cache here. It is instead made on - the first call to curl_easy_perform() or when the handle is added to a - multi stack. */ + data->set.maxconnects = DEFAULT_CONNCACHE_SIZE; /* for easy handles */ } - if(res) { Curl_resolver_cleanup(data->state.resolver); if(data->state.headerbuff) @@ -1134,41 +1146,44 @@ CURLcode Curl_setopt(struct SessionHandle *data, CURLoption option, if(argptr == NULL) break; + Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE); + if(Curl_raw_equal(argptr, "ALL")) { /* clear all cookies */ Curl_cookie_clearall(data->cookies); - break; } else if(Curl_raw_equal(argptr, "SESS")) { /* clear session cookies */ Curl_cookie_clearsess(data->cookies); - break; } else if(Curl_raw_equal(argptr, "FLUSH")) { /* flush cookies to file */ Curl_flush_cookies(data, 0); - break; } + else { + if(!data->cookies) + /* if cookie engine was not running, activate it */ + data->cookies = Curl_cookie_init(data, NULL, NULL, TRUE); - if(!data->cookies) - /* if cookie engine was not running, activate it */ - data->cookies = Curl_cookie_init(data, NULL, NULL, TRUE); + argptr = strdup(argptr); + if(!argptr) { + result = CURLE_OUT_OF_MEMORY; + } + else { - argptr = strdup(argptr); - if(!argptr) { - result = CURLE_OUT_OF_MEMORY; - break; - } + if(checkprefix("Set-Cookie:", argptr)) + /* HTTP Header format line */ + Curl_cookie_add(data, data->cookies, TRUE, argptr + 11, NULL, NULL); - if(checkprefix("Set-Cookie:", argptr)) - /* HTTP Header format line */ - Curl_cookie_add(data, data->cookies, TRUE, argptr + 11, NULL, NULL); + else + /* Netscape format line */ + Curl_cookie_add(data, data->cookies, FALSE, argptr, NULL, NULL); - else - /* Netscape format line */ - Curl_cookie_add(data, data->cookies, FALSE, argptr, NULL, NULL); + free(argptr); + } + } + Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE); - free(argptr); break; #endif /* CURL_DISABLE_COOKIES */ @@ -1188,7 +1203,12 @@ CURLcode Curl_setopt(struct SessionHandle *data, CURLoption option, * This sets a requested HTTP version to be used. The value is one of * the listed enums in curl/curl.h. */ - data->set.httpversion = va_arg(param, long); + arg = va_arg(param, long); +#ifndef USE_NGHTTP2 + if(arg == CURL_HTTP_VERSION_2_0) + return CURLE_UNSUPPORTED_PROTOCOL; +#endif + data->set.httpversion = arg; break; case CURLOPT_HTTPAUTH: @@ -1394,9 +1414,10 @@ CURLcode Curl_setopt(struct SessionHandle *data, CURLoption option, break; case CURLOPT_FILE: /* - * FILE pointer to write to or include in the data write callback + * FILE pointer to write to. Or possibly + * used as argument to the write callback. */ - data->set.out = va_arg(param, FILE *); + data->set.out = va_arg(param, void *); break; case CURLOPT_FTPPORT: /* @@ -1437,7 +1458,7 @@ CURLcode Curl_setopt(struct SessionHandle *data, CURLoption option, * FILE pointer to read the file to be uploaded from. Or possibly * used as argument to the read callback. */ - data->set.in = va_arg(param, FILE *); + data->set.in = va_arg(param, void *); break; case CURLOPT_INFILESIZE: /* @@ -1532,11 +1553,12 @@ CURLcode Curl_setopt(struct SessionHandle *data, CURLoption option, case CURLOPT_USERPWD: /* - * user:password to use in the operation + * user:password;options to use in the operation */ result = setstropt_userpwd(va_arg(param, char *), &data->set.str[STRING_USERNAME], - &data->set.str[STRING_PASSWORD]); + &data->set.str[STRING_PASSWORD], + &data->set.str[STRING_OPTIONS]); break; case CURLOPT_USERNAME: /* @@ -1552,6 +1574,13 @@ CURLcode Curl_setopt(struct SessionHandle *data, CURLoption option, result = setstropt(&data->set.str[STRING_PASSWORD], va_arg(param, char *)); break; + case CURLOPT_XOAUTH2_BEARER: + /* + * XOAUTH2 bearer token to use in the operation + */ + result = setstropt(&data->set.str[STRING_BEARER], + va_arg(param, char *)); + break; case CURLOPT_POSTQUOTE: /* * List of RAW FTP commands to use after a transfer @@ -1593,8 +1622,20 @@ CURLcode Curl_setopt(struct SessionHandle *data, CURLoption option, data->progress.callback = TRUE; /* no longer internal */ else data->progress.callback = FALSE; /* NULL enforces internal */ + break; + + case CURLOPT_XFERINFOFUNCTION: + /* + * Transfer info callback function + */ + data->set.fxferinfo = va_arg(param, curl_xferinfo_callback); + if(data->set.fxferinfo) + data->progress.callback = TRUE; /* no longer internal */ + else + data->progress.callback = FALSE; /* NULL enforces internal */ break; + case CURLOPT_PROGRESSDATA: /* * Custom client data to pass to the progress callback @@ -1609,7 +1650,7 @@ CURLcode Curl_setopt(struct SessionHandle *data, CURLoption option, */ result = setstropt_userpwd(va_arg(param, char *), &data->set.str[STRING_PROXYUSERNAME], - &data->set.str[STRING_PROXYPASSWORD]); + &data->set.str[STRING_PROXYPASSWORD], NULL); break; case CURLOPT_PROXYUSERNAME: /* @@ -1884,6 +1925,8 @@ CURLcode Curl_setopt(struct SessionHandle *data, CURLoption option, */ data->set.ssl.fsslctxp = va_arg(param, void *); break; +#endif +#if defined(USE_SSLEAY) || defined(USE_QSOSSL) || defined(USE_GSKIT) case CURLOPT_CERTINFO: data->set.ssl.certinfo = (0 != va_arg(param, long))?TRUE:FALSE; break; @@ -2241,20 +2284,27 @@ CURLcode Curl_setopt(struct SessionHandle *data, CURLoption option, break; case CURLOPT_MAIL_FROM: + /* Set the SMTP mail originator */ result = setstropt(&data->set.str[STRING_MAIL_FROM], va_arg(param, char *)); break; case CURLOPT_MAIL_AUTH: + /* Set the SMTP auth originator */ result = setstropt(&data->set.str[STRING_MAIL_AUTH], va_arg(param, char *)); break; case CURLOPT_MAIL_RCPT: - /* get a list of mail recipients */ + /* Set the list of mail recipients */ data->set.mail_rcpt = va_arg(param, struct curl_slist *); break; + case CURLOPT_SASL_IR: + /* Enable/disable SASL initial response */ + data->set.sasl_ir = (0 != va_arg(param, long)) ? TRUE : FALSE; + break; + case CURLOPT_RTSP_REQUEST: { /* @@ -2405,6 +2455,15 @@ CURLcode Curl_setopt(struct SessionHandle *data, CURLoption option, case CURLOPT_DNS_SERVERS: result = Curl_set_dns_servers(data, va_arg(param, char *)); break; + case CURLOPT_DNS_INTERFACE: + result = Curl_set_dns_interface(data, va_arg(param, char *)); + break; + case CURLOPT_DNS_LOCAL_IP4: + result = Curl_set_dns_local_ip4(data, va_arg(param, char *)); + break; + case CURLOPT_DNS_LOCAL_IP6: + result = Curl_set_dns_local_ip6(data, va_arg(param, char *)); + break; case CURLOPT_TCP_KEEPALIVE: data->set.tcp_keepalive = (0 != va_arg(param, long))?TRUE:FALSE; @@ -2450,6 +2509,8 @@ static void conn_free(struct connectdata *conn) Curl_safefree(conn->user); Curl_safefree(conn->passwd); + Curl_safefree(conn->xoauth2_bearer); + Curl_safefree(conn->options); Curl_safefree(conn->proxyuser); Curl_safefree(conn->proxypasswd); Curl_safefree(conn->allocptr.proxyuserpwd); @@ -2469,13 +2530,9 @@ static void conn_free(struct connectdata *conn) Curl_llist_destroy(conn->send_pipe, NULL); Curl_llist_destroy(conn->recv_pipe, NULL); - Curl_llist_destroy(conn->pend_pipe, NULL); - Curl_llist_destroy(conn->done_pipe, NULL); conn->send_pipe = NULL; conn->recv_pipe = NULL; - conn->pend_pipe = NULL; - conn->done_pipe = NULL; Curl_safefree(conn->localdev); Curl_free_ssl_config(&conn->ssl_config); @@ -2522,13 +2579,13 @@ CURLcode Curl_disconnect(struct connectdata *conn, bool dead_connection) data->state.authproxy.want; } - if(has_host_ntlm || has_proxy_ntlm) { + if(has_host_ntlm || has_proxy_ntlm) data->state.authproblem = FALSE; - - Curl_http_ntlm_cleanup(conn); - } } + /* Cleanup NTLM connection-related data */ + Curl_http_ntlm_cleanup(conn); + /* Cleanup possible redirect junk */ if(data->req.newurl) { free(data->req.newurl); @@ -2540,7 +2597,7 @@ CURLcode Curl_disconnect(struct connectdata *conn, bool dead_connection) conn->handler->disconnect(conn, dead_connection); /* unlink ourselves! */ - infof(data, "Closing connection %d\n", conn->connection_id); + infof(data, "Closing connection %ld\n", conn->connection_id); Curl_conncache_remove_conn(data->state.conn_cache, conn); #if defined(USE_LIBIDN) @@ -2565,15 +2622,13 @@ CURLcode Curl_disconnect(struct connectdata *conn, bool dead_connection) Curl_ssl_close(conn, FIRSTSOCKET); /* Indicate to all handles on the pipe that we're dead */ - if(Curl_isPipeliningEnabled(data)) { + if(Curl_multi_pipeline_enabled(data->multi)) { signalPipeClose(conn->send_pipe, TRUE); signalPipeClose(conn->recv_pipe, TRUE); - signalPipeClose(conn->pend_pipe, TRUE); - signalPipeClose(conn->done_pipe, FALSE); } conn_free(conn); - data->state.current_conn = NULL; + Curl_speedinit(data); return CURLE_OK; @@ -2601,7 +2656,7 @@ static bool IsPipeliningPossible(const struct SessionHandle *handle, const struct connectdata *conn) { if((conn->handler->protocol & CURLPROTO_HTTP) && - handle->multi && Curl_multi_canPipeline(handle->multi) && + Curl_multi_pipeline_enabled(handle->multi) && (handle->set.httpreq == HTTPREQ_GET || handle->set.httpreq == HTTPREQ_HEAD) && handle->set.httpversion != CURL_HTTP_VERSION_1_0) @@ -2612,10 +2667,7 @@ static bool IsPipeliningPossible(const struct SessionHandle *handle, bool Curl_isPipeliningEnabled(const struct SessionHandle *handle) { - if(handle->multi && Curl_multi_canPipeline(handle->multi)) - return TRUE; - - return FALSE; + return Curl_multi_pipeline_enabled(handle->multi); } CURLcode Curl_addHandleToPipeline(struct SessionHandle *data, @@ -2623,6 +2675,7 @@ CURLcode Curl_addHandleToPipeline(struct SessionHandle *data, { if(!Curl_llist_insert_next(pipeline, pipeline->tail, data)) return CURLE_OUT_OF_MEMORY; + infof(data, "Curl_addHandleToPipeline: length: %d\n", pipeline->size); return CURLE_OK; } @@ -2682,8 +2735,6 @@ void Curl_getoff_all_pipelines(struct SessionHandle *data, conn->readchannel_inuse = FALSE; if(Curl_removeHandleFromPipeline(data, conn->send_pipe) && send_head) conn->writechannel_inuse = FALSE; - Curl_removeHandleFromPipeline(data, conn->pend_pipe); - Curl_removeHandleFromPipeline(data, conn->done_pipe); } static void signalPipeClose(struct curl_llist *pipeline, bool pipe_broke) @@ -2714,8 +2765,8 @@ static void signalPipeClose(struct curl_llist *pipeline, bool pipe_broke) } /* - * This function kills and removes an existing connection in the connection - * cache. The connection that has been unused for the longest time. + * This function finds the connection in the connection + * cache that has been unused for the longest time. * * Returns the pointer to the oldest idle connection, or NULL if none was * found. @@ -2766,6 +2817,47 @@ find_oldest_idle_connection(struct SessionHandle *data) } /* + * This function finds the connection in the connection + * bundle that has been unused for the longest time. + * + * Returns the pointer to the oldest idle connection, or NULL if none was + * found. + */ +static struct connectdata * +find_oldest_idle_connection_in_bundle(struct SessionHandle *data, + struct connectbundle *bundle) +{ + struct curl_llist_element *curr; + long highscore=-1; + long score; + struct timeval now; + struct connectdata *conn_candidate = NULL; + struct connectdata *conn; + + (void)data; + + now = Curl_tvnow(); + + curr = bundle->conn_list->head; + while(curr) { + conn = curr->ptr; + + if(!conn->inuse) { + /* Set higher score for the age passed since the connection was used */ + score = Curl_tvdiff(now, conn->now); + + if(score > highscore) { + highscore = score; + conn_candidate = conn; + } + } + curr = curr->next; + } + + return conn_candidate; +} + +/* * Given one filled in connection struct (named needle), this function should * detect if there already is one that has all the significant details * exactly the same and thus should be used instead. @@ -2773,11 +2865,15 @@ find_oldest_idle_connection(struct SessionHandle *data) * If there is a match, this function returns TRUE - and has marked the * connection as 'in-use'. It must later be called with ConnectionDone() to * return back to 'idle' (unused) state. + * + * The force_reuse flag is set if the connection must be used, even if + * the pipelining strategy wants to open a new connection instead of reusing. */ static bool ConnectionExists(struct SessionHandle *data, struct connectdata *needle, - struct connectdata **usethis) + struct connectdata **usethis, + bool *force_reuse) { struct connectdata *check; struct connectdata *chosen = 0; @@ -2786,14 +2882,30 @@ ConnectionExists(struct SessionHandle *data, (data->state.authhost.want==CURLAUTH_NTLM_WB) ? TRUE : FALSE; struct connectbundle *bundle; + *force_reuse = FALSE; + + /* We can't pipe if the site is blacklisted */ + if(canPipeline && Curl_pipeline_site_blacklisted(data, needle)) { + canPipeline = FALSE; + } + /* Look up the bundle with all the connections to this particular host */ bundle = Curl_conncache_find_bundle(data->state.conn_cache, needle->host.name); if(bundle) { + size_t max_pipe_len = Curl_multi_max_pipeline_length(data->multi); + size_t best_pipe_len = max_pipe_len; struct curl_llist_element *curr; - infof(data, "Found bundle for host %s: %p\n", needle->host.name, bundle); + infof(data, "Found bundle for host %s: %p\n", + needle->host.name, (void *)bundle); + + /* We can't pipe if we don't know anything about the server */ + if(canPipeline && !bundle->server_supports_pipelining) { + infof(data, "Server doesn't support pipelining\n"); + canPipeline = FALSE; + } curr = bundle->conn_list->head; while(curr) { @@ -2823,7 +2935,7 @@ ConnectionExists(struct SessionHandle *data, if(dead) { check->data = data; - infof(data, "Connection %d seems to be dead!\n", + infof(data, "Connection %ld seems to be dead!\n", check->connection_id); /* disconnect resources */ @@ -2844,12 +2956,6 @@ ConnectionExists(struct SessionHandle *data, if(!IsPipeliningPossible(rh, check)) continue; } -#ifdef DEBUGBUILD - if(pipeLen > MAX_PIPELINE_LENGTH) { - infof(data, "BAD! Connection #%ld has too big pipeline!\n", - check->connection_id); - } -#endif } else { if(pipeLen > 0) { @@ -2928,6 +3034,18 @@ ConnectionExists(struct SessionHandle *data, continue; } + if((needle->handler->protocol & CURLPROTO_FTP) || + ((needle->handler->protocol & CURLPROTO_HTTP) && wantNTLM)) { + /* This is FTP or HTTP+NTLM, verify that we're using the same name + and password as well */ + if(!strequal(needle->user, check->user) || + !strequal(needle->passwd, check->passwd)) { + /* one of them was different */ + continue; + } + credentialsMatch = TRUE; + } + if(!needle->bits.httpproxy || needle->handler->flags&PROTOPT_SSL || (needle->bits.httpproxy && check->bits.httpproxy && needle->bits.tunnel_proxy && check->bits.tunnel_proxy && @@ -2961,17 +3079,6 @@ ConnectionExists(struct SessionHandle *data, continue; } } - if((needle->handler->protocol & CURLPROTO_FTP) || - ((needle->handler->protocol & CURLPROTO_HTTP) && wantNTLM)) { - /* This is FTP or HTTP+NTLM, verify that we're using the same name - and password as well */ - if(!strequal(needle->user, check->user) || - !strequal(needle->passwd, check->passwd)) { - /* one of them was different */ - continue; - } - credentialsMatch = TRUE; - } match = TRUE; } } @@ -2988,26 +3095,60 @@ ConnectionExists(struct SessionHandle *data, } if(match) { - chosen = check; + /* If we are looking for an NTLM connection, check if this is already + authenticating with the right credentials. If not, keep looking so + that we can reuse NTLM connections if possible. (Especially we + must not reuse the same connection if partway through + a handshake!) */ + if(wantNTLM) { + if(credentialsMatch && check->ntlm.state != NTLMSTATE_NONE) { + chosen = check; + + /* We must use this connection, no other */ + *force_reuse = TRUE; + break; + } + else + continue; + } - /* If we are not looking for an NTLM connection, we can choose this one - immediately. */ - if(!wantNTLM) - break; + if(canPipeline) { + /* We can pipeline if we want to. Let's continue looking for + the optimal connection to use, i.e the shortest pipe that is not + blacklisted. */ + + if(pipeLen == 0) { + /* We have the optimal connection. Let's stop looking. */ + chosen = check; + break; + } + + /* We can't use the connection if the pipe is full */ + if(pipeLen >= max_pipe_len) + continue; - /* Otherwise, check if this is already authenticating with the right - credentials. If not, keep looking so that we can reuse NTLM - connections if possible. (Especially we must reuse the same - connection if partway through a handshake!) */ - if(credentialsMatch && chosen->ntlm.state != NTLMSTATE_NONE) + /* We can't use the connection if the pipe is penalized */ + if(Curl_pipeline_penalized(data, check)) + continue; + + if(pipeLen < best_pipe_len) { + /* This connection has a shorter pipe so far. We'll pick this + and continue searching */ + chosen = check; + best_pipe_len = pipeLen; + continue; + } + } + else { + /* We have found a connection. Let's stop searching. */ + chosen = check; break; + } } } } if(chosen) { - chosen->inuse = TRUE; /* mark this as being in use so that no other - handle in a multi stack may nick it */ *usethis = chosen; return TRUE; /* yes, we found one to use! */ } @@ -3080,6 +3221,9 @@ static CURLcode ConnectionStore(struct SessionHandle *data, */ CURLcode Curl_connected_proxy(struct connectdata *conn) { + if(!conn->bits.proxy) + return CURLE_OK; + switch(conn->proxytype) { #ifndef CURL_DISABLE_PROXY case CURLPROXY_SOCKS5: @@ -3471,7 +3615,7 @@ static struct connectdata *allocate_conn(struct SessionHandle *data) conn->response_header = NULL; #endif - if(data->multi && Curl_multi_canPipeline(data->multi) && + if(Curl_multi_pipeline_enabled(data->multi) && !conn->master_buffer) { /* Allocate master_buffer to be used for pipelining */ conn->master_buffer = calloc(BUFSIZE, sizeof (char)); @@ -3482,13 +3626,10 @@ static struct connectdata *allocate_conn(struct SessionHandle *data) /* Initialize the pipeline lists */ conn->send_pipe = Curl_llist_alloc((curl_llist_dtor) llist_dtor); conn->recv_pipe = Curl_llist_alloc((curl_llist_dtor) llist_dtor); - conn->pend_pipe = Curl_llist_alloc((curl_llist_dtor) llist_dtor); - conn->done_pipe = Curl_llist_alloc((curl_llist_dtor) llist_dtor); - if(!conn->send_pipe || !conn->recv_pipe || !conn->pend_pipe || - !conn->done_pipe) + if(!conn->send_pipe || !conn->recv_pipe) goto error; -#if defined(HAVE_KRB4) || defined(HAVE_GSSAPI) +#ifdef HAVE_GSSAPI conn->data_prot = PROT_CLEAR; #endif @@ -3511,13 +3652,9 @@ static struct connectdata *allocate_conn(struct SessionHandle *data) Curl_llist_destroy(conn->send_pipe, NULL); Curl_llist_destroy(conn->recv_pipe, NULL); - Curl_llist_destroy(conn->pend_pipe, NULL); - Curl_llist_destroy(conn->done_pipe, NULL); conn->send_pipe = NULL; conn->recv_pipe = NULL; - conn->pend_pipe = NULL; - conn->done_pipe = NULL; Curl_safefree(conn->master_buffer); Curl_safefree(conn->localdev); @@ -3573,8 +3710,8 @@ static CURLcode findprotocol(struct SessionHandle *data, static CURLcode parseurlandfillconn(struct SessionHandle *data, struct connectdata *conn, bool *prot_missing, - char *user, - char *passwd) + char **userp, char **passwdp, + char **optionsp) { char *at; char *fragment; @@ -3584,6 +3721,7 @@ static CURLcode parseurlandfillconn(struct SessionHandle *data, char protobuf[16]; const char *protop; CURLcode result; + bool rebuild_url = FALSE; *prot_missing = FALSE; @@ -3690,6 +3828,10 @@ static CURLcode parseurlandfillconn(struct SessionHandle *data, protop = "LDAP"; else if(checkprefix("IMAP.", conn->host.name)) protop = "IMAP"; + else if(checkprefix("SMTP.", conn->host.name)) + protop = "smtp"; + else if(checkprefix("POP3.", conn->host.name)) + protop = "pop3"; else { protop = "http"; } @@ -3730,12 +3872,14 @@ static CURLcode parseurlandfillconn(struct SessionHandle *data, memcpy(path+1, query, hostlen); path[0]='/'; /* prepend the missing slash */ + rebuild_url = TRUE; *query=0; /* now cut off the hostname at the ? */ } else if(!path[0]) { /* if there's no path set, use a single slash */ strcpy(path, "/"); + rebuild_url = TRUE; } /* If the URL is malformatted (missing a '/' after hostname before path) we @@ -3748,13 +3892,70 @@ static CURLcode parseurlandfillconn(struct SessionHandle *data, is bigger than the path. Use +1 to move the zero byte too. */ memmove(&path[1], path, strlen(path)+1); path[0] = '/'; + rebuild_url = TRUE; + } + else { + /* sanitise paths and remove ../ and ./ sequences according to RFC3986 */ + char *newp = Curl_dedotdotify(path); + if(!newp) + return CURLE_OUT_OF_MEMORY; + + if(strcmp(newp, path)) { + rebuild_url = TRUE; + free(data->state.pathbuffer); + data->state.pathbuffer = newp; + data->state.path = newp; + path = newp; + } + else + free(newp); } - /************************************************************* - * Parse a user name and password in the URL and strip it out - * of the host name - *************************************************************/ - result = parse_url_userpass(data, conn, user, passwd); + /* + * "rebuild_url" means that one or more URL components have been modified so + * we need to generate an updated full version. We need the corrected URL + * when communicating over HTTP proxy and we don't know at this point if + * we're using a proxy or not. + */ + if(rebuild_url) { + char *reurl; + + size_t plen = strlen(path); /* new path, should be 1 byte longer than + the original */ + size_t urllen = strlen(data->change.url); /* original URL length */ + + size_t prefixlen = strlen(conn->host.name); + + if(!*prot_missing) + prefixlen += strlen(protop) + strlen("://"); + + reurl = malloc(urllen + 2); /* 2 for zerobyte + slash */ + if(!reurl) + return CURLE_OUT_OF_MEMORY; + + /* copy the prefix */ + memcpy(reurl, data->change.url, prefixlen); + + /* append the trailing piece + zerobyte */ + memcpy(&reurl[prefixlen], path, plen + 1); + + /* possible free the old one */ + if(data->change.url_alloc) { + Curl_safefree(data->change.url); + data->change.url_alloc = FALSE; + } + + infof(data, "Rebuilt URL to: %s\n", reurl); + + data->change.url = reurl; + data->change.url_alloc = TRUE; /* free this later */ + } + + /* + * Parse the login details from the URL and strip them out of + * the host name + */ + result = parse_url_login(data, conn, userp, passwdp, optionsp); if(result != CURLE_OK) return result; @@ -3839,18 +4040,31 @@ static CURLcode setup_range(struct SessionHandle *data) } -/*************************************************************** -* Setup connection internals specific to the requested protocol. -* This MUST get called after proxy magic has been figured out. -***************************************************************/ +/* + * setup_connection_internals() - + * + * Setup connection internals specific to the requested protocol in the + * SessionHandle. This is inited and setup before the connection is made but + * is about the particular protocol that is to be used. + * + * This MUST get called after proxy magic has been figured out. + */ static CURLcode setup_connection_internals(struct connectdata *conn) { const struct Curl_handler * p; CURLcode result; - conn->socktype = SOCK_STREAM; /* most of them are TCP streams */ + /* in some case in the multi state-machine, we go back to the CONNECT state + and then a second (or third or...) call to this function will be made + without doing a DISCONNECT or DONE in between (since the connection is + yet in place) and therefore this function needs to first make sure + there's no lingering previous data allocated. */ + Curl_free_request_state(conn->data); - /* Scan protocol handler table. */ + memset(&conn->data->req, 0, sizeof(struct SingleRequest)); + conn->data->req.maxdownload = -1; + + conn->socktype = SOCK_STREAM; /* most of them are TCP streams */ /* Perform setup complement if some. */ p = conn->handler; @@ -3868,11 +4082,26 @@ static CURLcode setup_connection_internals(struct connectdata *conn) /* we check for -1 here since if proxy was detected already, this was very likely already set to the proxy port */ conn->port = p->defport; - conn->remote_port = (unsigned short)conn->given->defport; + + /* only if remote_port was not already parsed off the URL we use the + default port number */ + if(!conn->remote_port) + conn->remote_port = (unsigned short)conn->given->defport; return CURLE_OK; } +/* + * Curl_free_request_state() should free temp data that was allocated in the + * SessionHandle for this single request. + */ + +void Curl_free_request_state(struct SessionHandle *data) +{ + Curl_safefree(data->req.protop); +} + + #ifndef CURL_DISABLE_PROXY /**************************************************************** * Checks if the host is in the noproxy list. returns true if it matches @@ -4078,34 +4307,37 @@ static CURLcode parse_proxy(struct SessionHandle *data, /* Is there a username and password given in this proxy url? */ atsign = strchr(proxyptr, '@'); if(atsign) { - char proxyuser[MAX_CURL_USER_LENGTH]; - char proxypasswd[MAX_CURL_PASSWORD_LENGTH]; - proxypasswd[0] = 0; - - if(1 <= sscanf(proxyptr, - "%" MAX_CURL_USER_LENGTH_TXT"[^:@]:" - "%" MAX_CURL_PASSWORD_LENGTH_TXT "[^@]", - proxyuser, proxypasswd)) { - CURLcode res = CURLE_OK; + CURLcode res = CURLE_OK; + char *proxyuser = NULL; + char *proxypasswd = NULL; + res = parse_login_details(proxyptr, atsign - proxyptr, + &proxyuser, &proxypasswd, NULL); + if(!res) { /* found user and password, rip them out. note that we are unescaping them, as there is otherwise no way to have a username or password with reserved characters like ':' in them. */ Curl_safefree(conn->proxyuser); - conn->proxyuser = curl_easy_unescape(data, proxyuser, 0, NULL); + if(proxyuser && strlen(proxyuser) < MAX_CURL_USER_LENGTH) + conn->proxyuser = curl_easy_unescape(data, proxyuser, 0, NULL); + else + conn->proxyuser = strdup(""); if(!conn->proxyuser) res = CURLE_OUT_OF_MEMORY; else { Curl_safefree(conn->proxypasswd); - conn->proxypasswd = curl_easy_unescape(data, proxypasswd, 0, NULL); + if(proxypasswd && strlen(proxypasswd) < MAX_CURL_PASSWORD_LENGTH) + conn->proxypasswd = curl_easy_unescape(data, proxypasswd, 0, NULL); + else + conn->proxypasswd = strdup(""); if(!conn->proxypasswd) res = CURLE_OUT_OF_MEMORY; } - if(CURLE_OK == res) { + if(!res) { conn->bits.proxy_user_passwd = TRUE; /* enable it */ atsign++; /* the right side of the @-letter */ @@ -4114,10 +4346,13 @@ static CURLcode parse_proxy(struct SessionHandle *data, else res = CURLE_OUT_OF_MEMORY; } - - if(res) - return res; } + + Curl_safefree(proxyuser); + Curl_safefree(proxypasswd); + + if(res) + return res; } /* start scanning for port number at this point */ @@ -4211,8 +4446,10 @@ static CURLcode parse_proxy_auth(struct SessionHandle *data, #endif /* CURL_DISABLE_PROXY */ /* + * parse_url_login() * - * Parse a user name and password in the URL and strip it out of the host name + * Parse the login details (user name, password and options) from the URL and + * strip them out of the host name * * Inputs: data->set.use_netrc (CURLOPT_NETRC) * conn->host.name @@ -4220,80 +4457,233 @@ static CURLcode parse_proxy_auth(struct SessionHandle *data, * Outputs: (almost :- all currently undefined) * conn->bits.user_passwd - non-zero if non-default passwords exist * user - non-zero length if defined - * passwd - ditto + * passwd - non-zero length if defined + * options - non-zero length if defined * conn->host.name - remove user name and password */ -static CURLcode parse_url_userpass(struct SessionHandle *data, - struct connectdata *conn, - char *user, char *passwd) +static CURLcode parse_url_login(struct SessionHandle *data, + struct connectdata *conn, + char **user, char **passwd, char **options) { + CURLcode result = CURLE_OK; + char *userp = NULL; + char *passwdp = NULL; + char *optionsp = NULL; + /* At this point, we're hoping all the other special cases have * been taken care of, so conn->host.name is at most - * [user[:password]]@]hostname + * [user[:password][;options]]@]hostname * * We need somewhere to put the embedded details, so do that first. */ - char *ptr=strchr(conn->host.name, '@'); - char *userpass = conn->host.name; + char *ptr = strchr(conn->host.name, '@'); + char *login = conn->host.name; + + DEBUGASSERT(!**user); + DEBUGASSERT(!**passwd); + DEBUGASSERT(!**options); - user[0] =0; /* to make everything well-defined */ - passwd[0]=0; + if(!ptr) + goto out; /* We will now try to extract the - * possible user+password pair in a string like: + * possible login information in a string like: * ftp://user:password@ftp.my.site:8021/README */ - if(ptr != NULL) { - /* there's a user+password given here, to the left of the @ */ + conn->host.name = ++ptr; + + /* So the hostname is sane. Only bother interpreting the + * results if we could care. It could still be wasted + * work because it might be overtaken by the programmatically + * set user/passwd, but doing that first adds more cases here :-( + */ - conn->host.name = ++ptr; + if(data->set.use_netrc == CURL_NETRC_REQUIRED) + goto out; - /* So the hostname is sane. Only bother interpreting the - * results if we could care. It could still be wasted - * work because it might be overtaken by the programmatically - * set user/passwd, but doing that first adds more cases here :-( - */ + /* We could use the login information in the URL so extract it */ + result = parse_login_details(login, ptr - login - 1, + &userp, &passwdp, &optionsp); + if(result != CURLE_OK) + goto out; + if(userp) { + char *newname; + + /* We have a user in the URL */ conn->bits.userpwd_in_url = TRUE; - if(data->set.use_netrc != CURL_NETRC_REQUIRED) { - /* We could use the one in the URL */ + conn->bits.user_passwd = TRUE; /* enable user+password */ - conn->bits.user_passwd = TRUE; /* enable user+password */ + /* Decode the user */ + newname = curl_easy_unescape(data, userp, 0, NULL); + if(!newname) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } - if(*userpass != ':') { - /* the name is given, get user+password */ - sscanf(userpass, "%" MAX_CURL_USER_LENGTH_TXT "[^:@]:" - "%" MAX_CURL_PASSWORD_LENGTH_TXT "[^@]", - user, passwd); - } - else - /* no name given, get the password only */ - sscanf(userpass, ":%" MAX_CURL_PASSWORD_LENGTH_TXT "[^@]", passwd); - - if(user[0]) { - char *newname=curl_easy_unescape(data, user, 0, NULL); - if(!newname) - return CURLE_OUT_OF_MEMORY; - if(strlen(newname) < MAX_CURL_USER_LENGTH) - strcpy(user, newname); - - /* if the new name is longer than accepted, then just use - the unconverted name, it'll be wrong but what the heck */ - free(newname); - } - if(passwd[0]) { - /* we have a password found in the URL, decode it! */ - char *newpasswd=curl_easy_unescape(data, passwd, 0, NULL); - if(!newpasswd) - return CURLE_OUT_OF_MEMORY; - if(strlen(newpasswd) < MAX_CURL_PASSWORD_LENGTH) - strcpy(passwd, newpasswd); - - free(newpasswd); - } + free(*user); + *user = newname; + } + + if(passwdp) { + /* We have a password in the URL so decode it */ + char *newpasswd = curl_easy_unescape(data, passwdp, 0, NULL); + if(!newpasswd) { + result = CURLE_OUT_OF_MEMORY; + goto out; } + + free(*passwd); + *passwd = newpasswd; } - return CURLE_OK; + + if(optionsp) { + /* We have an options list in the URL so decode it */ + char *newoptions = curl_easy_unescape(data, optionsp, 0, NULL); + if(!newoptions) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } + + free(*options); + *options = newoptions; + } + + + out: + + Curl_safefree(userp); + Curl_safefree(passwdp); + Curl_safefree(optionsp); + + return result; +} + +/* + * parse_login_details() + * + * This is used to parse a login string for user name, password and options in + * the following formats: + * + * user + * user:password + * user:password;options + * user;options + * user;options:password + * :password + * :password;options + * ;options + * ;options:password + * + * Parameters: + * + * login [in] - The login string. + * len [in] - The length of the login string. + * userp [in/out] - The address where a pointer to newly allocated memory + * holding the user will be stored upon completion. + * passdwp [in/out] - The address where a pointer to newly allocated memory + * holding the password will be stored upon completion. + * optionsp [in/out] - The address where a pointer to newly allocated memory + * holding the options will be stored upon completion. + * + * Returns CURLE_OK on success. + */ +static CURLcode parse_login_details(const char *login, const size_t len, + char **userp, char **passwdp, + char **optionsp) +{ + CURLcode result = CURLE_OK; + char *ubuf = NULL; + char *pbuf = NULL; + char *obuf = NULL; + const char *psep = NULL; + const char *osep = NULL; + size_t ulen; + size_t plen; + size_t olen; + + /* Attempt to find the password separator */ + if(passwdp) { + psep = strchr(login, ':'); + + /* Within the constraint of the login string */ + if(psep >= login + len) + psep = NULL; + } + + /* Attempt to find the options separator */ + if(optionsp) { + osep = strchr(login, ';'); + + /* Within the constraint of the login string */ + if(osep >= login + len) + osep = NULL; + } + + /* Calculate the portion lengths */ + ulen = (psep ? + (size_t)(osep && psep > osep ? osep - login : psep - login) : + (osep ? (size_t)(osep - login) : len)); + plen = (psep ? + (osep && osep > psep ? (size_t)(osep - psep) : + (size_t)(login + len - psep)) - 1 : 0); + olen = (osep ? + (psep && psep > osep ? (size_t)(psep - osep) : + (size_t)(login + len - osep)) - 1 : 0); + + /* Allocate the user portion buffer */ + if(userp && ulen) { + ubuf = malloc(ulen + 1); + if(!ubuf) + result = CURLE_OUT_OF_MEMORY; + } + + /* Allocate the password portion buffer */ + if(!result && passwdp && plen) { + pbuf = malloc(plen + 1); + if(!pbuf) { + Curl_safefree(ubuf); + result = CURLE_OUT_OF_MEMORY; + } + } + + /* Allocate the options portion buffer */ + if(!result && optionsp && olen) { + obuf = malloc(olen + 1); + if(!obuf) { + Curl_safefree(pbuf); + Curl_safefree(ubuf); + result = CURLE_OUT_OF_MEMORY; + } + } + + if(!result) { + /* Store the user portion if necessary */ + if(ubuf) { + memcpy(ubuf, login, ulen); + ubuf[ulen] = '\0'; + Curl_safefree(*userp); + *userp = ubuf; + } + + /* Store the password portion if necessary */ + if(pbuf) { + memcpy(pbuf, psep + 1, plen); + pbuf[plen] = '\0'; + Curl_safefree(*passwdp); + *passwdp = pbuf; + } + + /* Store the options portion if necessary */ + if(obuf) { + memcpy(obuf, osep + 1, olen); + obuf[olen] = '\0'; + Curl_safefree(*optionsp); + *optionsp = obuf; + } + } + + return result; } /************************************************************* @@ -4416,26 +4806,38 @@ static CURLcode parse_remote_port(struct SessionHandle *data, } /* - * Override a user name and password from the URL with that in the - * CURLOPT_USERPWD option or a .netrc file, if applicable. + * Override the login details from the URL with that in the CURLOPT_USERPWD + * option or a .netrc file, if applicable. */ -static void override_userpass(struct SessionHandle *data, - struct connectdata *conn, - char *user, char *passwd) +static CURLcode override_login(struct SessionHandle *data, + struct connectdata *conn, + char **userp, char **passwdp, char **optionsp) { - if(data->set.str[STRING_USERNAME] != NULL) { - strncpy(user, data->set.str[STRING_USERNAME], MAX_CURL_USER_LENGTH); - user[MAX_CURL_USER_LENGTH-1] = '\0'; /*To be on safe side*/ + if(data->set.str[STRING_USERNAME]) { + free(*userp); + *userp = strdup(data->set.str[STRING_USERNAME]); + if(!*userp) + return CURLE_OUT_OF_MEMORY; } - if(data->set.str[STRING_PASSWORD] != NULL) { - strncpy(passwd, data->set.str[STRING_PASSWORD], MAX_CURL_PASSWORD_LENGTH); - passwd[MAX_CURL_PASSWORD_LENGTH-1] = '\0'; /*To be on safe side*/ + + if(data->set.str[STRING_PASSWORD]) { + free(*passwdp); + *passwdp = strdup(data->set.str[STRING_PASSWORD]); + if(!*passwdp) + return CURLE_OUT_OF_MEMORY; + } + + if(data->set.str[STRING_OPTIONS]) { + free(*optionsp); + *optionsp = strdup(data->set.str[STRING_OPTIONS]); + if(!*optionsp) + return CURLE_OUT_OF_MEMORY; } conn->bits.netrc = FALSE; if(data->set.use_netrc != CURL_NETRC_IGNORED) { if(Curl_parsenetrc(conn->host.name, - user, passwd, + userp, passwdp, data->set.str[STRING_NETRC_FILE])) { infof(data, "Couldn't find host %s in the " DOT_CHAR "netrc file; using defaults\n", @@ -4450,37 +4852,55 @@ static void override_userpass(struct SessionHandle *data, conn->bits.user_passwd = TRUE; /* enable user+password */ } } + + return CURLE_OK; } /* * Set password so it's available in the connection. */ -static CURLcode set_userpass(struct connectdata *conn, - const char *user, const char *passwd) +static CURLcode set_login(struct connectdata *conn, + const char *user, const char *passwd, + const char *options) { - /* If our protocol needs a password and we have none, use the defaults */ - if((conn->handler->flags & PROTOPT_NEEDSPWD) && - !conn->bits.user_passwd) { + CURLcode result = CURLE_OK; + /* If our protocol needs a password and we have none, use the defaults */ + if((conn->handler->flags & PROTOPT_NEEDSPWD) && !conn->bits.user_passwd) { + /* Store the default user */ conn->user = strdup(CURL_DEFAULT_USER); + + /* Store the default password */ if(conn->user) conn->passwd = strdup(CURL_DEFAULT_PASSWORD); else conn->passwd = NULL; + /* This is the default password, so DON'T set conn->bits.user_passwd */ } else { - /* store user + password, zero-length if not set */ + /* Store the user, zero-length if not set */ conn->user = strdup(user); + + /* Store the password (only if user is present), zero-length if not set */ if(conn->user) conn->passwd = strdup(passwd); else conn->passwd = NULL; } + if(!conn->user || !conn->passwd) - return CURLE_OUT_OF_MEMORY; + result = CURLE_OUT_OF_MEMORY; - return CURLE_OK; + /* Store the options, null if not set */ + if(!result && options[0]) { + conn->options = strdup(options); + + if(!conn->options) + result = CURLE_OUT_OF_MEMORY; + } + + return result; } /************************************************************* @@ -4619,13 +5039,9 @@ static void reuse_conn(struct connectdata *old_conn, Curl_llist_destroy(old_conn->send_pipe, NULL); Curl_llist_destroy(old_conn->recv_pipe, NULL); - Curl_llist_destroy(old_conn->pend_pipe, NULL); - Curl_llist_destroy(old_conn->done_pipe, NULL); old_conn->send_pipe = NULL; old_conn->recv_pipe = NULL; - old_conn->pend_pipe = NULL; - old_conn->done_pipe = NULL; Curl_safefree(old_conn->master_buffer); } @@ -4650,15 +5066,20 @@ static CURLcode create_conn(struct SessionHandle *data, struct connectdata **in_connect, bool *async) { - CURLcode result=CURLE_OK; + CURLcode result = CURLE_OK; struct connectdata *conn; struct connectdata *conn_temp = NULL; size_t urllen; - char user[MAX_CURL_USER_LENGTH]; - char passwd[MAX_CURL_PASSWORD_LENGTH]; + char *user = NULL; + char *passwd = NULL; + char *options = NULL; bool reuse; char *proxy = NULL; bool prot_missing = FALSE; + bool no_connections_available = FALSE; + bool force_reuse; + size_t max_host_connections = Curl_multi_max_host_connections(data->multi); + size_t max_total_connections = Curl_multi_max_total_connections(data->multi); *async = FALSE; @@ -4666,8 +5087,10 @@ static CURLcode create_conn(struct SessionHandle *data, * Check input data *************************************************************/ - if(!data->change.url) - return CURLE_URL_MALFORMAT; + if(!data->change.url) { + result = CURLE_URL_MALFORMAT; + goto out; + } /* First, split up the current URL in parts so that we can use the parts for checking against the already present connections. In order @@ -4675,8 +5098,10 @@ static CURLcode create_conn(struct SessionHandle *data, connection data struct and fill in for comparison purposes. */ conn = allocate_conn(data); - if(!conn) - return CURLE_OUT_OF_MEMORY; + if(!conn) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } /* We must set the return variable as soon as possible, so that our parent can cleanup any possible allocs we may have done before @@ -4706,23 +5131,35 @@ static CURLcode create_conn(struct SessionHandle *data, data->state.path = NULL; data->state.pathbuffer = malloc(urllen+2); - if(NULL == data->state.pathbuffer) - return CURLE_OUT_OF_MEMORY; /* really bad error */ + if(NULL == data->state.pathbuffer) { + result = CURLE_OUT_OF_MEMORY; /* really bad error */ + goto out; + } data->state.path = data->state.pathbuffer; conn->host.rawalloc = malloc(urllen+2); if(NULL == conn->host.rawalloc) { Curl_safefree(data->state.pathbuffer); data->state.path = NULL; - return CURLE_OUT_OF_MEMORY; + result = CURLE_OUT_OF_MEMORY; + goto out; } conn->host.name = conn->host.rawalloc; conn->host.name[0] = 0; - result = parseurlandfillconn(data, conn, &prot_missing, user, passwd); + user = strdup(""); + passwd = strdup(""); + options = strdup(""); + if(!user || !passwd || !options) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } + + result = parseurlandfillconn(data, conn, &prot_missing, &user, &passwd, + &options); if(result != CURLE_OK) - return result; + goto out; /************************************************************* * No protocol part in URL was used, add it! @@ -4736,8 +5173,8 @@ static CURLcode create_conn(struct SessionHandle *data, reurl = aprintf("%s://%s", conn->handler->scheme, data->change.url); if(!reurl) { - Curl_safefree(proxy); - return CURLE_OUT_OF_MEMORY; + result = CURLE_OUT_OF_MEMORY; + goto out; } if(data->change.url_alloc) { @@ -4767,6 +5204,13 @@ static CURLcode create_conn(struct SessionHandle *data, } } + if(data->set.str[STRING_BEARER]) { + conn->xoauth2_bearer = strdup(data->set.str[STRING_BEARER]); + if(!conn->xoauth2_bearer) { + return CURLE_OUT_OF_MEMORY; + } + } + #ifndef CURL_DISABLE_PROXY /************************************************************* * Extract the user and password from the authentication string @@ -4774,7 +5218,7 @@ static CURLcode create_conn(struct SessionHandle *data, if(conn->bits.proxy_user_passwd) { result = parse_proxy_auth(data, conn); if(result != CURLE_OK) - return result; + goto out; } /************************************************************* @@ -4785,7 +5229,8 @@ static CURLcode create_conn(struct SessionHandle *data, /* if global proxy is set, this is it */ if(NULL == proxy) { failf(data, "memory shortage"); - return CURLE_OUT_OF_MEMORY; + result = CURLE_OUT_OF_MEMORY; + goto out; } } @@ -4813,16 +5258,17 @@ static CURLcode create_conn(struct SessionHandle *data, if(proxy) { result = parse_proxy(data, conn, proxy); - free(proxy); /* parse_proxy copies the proxy string */ + Curl_safefree(proxy); /* parse_proxy copies the proxy string */ if(result) - return result; + goto out; if((conn->proxytype == CURLPROXY_HTTP) || (conn->proxytype == CURLPROXY_HTTP_1_0)) { #ifdef CURL_DISABLE_HTTP /* asking for a HTTP proxy is a bit funny when HTTP is disabled... */ - return CURLE_UNSUPPORTED_PROTOCOL; + result = CURLE_UNSUPPORTED_PROTOCOL; + goto out; #else /* force this connection's protocol to become HTTP if not already compatible - if it isn't tunneling through */ @@ -4848,14 +5294,35 @@ static CURLcode create_conn(struct SessionHandle *data, #endif /* CURL_DISABLE_PROXY */ /************************************************************* + * If the protocol is using SSL and HTTP proxy is used, we set + * the tunnel_proxy bit. + *************************************************************/ + if((conn->given->flags&PROTOPT_SSL) && conn->bits.httpproxy) + conn->bits.tunnel_proxy = TRUE; + + /************************************************************* + * Figure out the remote port number and fix it in the URL + *************************************************************/ + result = parse_remote_port(data, conn); + if(result != CURLE_OK) + goto out; + + /* Check for overridden login details and set them accordingly so they + they are known when protocol->setup_connection is called! */ + result = override_login(data, conn, &user, &passwd, &options); + if(result != CURLE_OK) + goto out; + result = set_login(conn, user, passwd, options); + if(result != CURLE_OK) + goto out; + + /************************************************************* * Setup internals depending on protocol. Needs to be done after * we figured out what/if proxy to use. *************************************************************/ result = setup_connection_internals(conn); - if(result != CURLE_OK) { - Curl_safefree(proxy); - return result; - } + if(result != CURLE_OK) + goto out; conn->recv[FIRSTSOCKET] = Curl_recv_plain; conn->send[FIRSTSOCKET] = Curl_send_plain; @@ -4888,40 +5355,20 @@ static CURLcode create_conn(struct SessionHandle *data, DEBUGASSERT(conn->handler->done); /* we ignore the return code for the protocol-specific DONE */ (void)conn->handler->done(conn, result, FALSE); - return result; + goto out; } Curl_setup_transfer(conn, -1, -1, FALSE, NULL, /* no download */ -1, NULL); /* no upload */ } - return result; + /* since we skip do_init() */ + do_init(conn); + + goto out; } #endif - /************************************************************* - * If the protocol is using SSL and HTTP proxy is used, we set - * the tunnel_proxy bit. - *************************************************************/ - if((conn->given->flags&PROTOPT_SSL) && conn->bits.httpproxy) - conn->bits.tunnel_proxy = TRUE; - - /************************************************************* - * Figure out the remote port number and fix it in the URL - *************************************************************/ - result = parse_remote_port(data, conn); - if(result != CURLE_OK) - return result; - - /************************************************************* - * Check for an overridden user name and password, then set it - * for use - *************************************************************/ - override_userpass(data, conn, user, passwd); - result = set_userpass(conn, user, passwd); - if(result != CURLE_OK) - return result; - /* Get a cloned copy of the SSL config situation stored in the connection struct. But to get this going nicely, we must first make sure that the strings in the master copy are pointing to the correct @@ -4943,8 +5390,10 @@ static CURLcode create_conn(struct SessionHandle *data, data->set.ssl.password = data->set.str[STRING_TLSAUTH_PASSWORD]; #endif - if(!Curl_clone_ssl_config(&data->set.ssl, &conn->ssl_config)) - return CURLE_OUT_OF_MEMORY; + if(!Curl_clone_ssl_config(&data->set.ssl, &conn->ssl_config)) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } /************************************************************* * Check the current list of connections to see if we can @@ -4959,7 +5408,25 @@ static CURLcode create_conn(struct SessionHandle *data, if(data->set.reuse_fresh && !data->state.this_is_a_follow) reuse = FALSE; else - reuse = ConnectionExists(data, conn, &conn_temp); + reuse = ConnectionExists(data, conn, &conn_temp, &force_reuse); + + /* If we found a reusable connection, we may still want to + open a new connection if we are pipelining. */ + if(reuse && !force_reuse && IsPipeliningPossible(data, conn_temp)) { + size_t pipelen = conn_temp->send_pipe->size + conn_temp->recv_pipe->size; + if(pipelen > 0) { + infof(data, "Found connection %ld, with requests in the pipe (%zu)\n", + conn_temp->connection_id, pipelen); + + if(conn_temp->bundle->num_connections < max_host_connections && + data->state.conn_cache->num_connections < max_total_connections) { + /* We want a new connection anyway */ + reuse = FALSE; + + infof(data, "We can reuse, but we want a new connection anyway\n"); + } + } + } if(reuse) { /* @@ -4968,6 +5435,8 @@ static CURLcode create_conn(struct SessionHandle *data, * just allocated before we can move along and use the previously * existing one. */ + conn_temp->inuse = TRUE; /* mark this as being in use so that no other + handle in a multi stack may nick it */ reuse_conn(conn, conn_temp); free(conn); /* we don't need this anymore */ conn = conn_temp; @@ -4981,14 +5450,67 @@ static CURLcode create_conn(struct SessionHandle *data, conn->proxy.name?conn->proxy.dispname:conn->host.dispname); } else { - /* - * This is a brand new connection, so let's store it in the connection - * cache of ours! - */ - conn->inuse = TRUE; - ConnectionStore(data, conn); + /* We have decided that we want a new connection. However, we may not + be able to do that if we have reached the limit of how many + connections we are allowed to open. */ + struct connectbundle *bundle; + + bundle = Curl_conncache_find_bundle(data->state.conn_cache, + conn->host.name); + if(max_host_connections > 0 && bundle && + (bundle->num_connections >= max_host_connections)) { + struct connectdata *conn_candidate; + + /* The bundle is full. Let's see if we can kill a connection. */ + conn_candidate = find_oldest_idle_connection_in_bundle(data, bundle); + + if(conn_candidate) { + /* Set the connection's owner correctly, then kill it */ + conn_candidate->data = data; + (void)Curl_disconnect(conn_candidate, /* dead_connection */ FALSE); + } + else + no_connections_available = TRUE; + } + + if(max_total_connections > 0 && + (data->state.conn_cache->num_connections >= max_total_connections)) { + struct connectdata *conn_candidate; + + /* The cache is full. Let's see if we can kill a connection. */ + conn_candidate = find_oldest_idle_connection(data); + + if(conn_candidate) { + /* Set the connection's owner correctly, then kill it */ + conn_candidate->data = data; + (void)Curl_disconnect(conn_candidate, /* dead_connection */ FALSE); + } + else + no_connections_available = TRUE; + } + + + if(no_connections_available) { + infof(data, "No connections available.\n"); + + conn_free(conn); + *in_connect = NULL; + + result = CURLE_NO_CONNECTION_AVAILABLE; + goto out; + } + else { + /* + * This is a brand new connection, so let's store it in the connection + * cache of ours! + */ + ConnectionStore(data, conn); + } } + /* Mark the connection as used */ + conn->inuse = TRUE; + /* Setup and init stuff before DO starts, in preparing for the transfer. */ do_init(conn); @@ -4997,7 +5519,7 @@ static CURLcode create_conn(struct SessionHandle *data, */ result = setup_range(data); if(result) - return result; + goto out; /* Continue connectdata initialization here. */ @@ -5015,6 +5537,12 @@ static CURLcode create_conn(struct SessionHandle *data, *************************************************************/ result = resolve_server(data, conn, async); + out: + + Curl_safefree(options); + Curl_safefree(passwd); + Curl_safefree(user); + Curl_safefree(proxy); return result; } @@ -5163,6 +5691,11 @@ CURLcode Curl_connect(struct SessionHandle *data, } } + if(code == CURLE_NO_CONNECTION_AVAILABLE) { + *in_connect = NULL; + return code; + } + if(code && *in_connect) { /* We're not allowed to return failure with memory left allocated in the connectdata struct, free those here */ @@ -5219,6 +5752,19 @@ CURLcode Curl_done(struct connectdata **connp, conn->dns_entry = NULL; } + switch(status) { + case CURLE_ABORTED_BY_CALLBACK: + case CURLE_READ_ERROR: + case CURLE_WRITE_ERROR: + /* When we're aborted due to a callback return code it basically have to + be counted as premature as there is trouble ahead if we don't. We have + many callbacks and protocols work differently, we could potentially do + this more fine-grained in the future. */ + premature = TRUE; + default: + break; + } + /* this calls the protocol-specific function pointer previously set */ if(conn->handler->done) result = conn->handler->done(conn, status, premature); @@ -5247,12 +5793,8 @@ CURLcode Curl_done(struct connectdata **connp, state it is for re-using, so we're forced to close it. In a perfect world we can add code that keep track of if we really must close it here or not, but currently we have no such detail knowledge. - - connection_id == -1 here means that the connection has not been added - to the connection cache (OOM) and thus we must disconnect it here. */ - if(data->set.reuse_forbid || conn->bits.close || premature || - (-1 == conn->connection_id)) { + if(data->set.reuse_forbid || conn->bits.close || premature) { CURLcode res2 = Curl_disconnect(conn, premature); /* close connection */ /* If we had an error already, make sure we return that one. But @@ -5278,6 +5820,7 @@ CURLcode Curl_done(struct connectdata **connp, this was either closed or handed over to the connection cache here, and therefore cannot be used from this point on */ + Curl_free_request_state(data); return result; } @@ -5310,9 +5853,6 @@ static CURLcode do_init(struct connectdata *conn) HTTP. */ data->set.httpreq = HTTPREQ_GET; - /* NB: the content encoding software depends on this initialization */ - Curl_easy_initHandleData(data); - k->start = Curl_tvnow(); /* start time */ k->now = k->start; /* current time is now */ k->header = TRUE; /* assume header */ @@ -5391,33 +5931,23 @@ CURLcode Curl_do(struct connectdata **connp, bool *done) * * TODO: A future libcurl should be able to work away this state. * + * 'complete' can return 0 for incomplete, 1 for done and -1 for go back to + * DOING state there's more work to do! */ -CURLcode Curl_do_more(struct connectdata *conn, bool *completed) +CURLcode Curl_do_more(struct connectdata *conn, int *complete) { CURLcode result=CURLE_OK; - *completed = FALSE; + *complete = 0; if(conn->handler->do_more) - result = conn->handler->do_more(conn, completed); + result = conn->handler->do_more(conn, complete); - if(!result && *completed) + if(!result && (*complete == 1)) /* do_complete must be called after the protocol-specific DO function */ do_complete(conn); return result; } -/* Called on connect, and if there's already a protocol-specific struct - allocated for a different connection, this frees it that it can be setup - properly later on. */ -void Curl_reset_reqproto(struct connectdata *conn) -{ - struct SessionHandle *data = conn->data; - if(data->state.proto.generic && data->state.current_conn != conn) { - free(data->state.proto.generic); - data->state.proto.generic = NULL; - } - data->state.current_conn = conn; -} diff --git a/plugins/FTPFileYM/curl/lib/url.h b/plugins/FTPFileYM/curl/lib/url.h index a026e90e85..418413c484 100644 --- a/plugins/FTPFileYM/curl/lib/url.h +++ b/plugins/FTPFileYM/curl/lib/url.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2011, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2013, 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 @@ -37,7 +37,7 @@ CURLcode Curl_close(struct SessionHandle *data); /* opposite of curl_open() */ CURLcode Curl_connect(struct SessionHandle *, struct connectdata **, bool *async, bool *protocol_connect); CURLcode Curl_do(struct connectdata **, bool *done); -CURLcode Curl_do_more(struct connectdata *, bool *completed); +CURLcode Curl_do_more(struct connectdata *, int *completed); CURLcode Curl_done(struct connectdata **, CURLcode, bool premature); CURLcode Curl_disconnect(struct connectdata *, bool dead_connection); CURLcode Curl_protocol_connect(struct connectdata *conn, bool *done); @@ -45,6 +45,7 @@ CURLcode Curl_protocol_connecting(struct connectdata *conn, bool *done); CURLcode Curl_protocol_doing(struct connectdata *conn, bool *done); CURLcode Curl_setup_conn(struct connectdata *conn, bool *protocol_done); +void Curl_free_request_state(struct SessionHandle *data); int Curl_protocol_getsock(struct connectdata *conn, curl_socket_t *socks, @@ -65,11 +66,6 @@ void Curl_getoff_all_pipelines(struct SessionHandle *data, void Curl_close_connections(struct SessionHandle *data); -/* Called on connect, and if there's already a protocol-specific struct - allocated for a different connection, this frees it that it can be setup - properly later on. */ -void Curl_reset_reqproto(struct connectdata *conn); - #define CURL_DEFAULT_PROXY_PORT 1080 /* default proxy port unless specified */ #define CURL_DEFAULT_SOCKS5_GSSAPI_SERVICE "rcmd" /* default socks5 gssapi service */ diff --git a/plugins/FTPFileYM/curl/lib/urldata.h b/plugins/FTPFileYM/curl/lib/urldata.h index 7a275da5ee..ebaaf6ff4f 100644 --- a/plugins/FTPFileYM/curl/lib/urldata.h +++ b/plugins/FTPFileYM/curl/lib/urldata.h @@ -58,6 +58,8 @@ #define CURL_DEFAULT_USER "anonymous" #define CURL_DEFAULT_PASSWORD "ftp@example.com" +#define DEFAULT_CONNCACHE_SIZE 5 + /* length of longest IPv6 address string including the trailing null */ #define MAX_IPADR_LEN sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255") @@ -107,9 +109,15 @@ #endif #ifdef USE_POLARSSL -#include <polarssl/havege.h> #include <polarssl/ssl.h> -#endif +#include <polarssl/version.h> +#if POLARSSL_VERSION_NUMBER<0x01010000 +#include <polarssl/havege.h> +#else +#include <polarssl/entropy.h> +#include <polarssl/ctr_drbg.h> +#endif /* POLARSSL_VERSION_NUMBER<0x01010000 */ +#endif /* USE_POLARSSL */ #ifdef USE_CYASSL #undef OCSP_REQUEST /* avoid cyassl/openssl/ssl.h clash with wincrypt.h */ @@ -126,6 +134,10 @@ #include <qsossl.h> #endif +#ifdef USE_GSKIT +#include <gskssl.h> +#endif + #ifdef USE_AXTLS #include <axTLS/ssl.h> #undef malloc @@ -176,6 +188,7 @@ #include "http.h" #include "rtsp.h" #include "wildcard.h" +#include "multihandle.h" #ifdef HAVE_GSSAPI # ifdef HAVE_GSSGNU @@ -209,9 +222,9 @@ #define CURLMIN(x,y) ((x)<(y)?(x):(y)) -#if defined(HAVE_KRB4) || defined(HAVE_GSSAPI) -/* Types needed for krb4/5-ftp connections */ -struct krb4buffer { +#ifdef HAVE_GSSAPI +/* Types needed for krb5-ftp connections */ +struct krb5buffer { void *data; size_t size; size_t index; @@ -235,6 +248,7 @@ struct curl_schannel_cred { CredHandle cred_handle; TimeStamp time_stamp; int refcount; + bool cached; }; struct curl_schannel_ctxt { @@ -282,7 +296,13 @@ struct ssl_connect_data { ssl_connect_state connecting_state; #endif /* USE_GNUTLS */ #ifdef USE_POLARSSL +#if POLARSSL_VERSION_NUMBER<0x01010000 havege_state hs; +#else + /* from v1.1.0, use ctr_drbg and entropy */ + ctr_drbg_context ctr_drbg; + entropy_context entropy; +#endif /* POLARSSL_VERSION_NUMBER<0x01010000 */ ssl_context ssl; ssl_session ssn; int server_fd; @@ -307,9 +327,15 @@ struct ssl_connect_data { #ifdef USE_QSOSSL SSLHandle *handle; #endif /* USE_QSOSSL */ +#ifdef USE_GSKIT + gsk_handle handle; + int iocport; + ssl_connect_state connecting_state; +#endif #ifdef USE_AXTLS SSL_CTX* ssl_ctx; SSL* ssl; + ssl_connect_state connecting_state; #endif /* USE_AXTLS */ #ifdef USE_SCHANNEL struct curl_schannel_cred *cred; @@ -326,6 +352,7 @@ struct ssl_connect_data { curl_socket_t ssl_sockfd; ssl_connect_state connecting_state; bool ssl_direction; /* true if writing, false if reading */ + size_t ssl_write_buffered_length; #endif /* USE_DARWINSSL */ }; @@ -550,7 +577,7 @@ struct Curl_async { /* These function pointer types are here only to allow easier typecasting within the source when we need to cast between data pointers (such as NULL) and function pointers. */ -typedef CURLcode (*Curl_do_more_func)(struct connectdata *, bool *); +typedef CURLcode (*Curl_do_more_func)(struct connectdata *, int *); typedef CURLcode (*Curl_done_func)(struct connectdata *, CURLcode, bool); @@ -665,6 +692,9 @@ struct SingleRequest { bool forbidchunk; /* used only to explicitly forbid chunk-upload for specific upload buffers. See readmoredata() in http.c for details. */ + + void *protop; /* Allocated protocol-specific data. Each protocol + handler makes sure this points to data it needs. */ }; /* @@ -844,6 +874,9 @@ struct connectdata { char *user; /* user name string, allocated */ char *passwd; /* password string, allocated */ + char *options; /* options string, allocated */ + + char *xoauth2_bearer; /* bearer token for xoauth2, allocated */ char *proxyuser; /* proxy user name string, allocated */ char *proxypasswd; /* proxy password string, allocated */ @@ -903,12 +936,12 @@ struct connectdata { } allocptr; int sec_complete; /* if kerberos is enabled for this connection */ -#if defined(HAVE_KRB4) || defined(HAVE_GSSAPI) +#ifdef HAVE_GSSAPI enum protection_level command_prot; enum protection_level data_prot; enum protection_level request_data_prot; size_t buffer_size; - struct krb4buffer in_buffer; + struct krb5buffer in_buffer; void *app_data; const struct Curl_sec_client_mech *mech; struct sockaddr_in local_addr; @@ -922,17 +955,10 @@ struct connectdata { handle */ bool writechannel_inuse; /* whether the write channel is in use by an easy handle */ - bool server_supports_pipelining; /* TRUE if server supports pipelining, - set after first response */ struct curl_llist *send_pipe; /* List of handles waiting to send on this pipeline */ struct curl_llist *recv_pipe; /* List of handles waiting to read their responses on this pipeline */ - struct curl_llist *pend_pipe; /* List of pending handles on - this pipeline */ - struct curl_llist *done_pipe; /* Handles that are finished, but - still reference this connectdata */ -#define MAX_PIPELINE_LENGTH 5 char* master_buffer; /* The master buffer allocated on-demand; used for pipelining. */ size_t read_pos; /* Current read position in the master buffer */ @@ -975,13 +1001,14 @@ struct connectdata { union { struct ftp_conn ftpc; + struct http_conn httpc; struct ssh_conn sshc; struct tftp_state_data *tftpc; struct imap_conn imapc; struct pop3_conn pop3c; struct smtp_conn smtpc; struct rtsp_conn rtspc; - void *generic; + void *generic; /* RTMP and LDAP use this */ } proto; int cselect_bits; /* bitmask of socket events */ @@ -1009,8 +1036,7 @@ struct connectdata { TUNNEL_CONNECT, /* CONNECT has been sent off */ TUNNEL_COMPLETE /* CONNECT response received completely */ } tunnel_state[2]; /* two separate ones to allow FTP */ - - struct connectbundle *bundle; /* The bundle we are member of */ + struct connectbundle *bundle; /* The bundle we are member of */ }; /* The end of connectdata. */ @@ -1129,8 +1155,6 @@ typedef enum { * Session-data MUST be put in the connectdata struct and here. */ #define MAX_CURL_USER_LENGTH 256 #define MAX_CURL_PASSWORD_LENGTH 256 -#define MAX_CURL_USER_LENGTH_TXT "255" -#define MAX_CURL_PASSWORD_LENGTH_TXT "255" struct auth { unsigned long want; /* Bitmask set to the authentication methods wanted by @@ -1159,13 +1183,6 @@ struct UrlState { /* buffers to store authentication data in, as parsed from input options */ struct timeval keeps_speed; /* for the progress meter really */ - struct connectdata *pending_conn; /* This points to the connection we want - to open when we are waiting in the - CONNECT_PEND state in the multi - interface. This to avoid recreating it - when we enter the CONNECT state again. - */ - struct connectdata *lastconnect; /* The last connection, NULL if undefined */ char *headerbuff; /* allocated buffer to store headers in */ @@ -1262,30 +1279,6 @@ struct UrlState { long rtsp_next_server_CSeq; /* the session's next server CSeq */ long rtsp_CSeq_recv; /* most recent CSeq received */ - /* Protocol specific data. - * - ************************************************************************* - * Note that this data will be REMOVED after each request, so anything that - * should be kept/stored on a per-connection basis and thus live for the - * next request on the same connection MUST be put in the connectdata struct! - *************************************************************************/ - union { - struct HTTP *http; - struct HTTP *https; /* alias, just for the sake of being more readable */ - struct RTSP *rtsp; - struct FTP *ftp; - /* void *tftp; not used */ - struct FILEPROTO *file; - void *telnet; /* private for telnet.c-eyes only */ - void *generic; - struct SSHPROTO *ssh; - struct FTP *imap; - struct FTP *pop3; - struct FTP *smtp; - } proto; - /* current user of this SessionHandle instance, or NULL */ - struct connectdata *current_conn; - /* if true, force SSL connection retry (workaround for certain servers) */ bool ssl_connect_retry; }; @@ -1317,7 +1310,7 @@ struct DynamicStatic { * the 'DynamicStatic' struct. * Character pointer fields point to dynamic storage, unless otherwise stated. */ -struct Curl_one_easy; /* declared and used only in multi.c */ + struct Curl_multi; /* declared and used only in multi.c */ enum dupstring { @@ -1352,6 +1345,7 @@ enum dupstring { STRING_SSL_ISSUERCERT, /* issuer cert file to check certificate */ STRING_USERNAME, /* <username>, if used */ STRING_PASSWORD, /* <password>, if used */ + STRING_OPTIONS, /* <options>, if used */ STRING_PROXYUSERNAME, /* Proxy <username>, if used */ STRING_PROXYPASSWORD, /* Proxy <password>, if used */ STRING_NOPROXY, /* List of hosts which should not use the proxy, if @@ -1376,6 +1370,8 @@ enum dupstring { STRING_TLSAUTH_PASSWORD, /* TLS auth <password> */ #endif + STRING_BEARER, /* <bearer>, if used */ + /* -- end of strings -- */ STRING_LAST /* not used, just an end-of-list marker */ }; @@ -1416,7 +1412,8 @@ struct UserDefined { curl_read_callback fread_func; /* function that reads the input */ int is_fread_set; /* boolean, has read callback been set to non-NULL? */ int is_fwrite_set; /* boolean, has write callback been set to non-NULL? */ - curl_progress_callback fprogress; /* function for progress information */ + curl_progress_callback fprogress; /* OLD and deprecated progress callback */ + curl_xferinfo_callback fxferinfo; /* progress callback */ curl_debug_callback fdebug; /* function that write informational data */ curl_ioctl_callback ioctl_func; /* function for I/O control */ curl_sockopt_callback fsockopt; /* function for setting socket options */ @@ -1478,12 +1475,6 @@ struct UserDefined { long buffer_size; /* size of receive buffer to use */ void *private_data; /* application-private data */ - struct Curl_one_easy *one_easy; /* When adding an easy handle to a multi - handle, an internal 'Curl_one_easy' - struct is created and this is a pointer - to the particular struct associated with - this SessionHandle */ - struct curl_slist *http200aliases; /* linked list of aliases for http200 */ long ipver; /* the CURL_IPRESOLVE_* defines in the public header file @@ -1519,7 +1510,7 @@ struct UserDefined { bool include_header; /* include received protocol headers in data output */ bool http_set_referer; /* is a custom referer used */ bool http_auto_referer; /* set "correct" referer when following location: */ - bool opt_no_body; /* as set with CURLOPT_NO_BODY */ + bool opt_no_body; /* as set with CURLOPT_NOBODY */ bool set_port; /* custom port number used */ bool upload; /* upload request */ enum CURL_NETRC_OPTION @@ -1562,6 +1553,7 @@ struct UserDefined { long socks5_gssapi_nec; /* flag to support nec socks5 server */ #endif struct curl_slist *mail_rcpt; /* linked list of mail recipients */ + bool sasl_ir; /* Enable/disable SASL initial response */ /* Common RTSP header options */ Curl_RtspReq rtspreq; /* RTSP request type */ long rtspversion; /* like httpversion, for RTSP */ @@ -1605,6 +1597,24 @@ struct Names { */ struct SessionHandle { + /* first, two fields for the linked list of these */ + struct SessionHandle *next; + struct SessionHandle *prev; + + struct connectdata *easy_conn; /* the "unit's" connection */ + + CURLMstate mstate; /* the handle's state */ + CURLcode result; /* previous result */ + + struct Curl_message msg; /* A single posted message. */ + + /* Array with the plain socket numbers this handle takes care of, in no + particular order. Note that all sockets are added to the sockhash, where + the state etc are also kept. This array is mostly used to detect when a + socket is to be removed from the hash. See singlesocket(). */ + curl_socket_t sockets[MAX_SOCKSPEREASYHANDLE]; + int numsocks; + struct Names dns; struct Curl_multi *multi; /* if non-NULL, points to the multi handle struct to which this "belongs" when used by @@ -1612,9 +1622,6 @@ struct SessionHandle { struct Curl_multi *multi_easy; /* if non-NULL, points to the multi handle struct to which this "belongs" when used by the easy interface */ - struct Curl_one_easy *multi_pos; /* if non-NULL, points to its position - in multi controlling structure to assist - in removal. */ struct Curl_share *share; /* Share, handles global variable mutexing */ struct SingleRequest req; /* Request-specific data */ struct UserDefined set; /* values set by the libcurl user */ diff --git a/plugins/FTPFileYM/curl/lib/vc6libcurl_10.vcxproj b/plugins/FTPFileYM/curl/lib/vc6libcurl_10.vcxproj index bdda68844d..b72e10649e 100644 --- a/plugins/FTPFileYM/curl/lib/vc6libcurl_10.vcxproj +++ b/plugins/FTPFileYM/curl/lib/vc6libcurl_10.vcxproj @@ -178,6 +178,7 @@ <ClCompile Include="curl_threads.c" />
<ClCompile Include="cyassl.c" />
<ClCompile Include="dict.c" />
+ <ClCompile Include="dotdot.c" />
<ClCompile Include="easy.c" />
<ClCompile Include="escape.c" />
<ClCompile Include="file.c" />
@@ -224,6 +225,7 @@ <ClCompile Include="openldap.c" />
<ClCompile Include="parsedate.c" />
<ClCompile Include="pingpong.c" />
+ <ClCompile Include="pipeline.c" />
<ClCompile Include="polarssl.c" />
<ClCompile Include="pop3.c" />
<ClCompile Include="progress.c" />
@@ -297,6 +299,7 @@ <ClInclude Include="curlx.h" />
<ClInclude Include="cyassl.h" />
<ClInclude Include="dict.h" />
+ <ClInclude Include="dotdot.h" />
<ClInclude Include="easyif.h" />
<ClInclude Include="escape.h" />
<ClInclude Include="file.h" />
@@ -330,6 +333,7 @@ <ClInclude Include="nssg.h" />
<ClInclude Include="parsedate.h" />
<ClInclude Include="pingpong.h" />
+ <ClInclude Include="pipeline.h" />
<ClInclude Include="polarssl.h" />
<ClInclude Include="pop3.h" />
<ClInclude Include="progress.h" />
diff --git a/plugins/FTPFileYM/curl/lib/vc6libcurl_10.vcxproj.filters b/plugins/FTPFileYM/curl/lib/vc6libcurl_10.vcxproj.filters index a67028ea0f..ed5d184c6c 100644 --- a/plugins/FTPFileYM/curl/lib/vc6libcurl_10.vcxproj.filters +++ b/plugins/FTPFileYM/curl/lib/vc6libcurl_10.vcxproj.filters @@ -336,6 +336,12 @@ <ClCompile Include="wildcard.c">
<Filter>Source Files</Filter>
</ClCompile>
+ <ClCompile Include="pipeline.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="dotdot.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="amigaos.h">
@@ -647,6 +653,12 @@ <ClInclude Include="wildcard.h">
<Filter>Header Files</Filter>
</ClInclude>
+ <ClInclude Include="pipeline.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="dotdot.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="libcurl.rc">
diff --git a/plugins/FTPFileYM/curl/lib/vc6libcurl_11.vcxproj b/plugins/FTPFileYM/curl/lib/vc6libcurl_11.vcxproj index d8339c8d61..e4aa8eac19 100644 --- a/plugins/FTPFileYM/curl/lib/vc6libcurl_11.vcxproj +++ b/plugins/FTPFileYM/curl/lib/vc6libcurl_11.vcxproj @@ -180,6 +180,7 @@ <ClCompile Include="curl_threads.c" />
<ClCompile Include="cyassl.c" />
<ClCompile Include="dict.c" />
+ <ClCompile Include="dotdot.c" />
<ClCompile Include="easy.c" />
<ClCompile Include="escape.c" />
<ClCompile Include="file.c" />
@@ -226,6 +227,7 @@ <ClCompile Include="openldap.c" />
<ClCompile Include="parsedate.c" />
<ClCompile Include="pingpong.c" />
+ <ClCompile Include="pipeline.c" />
<ClCompile Include="polarssl.c" />
<ClCompile Include="pop3.c" />
<ClCompile Include="progress.c" />
@@ -299,6 +301,7 @@ <ClInclude Include="curlx.h" />
<ClInclude Include="cyassl.h" />
<ClInclude Include="dict.h" />
+ <ClInclude Include="dotdot.h" />
<ClInclude Include="easyif.h" />
<ClInclude Include="escape.h" />
<ClInclude Include="file.h" />
@@ -332,6 +335,7 @@ <ClInclude Include="nssg.h" />
<ClInclude Include="parsedate.h" />
<ClInclude Include="pingpong.h" />
+ <ClInclude Include="pipeline.h" />
<ClInclude Include="polarssl.h" />
<ClInclude Include="pop3.h" />
<ClInclude Include="progress.h" />
diff --git a/plugins/FTPFileYM/curl/lib/vc6libcurl_11.vcxproj.filters b/plugins/FTPFileYM/curl/lib/vc6libcurl_11.vcxproj.filters index a67028ea0f..faaafe1652 100644 --- a/plugins/FTPFileYM/curl/lib/vc6libcurl_11.vcxproj.filters +++ b/plugins/FTPFileYM/curl/lib/vc6libcurl_11.vcxproj.filters @@ -336,6 +336,12 @@ <ClCompile Include="wildcard.c">
<Filter>Source Files</Filter>
</ClCompile>
+ <ClCompile Include="dotdot.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="pipeline.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="amigaos.h">
@@ -647,6 +653,12 @@ <ClInclude Include="wildcard.h">
<Filter>Header Files</Filter>
</ClInclude>
+ <ClInclude Include="dotdot.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="pipeline.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="libcurl.rc">
diff --git a/plugins/FTPFileYM/curl/lib/vc6libcurl_12.vcxproj b/plugins/FTPFileYM/curl/lib/vc6libcurl_12.vcxproj index 6439b33a95..6fd48fd650 100644 --- a/plugins/FTPFileYM/curl/lib/vc6libcurl_12.vcxproj +++ b/plugins/FTPFileYM/curl/lib/vc6libcurl_12.vcxproj @@ -180,6 +180,7 @@ <ClCompile Include="curl_threads.c" />
<ClCompile Include="cyassl.c" />
<ClCompile Include="dict.c" />
+ <ClCompile Include="dotdot.c" />
<ClCompile Include="easy.c" />
<ClCompile Include="escape.c" />
<ClCompile Include="file.c" />
@@ -226,6 +227,7 @@ <ClCompile Include="openldap.c" />
<ClCompile Include="parsedate.c" />
<ClCompile Include="pingpong.c" />
+ <ClCompile Include="pipeline.c" />
<ClCompile Include="polarssl.c" />
<ClCompile Include="pop3.c" />
<ClCompile Include="progress.c" />
@@ -299,6 +301,7 @@ <ClInclude Include="curlx.h" />
<ClInclude Include="cyassl.h" />
<ClInclude Include="dict.h" />
+ <ClInclude Include="dotdot.h" />
<ClInclude Include="easyif.h" />
<ClInclude Include="escape.h" />
<ClInclude Include="file.h" />
@@ -332,6 +335,7 @@ <ClInclude Include="nssg.h" />
<ClInclude Include="parsedate.h" />
<ClInclude Include="pingpong.h" />
+ <ClInclude Include="pipeline.h" />
<ClInclude Include="polarssl.h" />
<ClInclude Include="pop3.h" />
<ClInclude Include="progress.h" />
diff --git a/plugins/FTPFileYM/curl/lib/vc6libcurl_12.vcxproj.filters b/plugins/FTPFileYM/curl/lib/vc6libcurl_12.vcxproj.filters index a67028ea0f..faaafe1652 100644 --- a/plugins/FTPFileYM/curl/lib/vc6libcurl_12.vcxproj.filters +++ b/plugins/FTPFileYM/curl/lib/vc6libcurl_12.vcxproj.filters @@ -336,6 +336,12 @@ <ClCompile Include="wildcard.c">
<Filter>Source Files</Filter>
</ClCompile>
+ <ClCompile Include="dotdot.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="pipeline.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="amigaos.h">
@@ -647,6 +653,12 @@ <ClInclude Include="wildcard.h">
<Filter>Header Files</Filter>
</ClInclude>
+ <ClInclude Include="dotdot.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="pipeline.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="libcurl.rc">
diff --git a/plugins/FTPFileYM/curl/lib/version.c b/plugins/FTPFileYM/curl/lib/version.c index d39fe0c1d0..1f62131eba 100644 --- a/plugins/FTPFileYM/curl/lib/version.c +++ b/plugins/FTPFileYM/curl/lib/version.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2012, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2013, 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 @@ -25,6 +25,7 @@ #include <curl/curl.h> #include "urldata.h" #include "sslgen.h" +#include "http2.h" #define _MPRINTF_REPLACE /* use the internal *printf() functions */ #include <curl/mprintf.h> @@ -122,6 +123,11 @@ char *curl_version(void) left -= len; ptr += len; #endif +#ifdef USE_NGHTTP2 + len = Curl_http2_ver(ptr, left); + left -= len; + ptr += len; +#endif #ifdef USE_LIBRTMP { char suff[2]; @@ -235,9 +241,6 @@ static curl_version_info_data version_info = { #ifdef ENABLE_IPV6 | CURL_VERSION_IPV6 #endif -#ifdef HAVE_KRB4 - | CURL_VERSION_KERBEROS4 -#endif #ifdef USE_SSL | CURL_VERSION_SSL #endif @@ -278,6 +281,9 @@ static curl_version_info_data version_info = { #if defined(USE_TLS_SRP) | CURL_VERSION_TLSAUTH_SRP #endif +#if defined(USE_NGHTTP2) + | CURL_VERSION_HTTP2 +#endif , NULL, /* ssl_version */ 0, /* ssl_version_num, this is kept at zero */ diff --git a/plugins/FTPFileYM/curl/lib/x509asn1.c b/plugins/FTPFileYM/curl/lib/x509asn1.c new file mode 100644 index 0000000000..94b89b2be0 --- /dev/null +++ b/plugins/FTPFileYM/curl/lib/x509asn1.c @@ -0,0 +1,1151 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2013, 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 + * are also available at http://curl.haxx.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ***************************************************************************/ + +#include "curl_setup.h" + +#if defined(USE_QSOSSL) || defined(USE_GSKIT) + +#include <curl/curl.h> +#include "urldata.h" +#include "strequal.h" +#include "hostcheck.h" +#include "sslgen.h" +#include "sendf.h" +#include "inet_pton.h" +#include "curl_base64.h" +#include "x509asn1.h" + +#define _MPRINTF_REPLACE /* use our functions only */ +#include <curl/mprintf.h> + +#include "curl_memory.h" +/* The last #include file should be: */ +#include "memdebug.h" + + +/* ASN.1 OIDs. */ +static const char cnOID[] = "2.5.4.3"; /* Common name. */ +static const char sanOID[] = "2.5.29.17"; /* Subject alternative name. */ + +static const curl_OID OIDtable[] = { + { "1.2.840.10040.4.1", "dsa" }, + { "1.2.840.10040.4.3", "dsa-with-sha1" }, + { "1.2.840.10045.2.1", "ecPublicKey" }, + { "1.2.840.10045.3.0.1", "c2pnb163v1" }, + { "1.2.840.10045.4.1", "ecdsa-with-SHA1" }, + { "1.2.840.10046.2.1", "dhpublicnumber" }, + { "1.2.840.113549.1.1.1", "rsaEncryption" }, + { "1.2.840.113549.1.1.2", "md2WithRSAEncryption" }, + { "1.2.840.113549.1.1.4", "md5WithRSAEncryption" }, + { "1.2.840.113549.1.1.5", "sha1WithRSAEncryption" }, + { "1.2.840.113549.1.1.10", "RSASSA-PSS" }, + { "1.2.840.113549.1.1.14", "sha224WithRSAEncryption" }, + { "1.2.840.113549.1.1.11", "sha256WithRSAEncryption" }, + { "1.2.840.113549.1.1.12", "sha384WithRSAEncryption" }, + { "1.2.840.113549.1.1.13", "sha512WithRSAEncryption" }, + { "1.2.840.113549.2.2", "md2" }, + { "1.2.840.113549.2.5", "md5" }, + { "1.3.14.3.2.26", "sha1" }, + { cnOID, "CN" }, + { "2.5.4.4", "SN" }, + { "2.5.4.5", "serialNumber" }, + { "2.5.4.6", "C" }, + { "2.5.4.7", "L" }, + { "2.5.4.8", "ST" }, + { "2.5.4.9", "streetAddress" }, + { "2.5.4.10", "O" }, + { "2.5.4.11", "OU" }, + { "2.5.4.12", "title" }, + { "2.5.4.13", "description" }, + { "2.5.4.17", "postalCode" }, + { "2.5.4.41", "name" }, + { "2.5.4.42", "givenName" }, + { "2.5.4.43", "initials" }, + { "2.5.4.44", "generationQualifier" }, + { "2.5.4.45", "X500UniqueIdentifier" }, + { "2.5.4.46", "dnQualifier" }, + { "2.5.4.65", "pseudonym" }, + { "1.2.840.113549.1.9.1", "emailAddress" }, + { "2.5.4.72", "role" }, + { sanOID, "subjectAltName" }, + { "2.5.29.18", "issuerAltName" }, + { "2.5.29.19", "basicConstraints" }, + { "2.16.840.1.101.3.4.2.4", "sha224" }, + { "2.16.840.1.101.3.4.2.1", "sha256" }, + { "2.16.840.1.101.3.4.2.2", "sha384" }, + { "2.16.840.1.101.3.4.2.3", "sha512" }, + { (const char *) NULL, (const char *) NULL } +}; + +/* + * Lightweight ASN.1 parser. + * In particular, it does not check for syntactic/lexical errors. + * It is intended to support certificate information gathering for SSL backends + * that offer a mean to get certificates as a whole, but do not supply + * entry points to get particular certificate sub-fields. + * Please note there is no pretention here to rewrite a full SSL library. + */ + + +const char * Curl_getASN1Element(curl_asn1Element * elem, + const char * beg, const char * end) +{ + unsigned char b; + unsigned long len; + curl_asn1Element lelem; + + /* Get a single ASN.1 element into `elem', parse ASN.1 string at `beg' + ending at `end'. + Returns a pointer in source string after the parsed element, or NULL + if an error occurs. */ + + if(beg >= end || !*beg) + return (const char *) NULL; + + /* Process header byte. */ + b = (unsigned char) *beg++; + elem->constructed = (b & 0x20) != 0; + elem->class = (b >> 6) & 3; + b &= 0x1F; + if(b == 0x1F) + return (const char *) NULL; /* Long tag values not supported here. */ + elem->tag = b; + + /* Process length. */ + if(beg >= end) + return (const char *) NULL; + b = (unsigned char) *beg++; + if(!(b & 0x80)) + len = b; + else if(!(b &= 0x7F)) { + /* Unspecified length. Since we have all the data, we can determine the + effective length by skipping element until an end element is found. */ + if(!elem->constructed) + return (const char *) NULL; + elem->beg = beg; + while(beg < end && *beg) { + beg = Curl_getASN1Element(&lelem, beg, end); + if(!beg) + return (const char *) NULL; + } + if(beg >= end) + return (const char *) NULL; + elem->end = beg; + return beg + 1; + } + else if(beg + b > end) + return (const char *) NULL; /* Does not fit in source. */ + else { + /* Get long length. */ + len = 0; + do { + if(len & 0xFF000000L) + return (const char *) NULL; /* Lengths > 32 bits are not supported. */ + len = (len << 8) | (unsigned char) *beg++; + } while(--b); + } + if((unsigned long) (end - beg) < len) + return (const char *) NULL; /* Element data does not fit in source. */ + elem->beg = beg; + elem->end = beg + len; + return elem->end; +} + +static const curl_OID * searchOID(const char * oid) +{ + const curl_OID * op; + + /* Search the null terminated OID or OID identifier in local table. + Return the table entry pointer or NULL if not found. */ + + for(op = OIDtable; op->numoid; op++) + if(!strcmp(op->numoid, oid) || curl_strequal(op->textoid, oid)) + return op; + + return (const curl_OID *) NULL; +} + +static const char * bool2str(const char * beg, const char * end) +{ + /* Convert an ASN.1 Boolean value into its string representation. + Return the dynamically allocated string, or NULL if source is not an + ASN.1 Boolean value. */ + + if(end - beg != 1) + return (const char *) NULL; + return strdup(*beg? "TRUE": "FALSE"); +} + +static const char * octet2str(const char * beg, const char * end) +{ + size_t n = end - beg; + char * buf; + + /* Convert an ASN.1 octet string to a printable string. + Return the dynamically allocated string, or NULL if an error occurs. */ + + buf = malloc(3 * n + 1); + if(buf) + for(n = 0; beg < end; n += 3) + snprintf(buf + n, 4, "%02x:", *(const unsigned char *) beg++); + return buf; +} + +static const char * bit2str(const char * beg, const char * end) + +{ + /* Convert an ASN.1 bit string to a printable string. + Return the dynamically allocated string, or NULL if an error occurs. */ + + if(++beg > end) + return (const char *) NULL; + return octet2str(beg, end); +} + +static const char * int2str(const char * beg, const char * end) +{ + long val = 0; + size_t n = end - beg; + + /* Convert an ASN.1 integer value into its string representation. + Return the dynamically allocated string, or NULL if source is not an + ASN.1 integer value. */ + + if(!n) + return (const char *) NULL; + + if(n > 4) + return octet2str(beg, end); + + /* Represent integers <= 32-bit as a single value. */ + if(*beg & 0x80) + val = ~val; + + do + val = (val << 8) | *(const unsigned char *) beg++; + while(beg < end); + return curl_maprintf("%s%lx", (val < 0 || val >= 10)? "0x": "", val); +} + +static ssize_t +utf8asn1str(char * * to, int type, const char * from, const char * end) +{ + size_t inlength = end - from; + int size = 1; + size_t outlength; + int chsize; + unsigned int wc; + char * buf; + + /* Perform a lazy conversion from an ASN.1 typed string to UTF8. Allocate the + destination buffer dynamically. The allocation size will normally be too + large: this is to avoid buffer overflows. + Terminate the string with a nul byte and return the converted + string length. */ + + *to = (char *) NULL; + switch (type) { + case CURL_ASN1_BMP_STRING: + size = 2; + break; + case CURL_ASN1_UNIVERSAL_STRING: + size = 4; + break; + case CURL_ASN1_NUMERIC_STRING: + case CURL_ASN1_PRINTABLE_STRING: + case CURL_ASN1_TELETEX_STRING: + case CURL_ASN1_IA5_STRING: + case CURL_ASN1_VISIBLE_STRING: + case CURL_ASN1_UTF8_STRING: + break; + default: + return -1; /* Conversion not supported. */ + } + + if(inlength % size) + return -1; /* Length inconsistent with character size. */ + buf = malloc(4 * (inlength / size) + 1); + if(!buf) + return -1; /* Not enough memory. */ + + if(type == CURL_ASN1_UTF8_STRING) { + /* Just copy. */ + outlength = inlength; + if(outlength) + memcpy(buf, from, outlength); + } + else { + for(outlength = 0; from < end;) { + wc = 0; + switch (size) { + case 4: + wc = (wc << 8) | *(const unsigned char *) from++; + wc = (wc << 8) | *(const unsigned char *) from++; + case 2: + wc = (wc << 8) | *(const unsigned char *) from++; + default: /* case 1: */ + wc = (wc << 8) | *(const unsigned char *) from++; + } + chsize = 1; + if(wc >= 0x00000080) { + if(wc >= 0x00000800) { + if(wc >= 0x00010000) { + if(wc >= 0x00200000) { + free(buf); + return -1; /* Invalid char. size for target encoding. */ + } + buf[outlength + 3] = (char) (0x80 | (wc & 0x3F)); + wc = (wc >> 6) | 0x00010000; + chsize++; + } + buf[outlength + 2] = (char) (0x80 | (wc & 0x3F)); + wc = (wc >> 6) | 0x00000800; + chsize++; + } + buf[outlength + 1] = (char) (0x80 | (wc & 0x3F)); + wc = (wc >> 6) | 0x000000C0; + chsize++; + } + buf[outlength] = (char) wc; + outlength += chsize; + } + } + buf[outlength] = '\0'; + *to = buf; + return outlength; +} + +static const char * string2str(int type, const char * beg, const char * end) +{ + char * buf; + + /* Convert an ASN.1 String into its UTF-8 string representation. + Return the dynamically allocated string, or NULL if an error occurs. */ + + if(utf8asn1str(&buf, type, beg, end) < 0) + return (const char *) NULL; + return buf; +} + +static int encodeUint(char * buf, int n, unsigned int x) +{ + int i = 0; + unsigned int y = x / 10; + + /* Decimal ASCII encode unsigned integer `x' in the `n'-byte buffer at `buf'. + Return the total number of encoded digits, even if larger than `n'. */ + + if(y) { + i += encodeUint(buf, n, y); + x -= y * 10; + } + if(i < n) + buf[i] = (char) ('0' + x); + i++; + if(i < n) + buf[i] = '\0'; /* Store a terminator if possible. */ + return i; +} + +static int encodeOID(char * buf, int n, const char * beg, const char * end) +{ + int i = 0; + unsigned int x; + unsigned int y; + + /* Convert an ASN.1 OID into its dotted string representation. + Store the result in th `n'-byte buffer at `buf'. + Return the converted string length, or -1 if an error occurs. */ + + /* Process the first two numbers. */ + y = *(const unsigned char *) beg++; + x = y / 40; + y -= x * 40; + i += encodeUint(buf + i, n - i, x); + if(i < n) + buf[i] = '.'; + i++; + i += encodeUint(buf + i, n - i, y); + + /* Process the trailing numbers. */ + while(beg < end) { + if(i < n) + buf[i] = '.'; + i++; + x = 0; + do { + if(x & 0xFF000000) + return -1; + y = *(const unsigned char *) beg++; + x = (x << 7) | (y & 0x7F); + } while(y & 0x80); + i += encodeUint(buf + i, n - i, x); + } + if(i < n) + buf[i] = '\0'; + return i; +} + +static const char * OID2str(const char * beg, const char * end, bool symbolic) +{ + char * buf = (char *) NULL; + const curl_OID * op; + int n; + + /* Convert an ASN.1 OID into its dotted or symbolic string representation. + Return the dynamically allocated string, or NULL if an error occurs. */ + + if(beg < end) { + n = encodeOID((char *) NULL, -1, beg, end); + if(n >= 0) { + buf = malloc(n + 1); + if(buf) { + encodeOID(buf, n, beg, end); + buf[n] = '\0'; + + if(symbolic) { + op = searchOID(buf); + if(op) { + free(buf); + buf = strdup(op->textoid); + } + } + } + } + } + return buf; +} + +static const char * GTime2str(const char * beg, const char * end) +{ + const char * tzp; + const char * fracp; + char sec1, sec2; + size_t fracl; + size_t tzl; + const char * sep = ""; + + /* Convert an ASN.1 Generalized time to a printable string. + Return the dynamically allocated string, or NULL if an error occurs. */ + + for(fracp = beg; fracp < end && *fracp >= '0' && *fracp <= '9'; fracp++) + ; + + /* Get seconds digits. */ + sec1 = '0'; + switch (fracp - beg - 12) { + case 0: + sec2 = '0'; + break; + case 2: + sec1 = fracp[-2]; + case 1: + sec2 = fracp[-1]; + break; + default: + return (const char *) NULL; + } + + /* Scan for timezone, measure fractional seconds. */ + tzp = fracp; + fracl = 0; + if(fracp < end && (*fracp == '.' || *fracp == ',')) { + fracp++; + do + tzp++; + while(tzp < end && *tzp >= '0' && *tzp <= '9'); + /* Strip leading zeroes in fractional seconds. */ + for(fracl = tzp - fracp - 1; fracl && fracp[fracl - 1] == '0'; fracl--) + ; + } + + /* Process timezone. */ + if(tzp >= end) + ; /* Nothing to do. */ + else if(*tzp == 'Z') { + tzp = " GMT"; + end = tzp + 4; + } + else { + sep = " "; + tzp++; + } + + tzl = end - tzp; + return curl_maprintf("%.4s-%.2s-%.2s %.2s:%.2s:%c%c%s%.*s%s%.*s", + beg, beg + 4, beg + 6, + beg + 8, beg + 10, sec1, sec2, + fracl? ".": "", fracl, fracp, + sep, tzl, tzp); +} + +static const char * UTime2str(const char * beg, const char * end) +{ + const char * tzp; + size_t tzl; + const char * sec; + + /* Convert an ASN.1 UTC time to a printable string. + Return the dynamically allocated string, or NULL if an error occurs. */ + + for(tzp = beg; tzp < end && *tzp >= '0' && *tzp <= '9'; tzp++) + ; + /* Get the seconds. */ + sec = beg + 10; + switch (tzp - sec) { + case 0: + sec = "00"; + case 2: + break; + default: + return (const char *) NULL; + } + + /* Process timezone. */ + if(tzp >= end) + return (const char *) NULL; + if(*tzp == 'Z') { + tzp = "GMT"; + end = tzp + 3; + } + else + tzp++; + + tzl = end - tzp; + return curl_maprintf("%u%.2s-%.2s-%.2s %.2s:%.2s:%.2s %.*s", + 20 - (*beg >= '5'), beg, beg + 2, beg + 4, + beg + 6, beg + 8, sec, + tzl, tzp); +} + +const char * Curl_ASN1tostr(curl_asn1Element * elem, int type) +{ + static const char zero = '\0'; + + /* Convert an ASN.1 element to a printable string. + Return the dynamically allocated string, or NULL if an error occurs. */ + + if(elem->constructed) + return (const char *) NULL; /* No conversion of structured elements. */ + + if(!type) + type = elem->tag; /* Type not forced: use element tag as type. */ + + switch (type) { + case CURL_ASN1_BOOLEAN: + return bool2str(elem->beg, elem->end); + case CURL_ASN1_INTEGER: + case CURL_ASN1_ENUMERATED: + return int2str(elem->beg, elem->end); + case CURL_ASN1_BIT_STRING: + return bit2str(elem->beg, elem->end); + case CURL_ASN1_OCTET_STRING: + return octet2str(elem->beg, elem->end); + case CURL_ASN1_NULL: + return strdup(&zero); + case CURL_ASN1_OBJECT_IDENTIFIER: + return OID2str(elem->beg, elem->end, TRUE); + case CURL_ASN1_UTC_TIME: + return UTime2str(elem->beg, elem->end); + case CURL_ASN1_GENERALIZED_TIME: + return GTime2str(elem->beg, elem->end); + case CURL_ASN1_UTF8_STRING: + case CURL_ASN1_NUMERIC_STRING: + case CURL_ASN1_PRINTABLE_STRING: + case CURL_ASN1_TELETEX_STRING: + case CURL_ASN1_IA5_STRING: + case CURL_ASN1_VISIBLE_STRING: + case CURL_ASN1_UNIVERSAL_STRING: + case CURL_ASN1_BMP_STRING: + return string2str(type, elem->beg, elem->end); + } + + return (const char *) NULL; /* Unsupported. */ +} + +static ssize_t encodeDN(char * buf, size_t n, curl_asn1Element * dn) +{ + curl_asn1Element rdn; + curl_asn1Element atv; + curl_asn1Element oid; + curl_asn1Element value; + size_t l = 0; + const char * p1; + const char * p2; + const char * p3; + const char * str; + + /* ASCII encode distinguished name at `dn' into the `n'-byte buffer at `buf'. + Return the total string length, even if larger than `n'. */ + + for(p1 = dn->beg; p1 < dn->end;) { + p1 = Curl_getASN1Element(&rdn, p1, dn->end); + for(p2 = rdn.beg; p2 < rdn.end;) { + p2 = Curl_getASN1Element(&atv, p2, rdn.end); + p3 = Curl_getASN1Element(&oid, atv.beg, atv.end); + Curl_getASN1Element(&value, p3, atv.end); + str = Curl_ASN1tostr(&oid, 0); + if(!str) + return -1; + + /* Encode delimiter. + If attribute has a short uppercase name, delimiter is ", ". */ + if(l) { + for(p3 = str; isupper(*p3); p3++) + ; + for(p3 = (*p3 || p3 - str > 2)? "/": ", "; *p3; p3++) { + if(l < n) + buf[l] = *p3; + l++; + } + } + + /* Encode attribute name. */ + for(p3 = str; *p3; p3++) { + if(l < n) + buf[l] = *p3; + l++; + } + free((char *) str); + + /* Generate equal sign. */ + if(l < n) + buf[l] = '='; + l++; + + /* Generate value. */ + str = Curl_ASN1tostr(&value, 0); + if(!str) + return -1; + for(p3 = str; *p3; p3++) { + if(l < n) + buf[l] = *p3; + l++; + } + free((char *) str); + } + } + + return l; +} + +const char * Curl_DNtostr(curl_asn1Element * dn) +{ + char * buf = (char *) NULL; + ssize_t n = encodeDN(buf, 0, dn); + + /* Convert an ASN.1 distinguished name into a printable string. + Return the dynamically allocated string, or NULL if an error occurs. */ + + if(n >= 0) { + buf = malloc(n + 1); + if(buf) { + encodeDN(buf, n + 1, dn); + buf[n] = '\0'; + } + } + return (const char *) buf; +} + +static const char * checkOID(const char * beg, const char * end, + const char * oid) +{ + curl_asn1Element e; + const char * ccp; + const char * p; + bool matched; + + /* Check if first ASN.1 element at `beg' is the given OID. + Return a pointer in the source after the OID if found, else NULL. */ + + ccp = Curl_getASN1Element(&e, beg, end); + if(!ccp || e.tag != CURL_ASN1_OBJECT_IDENTIFIER) + return (const char *) NULL; + + p = OID2str(e.beg, e.end, FALSE); + if(!p) + return (const char *) NULL; + + matched = !strcmp(p, oid); + free((char *) p); + return matched? ccp: (const char *) NULL; +} + + +/* + * X509 parser. + */ + +void Curl_parseX509(curl_X509certificate * cert, + const char * beg, const char * end) +{ + curl_asn1Element elem; + curl_asn1Element tbsCertificate; + const char * ccp; + static const char defaultVersion = 0; /* v1. */ + + /* ASN.1 parse an X509 certificate into structure subfields. + Syntax is assumed to have already been checked by the SSL backend. + See RFC 5280. */ + + cert->certificate.beg = beg; + cert->certificate.end = end; + + /* Get the sequence content. */ + Curl_getASN1Element(&elem, beg, end); + beg = elem.beg; + end = elem.end; + + /* Get tbsCertificate. */ + beg = Curl_getASN1Element(&tbsCertificate, beg, end); + /* Skip the signatureAlgorithm. */ + beg = Curl_getASN1Element(&cert->signatureAlgorithm, beg, end); + /* Get the signatureValue. */ + Curl_getASN1Element(&cert->signature, beg, end); + + /* Parse TBSCertificate. */ + beg = tbsCertificate.beg; + end = tbsCertificate.end; + /* Get optional version, get serialNumber. */ + cert->version.beg = &defaultVersion; + cert->version.end = &defaultVersion + sizeof defaultVersion;; + beg = Curl_getASN1Element(&elem, beg, end); + if(elem.tag == 0) { + Curl_getASN1Element(&cert->version, elem.beg, elem.end); + beg = Curl_getASN1Element(&elem, beg, end); + } + cert->serialNumber = elem; + /* Get signature algorithm. */ + beg = Curl_getASN1Element(&cert->signatureAlgorithm, beg, end); + /* Get issuer. */ + beg = Curl_getASN1Element(&cert->issuer, beg, end); + /* Get notBefore and notAfter. */ + beg = Curl_getASN1Element(&elem, beg, end); + ccp = Curl_getASN1Element(&cert->notBefore, elem.beg, elem.end); + Curl_getASN1Element(&cert->notAfter, ccp, elem.end); + /* Get subject. */ + beg = Curl_getASN1Element(&cert->subject, beg, end); + /* Get subjectPublicKeyAlgorithm and subjectPublicKey. */ + beg = Curl_getASN1Element(&elem, beg, end); + ccp = Curl_getASN1Element(&cert->subjectPublicKeyAlgorithm, + elem.beg, elem.end); + Curl_getASN1Element(&cert->subjectPublicKey, ccp, elem.end); + /* Get optional issuerUiqueID, subjectUniqueID and extensions. */ + cert->issuerUniqueID.tag = cert->subjectUniqueID.tag = 0; + cert->extensions.tag = elem.tag = 0; + cert->issuerUniqueID.beg = cert->issuerUniqueID.end = ""; + cert->subjectUniqueID.beg = cert->subjectUniqueID.end = ""; + cert->extensions.beg = cert->extensions.end = ""; + if(beg < end) + beg = Curl_getASN1Element(&elem, beg, end); + if(elem.tag == 1) { + cert->issuerUniqueID = elem; + if(beg < end) + beg = Curl_getASN1Element(&elem, beg, end); + } + if(elem.tag == 2) { + cert->subjectUniqueID = elem; + if(beg < end) + beg = Curl_getASN1Element(&elem, beg, end); + } + if(elem.tag == 3) + Curl_getASN1Element(&cert->extensions, elem.beg, elem.end); +} + +static size_t copySubstring(char * to, const char * from) +{ + size_t i; + + /* Copy at most 64-characters, terminate with a newline and returns the + effective number of stored characters. */ + + for(i = 0; i < 64; i++) { + to[i] = *from; + if(!*from++) + break; + } + + to[i++] = '\n'; + return i; +} + +static const char * dumpAlgo(curl_asn1Element * param, + const char * beg, const char * end) +{ + curl_asn1Element oid; + + /* Get algorithm parameters and return algorithm name. */ + + beg = Curl_getASN1Element(&oid, beg, end); + param->tag = 0; + param->beg = param->end = end; + if(beg < end) + Curl_getASN1Element(param, beg, end); + return OID2str(oid.beg, oid.end, TRUE); +} + +static void do_pubkey_field(struct SessionHandle *data, int certnum, + const char * label, curl_asn1Element * elem) +{ + const char * output; + + /* Generate a certificate information record for the public key. */ + + output = Curl_ASN1tostr(elem, 0); + if(output) { + Curl_ssl_push_certinfo(data, certnum, label, output); + infof(data, " %s: %s\n", label, output); + free((char *) output); + } +} + +static void do_pubkey(struct SessionHandle * data, int certnum, + const char * algo, curl_asn1Element * param, + curl_asn1Element * pubkey) +{ + curl_asn1Element elem; + curl_asn1Element pk; + const char * p; + const char * q; + unsigned long len; + unsigned int i; + + /* Generate all information records for the public key. */ + + /* Get the public key (single element). */ + Curl_getASN1Element(&pk, pubkey->beg + 1, pubkey->end); + + if(curl_strequal(algo, "rsaEncryption")) { + p = Curl_getASN1Element(&elem, pk.beg, pk.end); + /* Compute key length. */ + for(q = elem.beg; !*q && q < elem.end; q++) + ; + len = (elem.end - q) * 8; + if(len) + for(i = *(unsigned char *) q; !(i & 0x80); i <<= 1) + len--; + if(len > 32) + elem.beg = q; /* Strip leading zero bytes. */ + infof(data, " RSA Public Key (%lu bits)\n", len); + q = curl_maprintf("%lu", len); + if(q) { + Curl_ssl_push_certinfo(data, certnum, "RSA Public Key", q); + free((char *) q); + } + /* Generate coefficients. */ + do_pubkey_field(data, certnum, "rsa(n)", &elem); + Curl_getASN1Element(&elem, p, pk.end); + do_pubkey_field(data, certnum, "rsa(e)", &elem); + } + else if(curl_strequal(algo, "dsa")) { + p = Curl_getASN1Element(&elem, param->beg, param->end); + do_pubkey_field(data, certnum, "dsa(p)", &elem); + p = Curl_getASN1Element(&elem, p, param->end); + do_pubkey_field(data, certnum, "dsa(q)", &elem); + Curl_getASN1Element(&elem, p, param->end); + do_pubkey_field(data, certnum, "dsa(g)", &elem); + do_pubkey_field(data, certnum, "dsa(pub_key)", &pk); + } + else if(curl_strequal(algo, "dhpublicnumber")) { + p = Curl_getASN1Element(&elem, param->beg, param->end); + do_pubkey_field(data, certnum, "dh(p)", &elem); + Curl_getASN1Element(&elem, param->beg, param->end); + do_pubkey_field(data, certnum, "dh(g)", &elem); + do_pubkey_field(data, certnum, "dh(pub_key)", &pk); + } +#if 0 /* Patent-encumbered. */ + else if(curl_strequal(algo, "ecPublicKey")) { + /* Left TODO. */ + } +#endif +} + +CURLcode Curl_extract_certinfo(struct connectdata * conn, + int certnum, + const char * beg, + const char * end) +{ + curl_X509certificate cert; + struct SessionHandle * data = conn->data; + curl_asn1Element param; + const char * ccp; + char * cp1; + size_t cl1; + char * cp2; + CURLcode cc; + unsigned long version; + size_t i; + size_t j; + + /* Prepare the certificate information for curl_easy_getinfo(). */ + + /* Extract the certificate ASN.1 elements. */ + Curl_parseX509(&cert, beg, end); + + /* Subject. */ + ccp = Curl_DNtostr(&cert.subject); + if(!ccp) + return CURLE_OUT_OF_MEMORY; + Curl_ssl_push_certinfo(data, certnum, "Subject", ccp); + infof(data, "%2d Subject: %s\n", certnum, ccp); + free((char *) ccp); + + /* Issuer. */ + ccp = Curl_DNtostr(&cert.issuer); + if(!ccp) + return CURLE_OUT_OF_MEMORY; + Curl_ssl_push_certinfo(data, certnum, "Issuer", ccp); + infof(data, " Issuer: %s\n", ccp); + free((char *) ccp); + + /* Version (always fits in less than 32 bits). */ + version = 0; + for(ccp = cert.version.beg; ccp < cert.version.end; ccp++) + version = (version << 8) | *(const unsigned char *) ccp; + ccp = curl_maprintf("%lx", version); + if(!ccp) + return CURLE_OUT_OF_MEMORY; + Curl_ssl_push_certinfo(data, certnum, "Version", ccp); + free((char *) ccp); + infof(data, " Version: %lu (0x%lx)\n", version + 1, version); + + /* Serial number. */ + ccp = Curl_ASN1tostr(&cert.serialNumber, 0); + if(!ccp) + return CURLE_OUT_OF_MEMORY; + Curl_ssl_push_certinfo(data, certnum, "Serial Number", ccp); + infof(data, " Serial Number: %s\n", ccp); + free((char *) ccp); + + /* Signature algorithm .*/ + ccp = dumpAlgo(¶m, cert.signatureAlgorithm.beg, + cert.signatureAlgorithm.end); + if(!ccp) + return CURLE_OUT_OF_MEMORY; + Curl_ssl_push_certinfo(data, certnum, "Signature Algorithm", ccp); + infof(data, " Signature Algorithm: %s\n", ccp); + free((char *) ccp); + + /* Start Date. */ + ccp = Curl_ASN1tostr(&cert.notBefore, 0); + if(!ccp) + return CURLE_OUT_OF_MEMORY; + Curl_ssl_push_certinfo(data, certnum, "Start Date", ccp); + infof(data, " Start Date: %s\n", ccp); + free((char *) ccp); + + /* Expire Date. */ + ccp = Curl_ASN1tostr(&cert.notAfter, 0); + if(!ccp) + return CURLE_OUT_OF_MEMORY; + Curl_ssl_push_certinfo(data, certnum, "Expire Date", ccp); + infof(data, " Expire Date: %s\n", ccp); + free((char *) ccp); + + /* Public Key Algorithm. */ + ccp = dumpAlgo(¶m, cert.subjectPublicKeyAlgorithm.beg, + cert.subjectPublicKeyAlgorithm.end); + if(!ccp) + return CURLE_OUT_OF_MEMORY; + Curl_ssl_push_certinfo(data, certnum, "Public Key Algorithm", ccp); + infof(data, " Public Key Algorithm: %s\n", ccp); + do_pubkey(data, certnum, ccp, ¶m, &cert.subjectPublicKey); + free((char *) ccp); + +/* TODO: extensions. */ + + /* Signature. */ + ccp = Curl_ASN1tostr(&cert.signature, 0); + if(!ccp) + return CURLE_OUT_OF_MEMORY; + Curl_ssl_push_certinfo(data, certnum, "Signature", ccp); + infof(data, " Signature: %s\n", ccp); + free((char *) ccp); + + /* Generate PEM certificate. */ + cc = Curl_base64_encode(data, cert.certificate.beg, + cert.certificate.end - cert.certificate.beg, + &cp1, &cl1); + if(cc != CURLE_OK) + return cc; + /* Compute the number of charaters in final certificate string. Format is: + -----BEGIN CERTIFICATE-----\n + <max 64 base64 characters>\n + . + . + . + -----END CERTIFICATE-----\n + */ + i = 28 + cl1 + (cl1 + 64 - 1) / 64 + 26; + cp2 = malloc(i + 1); + if(!cp2) { + free(cp1); + return CURLE_OUT_OF_MEMORY; + } + /* Build the certificate string. */ + i = copySubstring(cp2, "-----BEGIN CERTIFICATE-----"); + for(j = 0; j < cl1; j += 64) + i += copySubstring(cp2 + i, cp1 + j); + i += copySubstring(cp2 + i, "-----END CERTIFICATE-----"); + cp2[i] = '\0'; + free(cp1); + Curl_ssl_push_certinfo(data, certnum, "Cert", cp2); + infof(data, "%s\n", cp2); + free(cp2); + return CURLE_OK; +} + + +CURLcode Curl_verifyhost(struct connectdata * conn, + const char * beg, const char * end) +{ + struct SessionHandle * data = conn->data; + curl_X509certificate cert; + curl_asn1Element dn; + curl_asn1Element elem; + curl_asn1Element ext; + curl_asn1Element name; + int i; + const char * p; + const char * q; + char * dnsname; + int matched = -1; + size_t addrlen = (size_t) -1; + ssize_t len; +#ifdef ENABLE_IPV6 + struct in6_addr addr; +#else + struct in_addr addr; +#endif + + /* Verify that connection server matches info in X509 certificate at + `beg'..`end'. */ + + if(!data->set.ssl.verifyhost) + return CURLE_OK; + + if(!beg) + return CURLE_PEER_FAILED_VERIFICATION; + Curl_parseX509(&cert, beg, end); + + /* Get the server IP address. */ +#ifdef ENABLE_IPV6 + if(conn->bits.ipv6_ip && Curl_inet_pton(AF_INET6, conn->host.name, &addr)) + addrlen = sizeof(struct in6_addr); + else +#endif + if(Curl_inet_pton(AF_INET, conn->host.name, &addr)) + addrlen = sizeof(struct in_addr); + + /* Process extensions. */ + for(p = cert.extensions.beg; p < cert.extensions.end && matched != 1;) { + p = Curl_getASN1Element(&ext, p, cert.extensions.end); + /* Check if extension is a subjectAlternativeName. */ + ext.beg = checkOID(ext.beg, ext.end, sanOID); + if(ext.beg) { + ext.beg = Curl_getASN1Element(&elem, ext.beg, ext.end); + /* Skip critical if present. */ + if(elem.tag == CURL_ASN1_BOOLEAN) + ext.beg = Curl_getASN1Element(&elem, ext.beg, ext.end); + /* Parse the octet string contents: is a single sequence. */ + Curl_getASN1Element(&elem, elem.beg, elem.end); + /* Check all GeneralNames. */ + for(q = elem.beg; matched != 1 && q < elem.end;) { + q = Curl_getASN1Element(&name, q, elem.end); + switch (name.tag) { + case 2: /* DNS name. */ + i = 0; + len = utf8asn1str(&dnsname, CURL_ASN1_IA5_STRING, + name.beg, name.end); + if(len > 0) + if(strlen(dnsname) == (size_t) len) + i = Curl_cert_hostcheck((const char *) dnsname, conn->host.name); + if(dnsname) + free(dnsname); + if(!i) + return CURLE_PEER_FAILED_VERIFICATION; + matched = i; + break; + + case 7: /* IP address. */ + matched = (size_t) (name.end - q) == addrlen && + !memcmp(&addr, q, addrlen); + break; + } + } + } + } + + switch (matched) { + case 1: + /* an alternative name matched the server hostname */ + infof(data, "\t subjectAltName: %s matched\n", conn->host.dispname); + return CURLE_OK; + case 0: + /* an alternative name field existed, but didn't match and then + we MUST fail */ + infof(data, "\t subjectAltName does not match %s\n", conn->host.dispname); + return CURLE_PEER_FAILED_VERIFICATION; + } + + /* Process subject. */ + name.beg = name.end = ""; + q = cert.subject.beg; + /* we have to look to the last occurrence of a commonName in the + distinguished one to get the most significant one. */ + while(q < cert.subject.end) { + q = Curl_getASN1Element(&dn, q, cert.subject.end); + for(p = dn.beg; p < dn.end;) { + p = Curl_getASN1Element(&elem, p, dn.end); + /* We have a DN's AttributeTypeAndValue: check it in case it's a CN. */ + elem.beg = checkOID(elem.beg, elem.end, cnOID); + if(elem.beg) + name = elem; /* Latch CN. */ + } + } + + /* Check the CN if found. */ + if(!Curl_getASN1Element(&elem, name.beg, name.end)) + failf(data, "SSL: unable to obtain common name from peer certificate"); + else { + len = utf8asn1str(&dnsname, elem.tag, elem.beg, elem.end); + if(len < 0) { + free(dnsname); + return CURLE_OUT_OF_MEMORY; + } + if(strlen(dnsname) != (size_t) len) /* Nul byte in string ? */ + failf(data, "SSL: illegal cert name field"); + else if(Curl_cert_hostcheck((const char *) dnsname, conn->host.name)) { + infof(data, "\t common name: %s (matched)\n", dnsname); + free(dnsname); + return CURLE_OK; + } + else + failf(data, "SSL: certificate subject name '%s' does not match " + "target host name '%s'", dnsname, conn->host.dispname); + free(dnsname); + } + + return CURLE_PEER_FAILED_VERIFICATION; +} + +#endif /* USE_QSOSSL or USE_GSKIT */ diff --git a/plugins/FTPFileYM/curl/lib/x509asn1.h b/plugins/FTPFileYM/curl/lib/x509asn1.h new file mode 100644 index 0000000000..2276b5b641 --- /dev/null +++ b/plugins/FTPFileYM/curl/lib/x509asn1.h @@ -0,0 +1,129 @@ +#ifndef HEADER_CURL_X509ASN1_H +#define HEADER_CURL_X509ASN1_H + +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2013, 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 + * are also available at http://curl.haxx.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ***************************************************************************/ + +#include "curl_setup.h" + +#if defined(USE_QSOSSL) || defined(USE_GSKIT) + +#include "urldata.h" + +/* + * Constants. + */ + +/* ASN.1 classes. */ +#define CURL_ASN1_UNIVERSAL 0 +#define CURL_ASN1_APPLICATION 1 +#define CURL_ASN1_CONTEXT_SPECIFIC 2 +#define CURL_ASN1_PRIVATE 3 + +/* ASN.1 types. */ +#define CURL_ASN1_BOOLEAN 1 +#define CURL_ASN1_INTEGER 2 +#define CURL_ASN1_BIT_STRING 3 +#define CURL_ASN1_OCTET_STRING 4 +#define CURL_ASN1_NULL 5 +#define CURL_ASN1_OBJECT_IDENTIFIER 6 +#define CURL_ASN1_OBJECT_DESCRIPTOR 7 +#define CURL_ASN1_INSTANCE_OF 8 +#define CURL_ASN1_REAL 9 +#define CURL_ASN1_ENUMERATED 10 +#define CURL_ASN1_EMBEDDED 11 +#define CURL_ASN1_UTF8_STRING 12 +#define CURL_ASN1_RELATIVE_OID 13 +#define CURL_ASN1_SEQUENCE 16 +#define CURL_ASN1_SET 17 +#define CURL_ASN1_NUMERIC_STRING 18 +#define CURL_ASN1_PRINTABLE_STRING 19 +#define CURL_ASN1_TELETEX_STRING 20 +#define CURL_ASN1_VIDEOTEX_STRING 21 +#define CURL_ASN1_IA5_STRING 22 +#define CURL_ASN1_UTC_TIME 23 +#define CURL_ASN1_GENERALIZED_TIME 24 +#define CURL_ASN1_GRAPHIC_STRING 25 +#define CURL_ASN1_VISIBLE_STRING 26 +#define CURL_ASN1_GENERAL_STRING 27 +#define CURL_ASN1_UNIVERSAL_STRING 28 +#define CURL_ASN1_CHARACTER_STRING 29 +#define CURL_ASN1_BMP_STRING 30 + + +/* + * Types. + */ + +/* ASN.1 parsed element. */ +typedef struct { + const char * beg; /* Pointer to element data. */ + const char * end; /* Pointer to 1st byte after element data. */ + unsigned char class; /* ASN.1 element class. */ + unsigned char tag; /* ASN.1 element tag. */ + bool constructed; /* Element is constructed. */ +} curl_asn1Element; + + +/* ASN.1 OID table entry. */ +typedef struct { + const char * numoid; /* Dotted-numeric OID. */ + const char * textoid; /* OID name. */ +} curl_OID; + + +/* X509 certificate: RFC 5280. */ +typedef struct { + curl_asn1Element certificate; + curl_asn1Element version; + curl_asn1Element serialNumber; + curl_asn1Element signatureAlgorithm; + curl_asn1Element signature; + curl_asn1Element issuer; + curl_asn1Element notBefore; + curl_asn1Element notAfter; + curl_asn1Element subject; + curl_asn1Element subjectPublicKeyAlgorithm; + curl_asn1Element subjectPublicKey; + curl_asn1Element issuerUniqueID; + curl_asn1Element subjectUniqueID; + curl_asn1Element extensions; +} curl_X509certificate; + + +/* + * Prototypes. + */ + +const char * Curl_getASN1Element(curl_asn1Element * elem, + const char * beg, const char * end); +const char * Curl_ASN1tostr(curl_asn1Element * elem, int type); +const char * Curl_DNtostr(curl_asn1Element * dn); +void Curl_parseX509(curl_X509certificate * cert, + const char * beg, const char * end); +CURLcode Curl_extract_certinfo(struct connectdata * conn, int certnum, + const char * beg, const char * end); +CURLcode Curl_verifyhost(struct connectdata * conn, + const char * beg, const char * end); + +#endif /* USE_QSOSSL or USE_GSKIT */ +#endif /* HEADER_CURL_X509ASN1_H */ |