From ce1cfcb7a6a0956c3101f5a6eb20961024e55d84 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Wed, 3 Oct 2007 08:45:00 +0000 Subject: Alexey Pesternikov introduced CURLOPT_OPENSOCKETFUNCTION and CURLOPT_OPENSOCKETDATA to set a callback that allows an application to replace the socket() call used by libcurl. It basically allows the app to change address, protocol or whatever of the socket. (I also did some whitespace indent/cleanups in lib/url.c which kind of hides some of these changes, sorry for mixing those in.) --- CHANGES | 5 ++ RELEASE-NOTES | 7 ++- include/curl/curl.h | 20 ++++++++ lib/connect.c | 37 ++++++++++++-- lib/url.c | 139 +++++++++++++++++++++++++++++----------------------- lib/urldata.h | 3 ++ 6 files changed, 142 insertions(+), 69 deletions(-) diff --git a/CHANGES b/CHANGES index 67c69c28d..803bf0f85 100644 --- a/CHANGES +++ b/CHANGES @@ -7,6 +7,11 @@ Changelog Daniel S (3 October 2007) +- Alexey Pesternikov introduced CURLOPT_OPENSOCKETFUNCTION and + CURLOPT_OPENSOCKETDATA to set a callback that allows an application to + replace the socket() call used by libcurl. It basically allows the app to + change address, protocol or whatever of the socket. + - I renamed the CURLE_SSL_PEER_CERTIFICATE error code to CURLE_PEER_FAILED_VERIFICATION (standard CURL_NO_OLDIES style), and made this return code get used by the previous SSH MD5 fingerprint check in case diff --git a/RELEASE-NOTES b/RELEASE-NOTES index 9c4e06381..93f8c6dc6 100644 --- a/RELEASE-NOTES +++ b/RELEASE-NOTES @@ -3,7 +3,7 @@ Curl and libcurl 7.17.1 Public curl release number: 102 Releases counted from the very beginning: 128 Available command line options: 121 - Available curl_easy_setopt() options: 145 + Available curl_easy_setopt() options: 147 Number of public functions in libcurl: 55 Amount of public web site mirrors: 42 Number of known libcurl bindings: 36 @@ -17,6 +17,8 @@ This release includes the following changes: o added --post301 and CURLOPT_POST301 o builds with c-ares 1.5.0 o added CURLOPT_SSH_HOST_PUBLIC_KEY_MD5 and --hostpubmd5 + o renamed CURLE_SSL_PEER_CERTIFICATE to CURLE_PEER_FAILED_VERIFICATION + o added CURLOPT_OPENSOCKETFUNCTION and CURLOPT_OPENSOCKETDATA This release includes the following bugfixes: @@ -48,6 +50,7 @@ This release would not have looked like this without help, code, reports and advice from friends like these: Dan Fandrich, Michal Marek, Günter Knauf, Rob Crittenden, Immanuel Gregoire, - Mark Davies, Max Katsev, Philip Langdale, Alex Fishman, Johnny Luong + Mark Davies, Max Katsev, Philip Langdale, Alex Fishman, Johnny Luong, + Alexey Pesternikov, Yang Tse Thanks! (and sorry if I forgot to mention someone) diff --git a/include/curl/curl.h b/include/curl/curl.h index 52acc564a..37058254d 100644 --- a/include/curl/curl.h +++ b/include/curl/curl.h @@ -246,6 +246,19 @@ typedef int (*curl_sockopt_callback)(void *clientp, curl_socket_t curlfd, curlsocktype purpose); +struct Curl_sockaddr { + int family; + int socktype; + int protocol; + socklen_t addrlen; + struct sockaddr addr; +}; + +typedef curl_socket_t +(*curl_opensocket_callback)(void* clentp, + curlsocktype purpose, + struct Curl_sockaddr* address); + #ifndef CURL_NO_OLDIES /* not used since 7.10.8, will be removed in a future release */ typedef int (*curl_passwd_callback)(void *clientp, @@ -1135,6 +1148,13 @@ typedef enum { /* used by scp/sftp to verify the host's public key */ CINIT(SSH_HOST_PUBLIC_KEY_MD5, OBJECTPOINT, 162), + /* Callback function for opening socket (instead of socket(2)). Optionally, + callback is able change the address or refuse to connect returning + CURL_SOCKET_BAD. The callback should have type + curl_opensocket_callback */ + CINIT(OPENSOCKETFUNCTION, FUNCTIONPOINT, 163), + CINIT(OPENSOCKETDATA, OBJECTPOINT, 164), + CURLOPT_LASTENTRY /* the last unused */ } CURLoption; diff --git a/lib/connect.c b/lib/connect.c index c8c82a1b7..b0615661f 100644 --- a/lib/connect.c +++ b/lib/connect.c @@ -677,14 +677,41 @@ singleipconnect(struct connectdata *conn, struct SessionHandle *data = conn->data; curl_socket_t sockfd; CURLcode res; - - sockfd = socket(ai->ai_family, conn->socktype, ai->ai_protocol); + /* + * Curl_sockaddr_storage, which is basically sockaddr_storage has a space + * for a largest possible struct sockaddr only. We should add some space for + * the other fields we are using. Hence the addr_storage size math. + */ + char addr_storage[sizeof(struct Curl_sockaddr)- + sizeof(struct sockaddr)+ + sizeof(struct Curl_sockaddr_storage)]; + struct Curl_sockaddr* addr=(struct Curl_sockaddr*)&addr_storage; + + addr->family=ai->ai_family; + addr->socktype=conn->socktype; + addr->protocol=ai->ai_protocol; + addr->addrlen=(ai->ai_addrlen<=sizeof(struct Curl_sockaddr_storage))? + ai->ai_addrlen:sizeof(struct Curl_sockaddr_storage); + memcpy(&addr->addr, ai->ai_addr, addr->addrlen); + + /* optionally use callback to get the socket */ + sockfd = (data->set.fopensocket)? + data->set.fopensocket(data->set.opensocket_client, CURLSOCKTYPE_IPCXN, + &addr): + socket(addr->family, addr->socktype, addr->protocol); if (sockfd == CURL_SOCKET_BAD) return CURL_SOCKET_BAD; *connected = FALSE; /* default is not connected */ - Curl_printable_address(ai, addr_buf, sizeof(addr_buf)); + /* FIXME: do we have Curl_printable_address-like with struct sockaddr* as + argument? */ + const void *iptoprint = &((const struct sockaddr_in*)(&addr->addr))->sin_addr; +#ifdef ENABLE_IPV6 + if(addr->family==AF_INET6) + iptoprint= &((const struct sockaddr_in6*)(&addr->addr))->sin6_addr; +#endif + Curl_inet_ntop(addr->family, iptoprint, addr_buf, sizeof(addr_buf)); infof(data, " Trying %s... ", addr_buf); if(data->set.tcp_nodelay) @@ -715,7 +742,7 @@ singleipconnect(struct connectdata *conn, /* Connect TCP sockets, bind UDP */ if(conn->socktype == SOCK_STREAM) - rc = connect(sockfd, ai->ai_addr, ai->ai_addrlen); + rc = connect(sockfd, &addr->addr, addr->addrlen); else rc = 0; @@ -779,7 +806,7 @@ singleipconnect(struct connectdata *conn, */ CURLcode Curl_connecthost(struct connectdata *conn, /* context */ - const struct Curl_dns_entry *remotehost, /* use this one */ + const struct Curl_dns_entry *remotehost, curl_socket_t *sockconn, /* the connected socket */ Curl_addrinfo **addr, /* the one we used */ bool *connected) /* really connected? */ diff --git a/lib/url.c b/lib/url.c index c91d062fe..0571fd651 100644 --- a/lib/url.c +++ b/lib/url.c @@ -1813,6 +1813,21 @@ CURLcode Curl_setopt(struct SessionHandle *data, CURLoption option, data->set.sockopt_client = va_arg(param, void *); break; + case CURLOPT_OPENSOCKETFUNCTION: + /* + * open/create socket callback function: called instead of socket(), + * before connect() + */ + data->set.fopensocket = va_arg(param, curl_opensocket_callback); + break; + + case CURLOPT_OPENSOCKETDATA: + /* + * socket callback data pointer. Might be NULL. + */ + data->set.opensocket_client = va_arg(param, void *); + break; + case CURLOPT_SSL_SESSIONID_CACHE: data->set.ssl.sessionid = (bool)(0 != va_arg(param, long)); break; @@ -1838,7 +1853,7 @@ CURLcode Curl_setopt(struct SessionHandle *data, CURLoption option, break; case CURLOPT_SSH_HOST_PUBLIC_KEY_MD5: /* - * Option to allow for the MD5 of the host public key to be checked + * Option to allow for the MD5 of the host public key to be checked * for validation purposes. */ result = Curl_setstropt(&data->set.str[STRING_SSH_HOST_PUBLIC_KEY_MD5], @@ -2449,20 +2464,20 @@ static CURLcode ConnectPlease(struct SessionHandle *data, switch(data->set.proxytype) { case CURLPROXY_SOCKS5: - result = Curl_SOCKS5(conn->proxyuser, conn->proxypasswd, conn->host.name, - conn->remote_port, FIRSTSOCKET, conn); - break; + result = Curl_SOCKS5(conn->proxyuser, conn->proxypasswd, conn->host.name, + conn->remote_port, FIRSTSOCKET, conn); + break; case CURLPROXY_HTTP: - /* do nothing here. handled later. */ - break; + /* do nothing here. handled later. */ + break; case CURLPROXY_SOCKS4: - result = Curl_SOCKS4(conn->proxyuser, conn->host.name, conn->remote_port, - FIRSTSOCKET, conn); - break; + result = Curl_SOCKS4(conn->proxyuser, conn->host.name, conn->remote_port, + FIRSTSOCKET, conn); + break; default: - failf(data, "unknown proxytype option given"); - result = CURLE_COULDNT_CONNECT; - break; + failf(data, "unknown proxytype option given"); + result = CURLE_COULDNT_CONNECT; + break; } } } @@ -2910,7 +2925,7 @@ static CURLcode setup_range(struct SessionHandle *data) * Setup connection internals specific to the requested protocol ***************************************************************/ static CURLcode setup_connection_internals(struct SessionHandle *data, - struct connectdata *conn) + struct connectdata *conn) { conn->socktype = SOCK_STREAM; /* most of them are TCP streams */ @@ -3237,17 +3252,17 @@ static char *detect_proxy(struct connectdata *conn) size_t namelen; char *endptr = strchr(conn->host.name, ':'); if(endptr) - namelen=endptr-conn->host.name; + namelen=endptr-conn->host.name; else - namelen=strlen(conn->host.name); + namelen=strlen(conn->host.name); if(strlen(nope) <= namelen) { - char *checkn= - conn->host.name + namelen - strlen(nope); - if(checkprefix(nope, checkn)) { - /* no proxy for this host! */ - break; - } + char *checkn= + conn->host.name + namelen - strlen(nope); + if(checkprefix(nope, checkn)) { + /* no proxy for this host! */ + break; + } } nope=strtok_r(NULL, ", ", &no_proxy_tok_buf); } @@ -3259,7 +3274,7 @@ static char *detect_proxy(struct connectdata *conn) /* Now, build _proxy and check for such a one to use */ while(*protop) - *envp++ = (char)tolower((int)*protop++); + *envp++ = (char)tolower((int)*protop++); /* append _proxy */ strcpy(envp, "_proxy"); @@ -3280,29 +3295,29 @@ static char *detect_proxy(struct connectdata *conn) * arbitrarily redirected by any external attacker. */ if(!prox && !strequal("http_proxy", proxy_env)) { - /* There was no lowercase variable, try the uppercase version: */ - for(envp = proxy_env; *envp; envp++) - *envp = (char)toupper((int)*envp); - prox=curl_getenv(proxy_env); + /* There was no lowercase variable, try the uppercase version: */ + for(envp = proxy_env; *envp; envp++) + *envp = (char)toupper((int)*envp); + prox=curl_getenv(proxy_env); } if(prox && *prox) { /* don't count "" strings */ - proxy = prox; /* use this */ + proxy = prox; /* use this */ } else { - proxy = curl_getenv("all_proxy"); /* default proxy to use */ - if(!proxy) - proxy=curl_getenv("ALL_PROXY"); + proxy = curl_getenv("all_proxy"); /* default proxy to use */ + if(!proxy) + proxy=curl_getenv("ALL_PROXY"); } if(proxy && *proxy) { - long bits = conn->protocol & (PROT_HTTPS|PROT_SSL|PROT_MISSING); + long bits = conn->protocol & (PROT_HTTPS|PROT_SSL|PROT_MISSING); - if(conn->proxytype == CURLPROXY_HTTP) { - /* force this connection's protocol to become HTTP */ - conn->protocol = PROT_HTTP | bits; - conn->bits.httpproxy = TRUE; - } + if(conn->proxytype == CURLPROXY_HTTP) { + /* force this connection's protocol to become HTTP */ + conn->protocol = PROT_HTTP | bits; + conn->bits.httpproxy = TRUE; + } } } /* if (!nope) - it wasn't specified non-proxy */ } /* NO_PROXY wasn't specified or '*' */ @@ -3351,43 +3366,43 @@ static CURLcode parse_proxy(struct SessionHandle *data, proxypasswd[0] = 0; if(1 <= sscanf(proxyptr, - "%" MAX_CURL_USER_LENGTH_TXT"[^:]:" - "%" MAX_CURL_PASSWORD_LENGTH_TXT "[^@]", - proxyuser, proxypasswd)) { + "%" MAX_CURL_USER_LENGTH_TXT"[^:]:" + "%" MAX_CURL_PASSWORD_LENGTH_TXT "[^@]", + proxyuser, proxypasswd)) { CURLcode res = CURLE_OK; /* 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. */ + 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(!conn->proxyuser) - res = CURLE_OUT_OF_MEMORY; + res = CURLE_OUT_OF_MEMORY; else { - Curl_safefree(conn->proxypasswd); - conn->proxypasswd = curl_easy_unescape(data, proxypasswd, 0, NULL); + Curl_safefree(conn->proxypasswd); + conn->proxypasswd = curl_easy_unescape(data, proxypasswd, 0, NULL); - if(!conn->proxypasswd) - res = CURLE_OUT_OF_MEMORY; + if(!conn->proxypasswd) + res = CURLE_OUT_OF_MEMORY; } if(CURLE_OK == res) { - conn->bits.proxy_user_passwd = TRUE; /* enable it */ - atsign = strdup(atsign+1); /* the right side of the @-letter */ - - if(atsign) { - free(proxy); /* free the former proxy string */ - proxy = proxyptr = atsign; /* now use this instead */ - } - else - res = CURLE_OUT_OF_MEMORY; + conn->bits.proxy_user_passwd = TRUE; /* enable it */ + atsign = strdup(atsign+1); /* the right side of the @-letter */ + + if(atsign) { + free(proxy); /* free the former proxy string */ + proxy = proxyptr = atsign; /* now use this instead */ + } + else + res = CURLE_OUT_OF_MEMORY; } if(res) { - free(proxy); /* free the allocated proxy string */ - return res; + free(proxy); /* free the allocated proxy string */ + return res; } } } @@ -3443,9 +3458,9 @@ static CURLcode parse_proxy_auth(struct SessionHandle *data, struct connectdata char proxypasswd[MAX_CURL_PASSWORD_LENGTH]=""; sscanf(data->set.str[STRING_PROXYUSERPWD], - "%" MAX_CURL_USER_LENGTH_TXT "[^:]:" - "%" MAX_CURL_PASSWORD_LENGTH_TXT "[^\n]", - proxyuser, proxypasswd); + "%" MAX_CURL_USER_LENGTH_TXT "[^:]:" + "%" MAX_CURL_PASSWORD_LENGTH_TXT "[^\n]", + proxyuser, proxypasswd); conn->proxyuser = curl_easy_unescape(data, proxyuser, 0, NULL); if(!conn->proxyuser) @@ -3633,7 +3648,7 @@ static CURLcode CreateConnection(struct SessionHandle *data, if(conn->bits.proxy_user_passwd) { result = parse_proxy_auth(data, conn); if (result != CURLE_OK) - return result; + return result; } /************************************************************* diff --git a/lib/urldata.h b/lib/urldata.h index 72bd0eb83..51844c4ba 100644 --- a/lib/urldata.h +++ b/lib/urldata.h @@ -1328,6 +1328,9 @@ struct UserDefined { curl_ioctl_callback ioctl_func; /* function for I/O control */ curl_sockopt_callback fsockopt; /* function for setting socket options */ void *sockopt_client; /* pointer to pass to the socket options callback */ + curl_opensocket_callback fopensocket; /* function for checking/translating + the address and opening the socket */ + void* opensocket_client; /* the 3 curl_conv_callback functions below are used on non-ASCII hosts */ /* function to convert from the network encoding: */ -- cgit v1.2.3