1 /***************************************************************************
3 * Project ___| | | | _ \| |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
8 * Copyright (C) 1998 - 2014, Daniel Stenberg, <daniel@haxx.se>, et al.
10 * This software is licensed as described in the file COPYING, which
11 * you should have received as part of this distribution. The terms
12 * are also available at http://curl.haxx.se/docs/copyright.html.
14 * You may opt to use, copy, modify, merge, publish, distribute and/or sell
15 * copies of the Software, and permit persons to whom the Software is
16 * furnished to do so, under the terms of the COPYING file.
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
21 ***************************************************************************/
23 #include "curl_setup.h"
25 #ifdef HAVE_NETINET_IN_H
26 #include <netinet/in.h> /* <netinet/tcp.h> may need it */
29 #include <sys/un.h> /* for sockaddr_un */
31 #ifdef HAVE_NETINET_TCP_H
32 #include <netinet/tcp.h> /* for TCP_NODELAY */
34 #ifdef HAVE_SYS_IOCTL_H
35 #include <sys/ioctl.h>
43 #ifdef HAVE_ARPA_INET_H
44 #include <arpa/inet.h>
47 #if (defined(HAVE_IOCTL_FIONBIO) && defined(NETWARE))
48 #include <sys/filio.h>
52 #define in_addr_t unsigned long
59 #define _MPRINTF_REPLACE /* use our functions only */
60 #include <curl/mprintf.h>
67 #include "curl_memory.h"
69 #include "url.h" /* for Curl_safefree() */
71 #include "sockaddr.h" /* required for Curl_sockaddr_storage */
72 #include "inet_ntop.h"
73 #include "inet_pton.h"
74 #include "vtls/vtls.h" /* for Curl_ssl_check_cxn() */
77 #include "conncache.h"
78 #include "multihandle.h"
80 /* The last #include file should be: */
84 /* This isn't actually supported under Symbian OS */
88 static bool verifyconnect(curl_socket_t sockfd, int *error);
90 #if defined(__DragonFly__) || defined(HAVE_WINSOCK_H)
91 /* DragonFlyBSD and Windows use millisecond units */
92 #define KEEPALIVE_FACTOR(x) (x *= 1000)
94 #define KEEPALIVE_FACTOR(x)
97 #if defined(HAVE_WINSOCK_H) && !defined(SIO_KEEPALIVE_VALS)
98 #define SIO_KEEPALIVE_VALS _WSAIOW(IOC_VENDOR,4)
100 struct tcp_keepalive {
102 u_long keepalivetime;
103 u_long keepaliveinterval;
108 tcpkeepalive(struct SessionHandle *data,
109 curl_socket_t sockfd)
111 int optval = data->set.tcp_keepalive?1:0;
113 /* only set IDLE and INTVL if setting KEEPALIVE is successful */
114 if(setsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE,
115 (void *)&optval, sizeof(optval)) < 0) {
116 infof(data, "Failed to set SO_KEEPALIVE on fd %d\n", sockfd);
119 #if defined(SIO_KEEPALIVE_VALS)
120 struct tcp_keepalive vals;
123 optval = curlx_sltosi(data->set.tcp_keepidle);
124 KEEPALIVE_FACTOR(optval);
125 vals.keepalivetime = optval;
126 optval = curlx_sltosi(data->set.tcp_keepintvl);
127 KEEPALIVE_FACTOR(optval);
128 vals.keepaliveinterval = optval;
129 if(WSAIoctl(sockfd, SIO_KEEPALIVE_VALS, (LPVOID) &vals, sizeof(vals),
130 NULL, 0, &dummy, NULL, NULL) != 0) {
131 infof(data, "Failed to set SIO_KEEPALIVE_VALS on fd %d: %d\n",
132 (int)sockfd, WSAGetLastError());
136 optval = curlx_sltosi(data->set.tcp_keepidle);
137 KEEPALIVE_FACTOR(optval);
138 if(setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPIDLE,
139 (void *)&optval, sizeof(optval)) < 0) {
140 infof(data, "Failed to set TCP_KEEPIDLE on fd %d\n", sockfd);
144 optval = curlx_sltosi(data->set.tcp_keepintvl);
145 KEEPALIVE_FACTOR(optval);
146 if(setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPINTVL,
147 (void *)&optval, sizeof(optval)) < 0) {
148 infof(data, "Failed to set TCP_KEEPINTVL on fd %d\n", sockfd);
153 optval = curlx_sltosi(data->set.tcp_keepidle);
154 KEEPALIVE_FACTOR(optval);
155 if(setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPALIVE,
156 (void *)&optval, sizeof(optval)) < 0) {
157 infof(data, "Failed to set TCP_KEEPALIVE on fd %d\n", sockfd);
165 singleipconnect(struct connectdata *conn,
166 const Curl_addrinfo *ai, /* start connecting to this */
167 curl_socket_t *sock);
170 * Curl_timeleft() returns the amount of milliseconds left allowed for the
171 * transfer/connection. If the value is negative, the timeout time has already
174 * The start time is stored in progress.t_startsingle - as set with
175 * Curl_pgrsTime(..., TIMER_STARTSINGLE);
177 * If 'nowp' is non-NULL, it points to the current time.
178 * 'duringconnect' is FALSE if not during a connect, as then of course the
179 * connect timeout is not taken into account!
183 long Curl_timeleft(struct SessionHandle *data,
184 struct timeval *nowp,
188 long timeout_ms = duringconnect?DEFAULT_CONNECT_TIMEOUT:0;
191 /* if a timeout is set, use the most restrictive one */
193 if(data->set.timeout > 0)
195 if(duringconnect && (data->set.connecttimeout > 0))
198 switch (timeout_set) {
200 timeout_ms = data->set.timeout;
203 timeout_ms = data->set.connecttimeout;
206 if(data->set.timeout < data->set.connecttimeout)
207 timeout_ms = data->set.timeout;
209 timeout_ms = data->set.connecttimeout;
212 /* use the default */
214 /* if we're not during connect, there's no default timeout so if we're
215 at zero we better just return zero and not make it a negative number
226 /* subtract elapsed time */
227 timeout_ms -= Curl_tvdiff(*nowp, data->progress.t_startsingle);
229 /* avoid returning 0 as that means no timeout! */
235 static CURLcode bindlocal(struct connectdata *conn,
236 curl_socket_t sockfd, int af)
238 struct SessionHandle *data = conn->data;
240 struct Curl_sockaddr_storage sa;
241 struct sockaddr *sock = (struct sockaddr *)&sa; /* bind to this address */
242 curl_socklen_t sizeof_sa = 0; /* size of the data sock points to */
243 struct sockaddr_in *si4 = (struct sockaddr_in *)&sa;
245 struct sockaddr_in6 *si6 = (struct sockaddr_in6 *)&sa;
248 struct Curl_dns_entry *h=NULL;
249 unsigned short port = data->set.localport; /* use this port number, 0 for
251 /* how many port numbers to try to bind to, increasing one at a time */
252 int portnum = data->set.localportrange;
253 const char *dev = data->set.str[STRING_DEVICE];
255 char myhost[256] = "";
256 int done = 0; /* -1 for error, 1 for address found */
257 bool is_interface = FALSE;
258 bool is_host = FALSE;
259 static const char *if_prefix = "if!";
260 static const char *host_prefix = "host!";
262 /*************************************************************
263 * Select device to bind socket to
264 *************************************************************/
266 /* no local kind of binding was requested */
269 memset(&sa, 0, sizeof(struct Curl_sockaddr_storage));
271 if(dev && (strlen(dev)<255) ) {
272 if(strncmp(if_prefix, dev, strlen(if_prefix)) == 0) {
273 dev += strlen(if_prefix);
276 else if(strncmp(host_prefix, dev, strlen(host_prefix)) == 0) {
277 dev += strlen(host_prefix);
283 switch(Curl_if2ip(af, conn->scope, dev, myhost, sizeof(myhost))) {
284 case IF2IP_NOT_FOUND:
286 /* Do not fall back to treating it as a host name */
287 failf(data, "Couldn't bind to interface '%s'", dev);
288 return CURLE_INTERFACE_FAILED;
291 case IF2IP_AF_NOT_SUPPORTED:
292 /* Signal the caller to try another address family if available */
293 return CURLE_UNSUPPORTED_PROTOCOL;
297 * We now have the numerical IP address in the 'myhost' buffer
299 infof(data, "Local Interface %s is ip %s using address family %i\n",
303 #ifdef SO_BINDTODEVICE
304 /* I am not sure any other OSs than Linux that provide this feature,
305 * and at the least I cannot test. --Ben
307 * This feature allows one to tightly bind the local socket to a
308 * particular interface. This will force even requests to other
309 * local interfaces to go out the external interface.
312 * Only bind to the interface when specified as interface, not just
313 * as a hostname or ip address.
315 if(setsockopt(sockfd, SOL_SOCKET, SO_BINDTODEVICE,
316 dev, (curl_socklen_t)strlen(dev)+1) != 0) {
318 infof(data, "SO_BINDTODEVICE %s failed with errno %d: %s;"
319 " will do regular bind\n",
320 dev, error, Curl_strerror(conn, error));
321 /* This is typically "errno 1, error: Operation not permitted" if
322 you're not running as root or another suitable privileged
331 * This was not an interface, resolve the name as a host name
334 * Temporarily force name resolution to use only the address type
335 * of the connection. The resolve functions should really be changed
336 * to take a type parameter instead.
338 long ipver = conn->ip_version;
342 conn->ip_version = CURL_IPRESOLVE_V4;
344 else if(af == AF_INET6)
345 conn->ip_version = CURL_IPRESOLVE_V6;
348 rc = Curl_resolv(conn, dev, 0, &h);
349 if(rc == CURLRESOLV_PENDING)
350 (void)Curl_resolver_wait_resolv(conn, &h);
351 conn->ip_version = ipver;
354 /* convert the resolved address, sizeof myhost >= INET_ADDRSTRLEN */
355 Curl_printable_address(h->addr, myhost, sizeof(myhost));
356 infof(data, "Name '%s' family %i resolved to '%s' family %i\n",
357 dev, af, myhost, h->addr->ai_family);
358 Curl_resolv_unlock(data, h);
363 * provided dev was no interface (or interfaces are not supported
364 * e.g. solaris) no ip address and no domain we fail here
374 #ifdef HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID
375 char *scope_ptr = strchr(myhost, '%');
379 if(Curl_inet_pton(AF_INET6, myhost, &si6->sin6_addr) > 0) {
380 si6->sin6_family = AF_INET6;
381 si6->sin6_port = htons(port);
382 #ifdef HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID
384 /* The "myhost" string either comes from Curl_if2ip or from
385 Curl_printable_address. The latter returns only numeric scope
386 IDs and the former returns none at all. So the scope ID, if
387 present, is known to be numeric */
388 si6->sin6_scope_id = atoi(scope_ptr);
391 sizeof_sa = sizeof(struct sockaddr_in6);
396 if((af == AF_INET) &&
397 (Curl_inet_pton(AF_INET, myhost, &si4->sin_addr) > 0)) {
398 si4->sin_family = AF_INET;
399 si4->sin_port = htons(port);
400 sizeof_sa = sizeof(struct sockaddr_in);
405 failf(data, "Couldn't bind to '%s'", dev);
406 return CURLE_INTERFACE_FAILED;
410 /* no device was given, prepare sa to match af's needs */
413 si6->sin6_family = AF_INET6;
414 si6->sin6_port = htons(port);
415 sizeof_sa = sizeof(struct sockaddr_in6);
420 si4->sin_family = AF_INET;
421 si4->sin_port = htons(port);
422 sizeof_sa = sizeof(struct sockaddr_in);
427 if(bind(sockfd, sock, sizeof_sa) >= 0) {
428 /* we succeeded to bind */
429 struct Curl_sockaddr_storage add;
430 curl_socklen_t size = sizeof(add);
431 memset(&add, 0, sizeof(struct Curl_sockaddr_storage));
432 if(getsockname(sockfd, (struct sockaddr *) &add, &size) < 0) {
433 data->state.os_errno = error = SOCKERRNO;
434 failf(data, "getsockname() failed with errno %d: %s",
435 error, Curl_strerror(conn, error));
436 return CURLE_INTERFACE_FAILED;
438 infof(data, "Local port: %hu\n", port);
439 conn->bits.bound = TRUE;
444 infof(data, "Bind to local port %hu failed, trying next\n", port);
445 port++; /* try next port */
446 /* We re-use/clobber the port variable here below */
447 if(sock->sa_family == AF_INET)
448 si4->sin_port = ntohs(port);
451 si6->sin6_port = ntohs(port);
458 data->state.os_errno = error = SOCKERRNO;
459 failf(data, "bind failed with errno %d: %s",
460 error, Curl_strerror(conn, error));
462 return CURLE_INTERFACE_FAILED;
466 * verifyconnect() returns TRUE if the connect really has happened.
468 static bool verifyconnect(curl_socket_t sockfd, int *error)
473 curl_socklen_t errSize = sizeof(err);
477 * In October 2003 we effectively nullified this function on Windows due to
478 * problems with it using all CPU in multi-threaded cases.
480 * In May 2004, we bring it back to offer more info back on connect failures.
481 * Gisle Vanem could reproduce the former problems with this function, but
482 * could avoid them by adding this SleepEx() call below:
484 * "I don't have Rational Quantify, but the hint from his post was
485 * ntdll::NtRemoveIoCompletion(). So I'd assume the SleepEx (or maybe
486 * just Sleep(0) would be enough?) would release whatever
487 * mutex/critical-section the ntdll call is waiting on.
489 * Someone got to verify this on Win-NT 4.0, 2000."
500 if(0 != getsockopt(sockfd, SOL_SOCKET, SO_ERROR, (void *)&err, &errSize))
503 /* Old WinCE versions don't support SO_ERROR */
504 if(WSAENOPROTOOPT == err) {
510 /* Minix 3.1.x doesn't support getsockopt on UDP sockets */
511 if(EBADIOCTL == err) {
516 if((0 == err) || (EISCONN == err))
517 /* we are connected, awesome! */
520 /* This wasn't a successful connect */
532 /* Used within the multi interface. Try next IP address, return TRUE if no
533 more address exists or error */
534 static CURLcode trynextip(struct connectdata *conn,
538 CURLcode rc = CURLE_COULDNT_CONNECT;
540 /* First clean up after the failed socket.
541 Don't close it yet to ensure that the next IP's socket gets a different
542 file descriptor, which can prevent bugs when the curl_multi_socket_action
543 interface is used with certain select() replacements such as kqueue. */
544 curl_socket_t fd_to_close = conn->tempsock[tempindex];
545 conn->tempsock[tempindex] = CURL_SOCKET_BAD;
547 if(sockindex == FIRSTSOCKET) {
551 if(conn->tempaddr[tempindex]) {
552 /* find next address in the same protocol family */
553 family = conn->tempaddr[tempindex]->ai_family;
554 ai = conn->tempaddr[tempindex]->ai_next;
557 /* happy eyeballs - try the other protocol family */
558 int firstfamily = conn->tempaddr[0]->ai_family;
560 family = (firstfamily == AF_INET) ? AF_INET6 : AF_INET;
562 family = firstfamily;
564 ai = conn->tempaddr[0]->ai_next;
568 while(ai && ai->ai_family != family)
572 rc = singleipconnect(conn, ai, &conn->tempsock[tempindex]);
573 conn->tempaddr[tempindex] = ai;
574 if(rc == CURLE_COULDNT_CONNECT) {
583 if(fd_to_close != CURL_SOCKET_BAD)
584 Curl_closesocket(conn, fd_to_close);
589 /* Copies connection info into the session handle to make it available
590 when the session handle is no longer associated with a connection. */
591 void Curl_persistconninfo(struct connectdata *conn)
593 memcpy(conn->data->info.conn_primary_ip, conn->primary_ip, MAX_IPADR_LEN);
594 memcpy(conn->data->info.conn_local_ip, conn->local_ip, MAX_IPADR_LEN);
595 conn->data->info.conn_primary_port = conn->primary_port;
596 conn->data->info.conn_local_port = conn->local_port;
599 /* retrieves ip address and port from a sockaddr structure */
600 static bool getaddressinfo(struct sockaddr* sa, char* addr,
603 unsigned short us_port;
604 struct sockaddr_in* si = NULL;
606 struct sockaddr_in6* si6 = NULL;
608 #if defined(HAVE_SYS_UN_H) && defined(AF_UNIX)
609 struct sockaddr_un* su = NULL;
612 switch (sa->sa_family) {
614 si = (struct sockaddr_in*) sa;
615 if(Curl_inet_ntop(sa->sa_family, &si->sin_addr,
616 addr, MAX_IPADR_LEN)) {
617 us_port = ntohs(si->sin_port);
624 si6 = (struct sockaddr_in6*)sa;
625 if(Curl_inet_ntop(sa->sa_family, &si6->sin6_addr,
626 addr, MAX_IPADR_LEN)) {
627 us_port = ntohs(si6->sin6_port);
633 #if defined(HAVE_SYS_UN_H) && defined(AF_UNIX)
635 su = (struct sockaddr_un*)sa;
636 snprintf(addr, MAX_IPADR_LEN, "%s", su->sun_path);
650 /* retrieves the start/end point information of a socket of an established
652 void Curl_updateconninfo(struct connectdata *conn, curl_socket_t sockfd)
656 struct Curl_sockaddr_storage ssrem;
657 struct Curl_sockaddr_storage ssloc;
658 struct SessionHandle *data = conn->data;
660 if(conn->socktype == SOCK_DGRAM)
661 /* there's no connection! */
664 if(!conn->bits.reuse) {
666 len = sizeof(struct Curl_sockaddr_storage);
667 if(getpeername(sockfd, (struct sockaddr*) &ssrem, &len)) {
669 failf(data, "getpeername() failed with errno %d: %s",
670 error, Curl_strerror(conn, error));
674 len = sizeof(struct Curl_sockaddr_storage);
675 if(getsockname(sockfd, (struct sockaddr*) &ssloc, &len)) {
677 failf(data, "getsockname() failed with errno %d: %s",
678 error, Curl_strerror(conn, error));
682 if(!getaddressinfo((struct sockaddr*)&ssrem,
683 conn->primary_ip, &conn->primary_port)) {
685 failf(data, "ssrem inet_ntop() failed with errno %d: %s",
686 error, Curl_strerror(conn, error));
689 memcpy(conn->ip_addr_str, conn->primary_ip, MAX_IPADR_LEN);
691 if(!getaddressinfo((struct sockaddr*)&ssloc,
692 conn->local_ip, &conn->local_port)) {
694 failf(data, "ssloc inet_ntop() failed with errno %d: %s",
695 error, Curl_strerror(conn, error));
701 /* persist connection info in session handle */
702 Curl_persistconninfo(conn);
706 * Curl_is_connected() checks if the socket has connected.
709 CURLcode Curl_is_connected(struct connectdata *conn,
713 struct SessionHandle *data = conn->data;
714 CURLcode code = CURLE_OK;
721 DEBUGASSERT(sockindex >= FIRSTSOCKET && sockindex <= SECONDARYSOCKET);
723 *connected = FALSE; /* a very negative world view is best */
725 if(conn->bits.tcpconnect[sockindex]) {
726 /* we are connected already! */
733 /* figure out how long time we have left to connect */
734 allow = Curl_timeleft(data, &now, TRUE);
737 /* time-out, bail out, go home */
738 failf(data, "Connection time-out");
739 return CURLE_OPERATION_TIMEDOUT;
743 if(conn->tempsock[i] == CURL_SOCKET_BAD)
747 /* Call this function once now, and ignore the results. We do this to
748 "clear" the error state on the socket so that we can later read it
749 reliably. This is reported necessary on the MPE/iX operating system. */
750 (void)verifyconnect(conn->tempsock[i], NULL);
753 /* check socket for connect */
754 result = Curl_socket_ready(CURL_SOCKET_BAD, conn->tempsock[i], 0);
756 if(result == 0) { /* no connection yet */
757 if(curlx_tvdiff(now, conn->connecttime) >= conn->timeoutms_per_addr) {
758 infof(data, "After %ldms connect time, move on!\n",
759 conn->timeoutms_per_addr);
763 /* should we try another protocol family? */
764 if(i == 0 && conn->tempaddr[1] == NULL &&
765 curlx_tvdiff(now, conn->connecttime) >= HAPPY_EYEBALLS_TIMEOUT) {
766 trynextip(conn, sockindex, 1);
769 else if(result == CURL_CSELECT_OUT) {
770 if(verifyconnect(conn->tempsock[i], &error)) {
771 /* we are connected with TCP, awesome! */
774 /* use this socket from now on */
775 conn->sock[sockindex] = conn->tempsock[i];
776 conn->ip_addr = conn->tempaddr[i];
777 conn->tempsock[i] = CURL_SOCKET_BAD;
779 /* close the other socket, if open */
780 if(conn->tempsock[other] != CURL_SOCKET_BAD) {
781 Curl_closesocket(conn, conn->tempsock[other]);
782 conn->tempsock[other] = CURL_SOCKET_BAD;
785 /* see if we need to do any proxy magic first once we connected */
786 code = Curl_connected_proxy(conn, sockindex);
790 conn->bits.tcpconnect[sockindex] = TRUE;
793 if(sockindex == FIRSTSOCKET)
794 Curl_pgrsTime(data, TIMER_CONNECT); /* connect done */
795 Curl_updateconninfo(conn, conn->sock[sockindex]);
796 Curl_verboseconnect(conn);
801 infof(data, "Connection failed\n");
803 else if(result & CURL_CSELECT_ERR)
804 (void)verifyconnect(conn->tempsock[i], &error);
807 * The connection failed here, we should attempt to connect to the "next
808 * address" for the given host. But first remember the latest error.
811 char ipaddress[MAX_IPADR_LEN];
812 data->state.os_errno = error;
813 SET_SOCKERRNO(error);
814 Curl_printable_address(conn->tempaddr[i], ipaddress, MAX_IPADR_LEN);
815 infof(data, "connect to %s port %ld failed: %s\n",
816 ipaddress, conn->port, Curl_strerror(conn, error));
818 conn->timeoutms_per_addr = conn->tempaddr[i]->ai_next == NULL ?
821 code = trynextip(conn, sockindex, i);
826 /* no more addresses to try */
828 /* if the first address family runs out of addresses to try before
829 the happy eyeball timeout, go ahead and try the next family now */
830 if(conn->tempaddr[1] == NULL) {
832 rc = trynextip(conn, sockindex, 1);
837 failf(data, "Failed to connect to %s port %ld: %s",
838 conn->bits.proxy?conn->proxy.name:conn->host.name,
839 conn->port, Curl_strerror(conn, error));
845 static void tcpnodelay(struct connectdata *conn,
846 curl_socket_t sockfd)
849 struct SessionHandle *data= conn->data;
850 curl_socklen_t onoff = (curl_socklen_t) data->set.tcp_nodelay;
851 int level = IPPROTO_TCP;
854 /* The use of getprotobyname() is disabled since it isn't thread-safe on
855 numerous systems. On these getprotobyname_r() should be used instead, but
856 that exists in at least one 4 arg version and one 5 arg version, and
857 since the proto number rarely changes anyway we now just use the hard
858 coded number. The "proper" fix would need a configure check for the
859 correct function much in the same style the gethostbyname_r versions are
861 struct protoent *pe = getprotobyname("tcp");
866 if(setsockopt(sockfd, level, TCP_NODELAY, (void *)&onoff,
868 infof(data, "Could not set TCP_NODELAY: %s\n",
869 Curl_strerror(conn, SOCKERRNO));
871 infof(data,"TCP_NODELAY set\n");
879 /* The preferred method on Mac OS X (10.2 and later) to prevent SIGPIPEs when
880 sending data to a dead peer (instead of relying on the 4th argument to send
881 being MSG_NOSIGNAL). Possibly also existing and in use on other BSD
883 static void nosigpipe(struct connectdata *conn,
884 curl_socket_t sockfd)
886 struct SessionHandle *data= conn->data;
888 if(setsockopt(sockfd, SOL_SOCKET, SO_NOSIGPIPE, (void *)&onoff,
890 infof(data, "Could not set SO_NOSIGPIPE: %s\n",
891 Curl_strerror(conn, SOCKERRNO));
894 #define nosigpipe(x,y) Curl_nop_stmt
898 /* When you run a program that uses the Windows Sockets API, you may
899 experience slow performance when you copy data to a TCP server.
901 http://support.microsoft.com/kb/823764
903 Work-around: Make the Socket Send Buffer Size Larger Than the Program Send
906 The problem described in this knowledge-base is applied only to pre-Vista
907 Windows. Following function trying to detect OS version and skips
908 SO_SNDBUF adjustment for Windows Vista and above.
910 #define DETECT_OS_NONE 0
911 #define DETECT_OS_PREVISTA 1
912 #define DETECT_OS_VISTA_OR_LATER 2
914 void Curl_sndbufset(curl_socket_t sockfd)
916 int val = CURL_MAX_WRITE_SIZE + 32;
918 int curlen = sizeof(curval);
921 static int detectOsState = DETECT_OS_NONE;
923 if(detectOsState == DETECT_OS_NONE) {
924 memset(&osver, 0, sizeof(osver));
925 osver.dwOSVersionInfoSize = sizeof(osver);
926 detectOsState = DETECT_OS_PREVISTA;
927 if(GetVersionEx(&osver)) {
928 if(osver.dwMajorVersion >= 6)
929 detectOsState = DETECT_OS_VISTA_OR_LATER;
932 if(detectOsState == DETECT_OS_VISTA_OR_LATER)
935 if(getsockopt(sockfd, SOL_SOCKET, SO_SNDBUF, (char *)&curval, &curlen) == 0)
939 setsockopt(sockfd, SOL_SOCKET, SO_SNDBUF, (const char *)&val, sizeof(val));
947 * Note that even on connect fail it returns CURLE_OK, but with 'sock' set to
948 * CURL_SOCKET_BAD. Other errors will however return proper errors.
950 * singleipconnect() connects to the given IP only, and it may return without
954 singleipconnect(struct connectdata *conn,
955 const Curl_addrinfo *ai,
956 curl_socket_t *sockp)
958 struct Curl_sockaddr_ex addr;
961 bool isconnected = FALSE;
962 struct SessionHandle *data = conn->data;
963 curl_socket_t sockfd;
964 CURLcode res = CURLE_OK;
965 char ipaddress[MAX_IPADR_LEN];
968 *sockp = CURL_SOCKET_BAD;
970 res = Curl_socket(conn, ai, &addr, &sockfd);
972 /* Failed to create the socket, but still return OK since we signal the
973 lack of socket as well. This allows the parent function to keep looping
974 over alternative addresses/socket families etc. */
977 /* store remote address and port used in this connection attempt */
978 if(!getaddressinfo((struct sockaddr*)&addr.sa_addr,
980 /* malformed address or bug in inet_ntop, try next address */
982 failf(data, "sa_addr inet_ntop() failed with errno %d: %s",
983 error, Curl_strerror(conn, error));
984 Curl_closesocket(conn, sockfd);
987 infof(data, " Trying %s...\n", ipaddress);
989 if(data->set.tcp_nodelay)
990 tcpnodelay(conn, sockfd);
992 nosigpipe(conn, sockfd);
994 Curl_sndbufset(sockfd);
996 if(data->set.tcp_keepalive)
997 tcpkeepalive(data, sockfd);
999 if(data->set.fsockopt) {
1000 /* activate callback for setting socket options */
1001 error = data->set.fsockopt(data->set.sockopt_client,
1003 CURLSOCKTYPE_IPCXN);
1005 if(error == CURL_SOCKOPT_ALREADY_CONNECTED)
1008 Curl_closesocket(conn, sockfd); /* close the socket and bail out */
1009 return CURLE_ABORTED_BY_CALLBACK;
1013 /* possibly bind the local end to an IP, interface or port */
1014 res = bindlocal(conn, sockfd, addr.family);
1016 Curl_closesocket(conn, sockfd); /* close socket and bail out */
1017 if(res == CURLE_UNSUPPORTED_PROTOCOL) {
1018 /* The address family is not supported on this interface.
1019 We can continue trying addresses */
1025 /* set socket non-blocking */
1026 curlx_nonblock(sockfd, TRUE);
1028 conn->connecttime = Curl_tvnow();
1029 if(conn->num_addr > 1)
1030 Curl_expire(data, conn->timeoutms_per_addr);
1032 /* Connect TCP sockets, bind UDP */
1033 if(!isconnected && (conn->socktype == SOCK_STREAM)) {
1034 rc = connect(sockfd, &addr.sa_addr, addr.addrlen);
1044 conn->bits.ipv6 = (addr.family == AF_INET6)?TRUE:FALSE;
1052 #if (EAGAIN) != (EWOULDBLOCK)
1053 /* On some platforms EAGAIN and EWOULDBLOCK are the
1054 * same value, and on others they are different, hence
1064 /* unknown error, fallthrough and try another address! */
1065 infof(data, "Immediate connect fail for %s: %s\n",
1066 ipaddress, Curl_strerror(conn,error));
1067 data->state.os_errno = error;
1069 /* connect failed */
1070 Curl_closesocket(conn, sockfd);
1071 res = CURLE_COULDNT_CONNECT;
1082 * TCP connect to the given host with timeout, proxy or remote doesn't matter.
1083 * There might be more than one IP address to try out. Fill in the passed
1084 * pointer with the connected socket.
1087 CURLcode Curl_connecthost(struct connectdata *conn, /* context */
1088 const struct Curl_dns_entry *remotehost)
1090 struct SessionHandle *data = conn->data;
1091 struct timeval before = Curl_tvnow();
1092 CURLcode res = CURLE_COULDNT_CONNECT;
1094 long timeout_ms = Curl_timeleft(data, &before, TRUE);
1096 if(timeout_ms < 0) {
1097 /* a precaution, no need to continue if time already is up */
1098 failf(data, "Connection time-out");
1099 return CURLE_OPERATION_TIMEDOUT;
1102 conn->num_addr = Curl_num_addresses(remotehost->addr);
1103 conn->tempaddr[0] = remotehost->addr;
1104 conn->tempaddr[1] = NULL;
1105 conn->tempsock[0] = CURL_SOCKET_BAD;
1106 conn->tempsock[1] = CURL_SOCKET_BAD;
1107 Curl_expire(conn->data,
1108 HAPPY_EYEBALLS_TIMEOUT + (MULTI_TIMEOUT_INACCURACY/1000));
1110 /* Max time for the next connection attempt */
1111 conn->timeoutms_per_addr =
1112 conn->tempaddr[0]->ai_next == NULL ? timeout_ms : timeout_ms / 2;
1114 /* start connecting to first IP */
1115 while(conn->tempaddr[0]) {
1116 res = singleipconnect(conn, conn->tempaddr[0], &(conn->tempsock[0]));
1119 conn->tempaddr[0] = conn->tempaddr[0]->ai_next;
1122 if(conn->tempsock[0] == CURL_SOCKET_BAD)
1125 data->info.numconnects++; /* to track the number of connections made */
1131 struct connectdata *tofind;
1135 static int conn_is_conn(struct connectdata *conn, void *param)
1137 struct connfind *f = (struct connfind *)param;
1138 if(conn == f->tofind) {
1146 * Used to extract socket and connectdata struct for the most recent
1147 * transfer on the given SessionHandle.
1149 * The returned socket will be CURL_SOCKET_BAD in case of failure!
1151 curl_socket_t Curl_getconnectinfo(struct SessionHandle *data,
1152 struct connectdata **connp)
1154 curl_socket_t sockfd;
1158 /* this only works for an easy handle that has been used for
1159 curl_easy_perform()! */
1160 if(data->state.lastconnect && data->multi_easy) {
1161 struct connectdata *c = data->state.lastconnect;
1162 struct connfind find;
1163 find.tofind = data->state.lastconnect;
1166 Curl_conncache_foreach(data->multi_easy->conn_cache, &find, conn_is_conn);
1169 data->state.lastconnect = NULL;
1170 return CURL_SOCKET_BAD;
1174 /* only store this if the caller cares for it */
1176 sockfd = c->sock[FIRSTSOCKET];
1177 /* we have a socket connected, let's determine if the server shut down */
1178 /* determine if ssl */
1179 if(c->ssl[FIRSTSOCKET].use) {
1180 /* use the SSL context */
1181 if(!Curl_ssl_check_cxn(c))
1182 return CURL_SOCKET_BAD; /* FIN received */
1184 /* Minix 3.1 doesn't support any flags on recv; just assume socket is OK */
1187 /* use the socket */
1189 if(recv((RECV_TYPE_ARG1)c->sock[FIRSTSOCKET], (RECV_TYPE_ARG2)&buf,
1190 (RECV_TYPE_ARG3)1, (RECV_TYPE_ARG4)MSG_PEEK) == 0) {
1191 return CURL_SOCKET_BAD; /* FIN received */
1197 return CURL_SOCKET_BAD;
1205 * 'conn' can be NULL, beware!
1207 int Curl_closesocket(struct connectdata *conn,
1210 if(conn && conn->fclosesocket) {
1211 if((sock == conn->sock[SECONDARYSOCKET]) &&
1212 conn->sock_accepted[SECONDARYSOCKET])
1213 /* if this socket matches the second socket, and that was created with
1214 accept, then we MUST NOT call the callback but clear the accepted
1216 conn->sock_accepted[SECONDARYSOCKET] = FALSE;
1218 return conn->fclosesocket(conn->closesocket_client, sock);
1223 /* tell the multi-socket code about this */
1224 Curl_multi_closed(conn, sock);
1230 * Create a socket based on info from 'conn' and 'ai'.
1232 * 'addr' should be a pointer to the correct struct to get data back, or NULL.
1233 * 'sockfd' must be a pointer to a socket descriptor.
1235 * If the open socket callback is set, used that!
1238 CURLcode Curl_socket(struct connectdata *conn,
1239 const Curl_addrinfo *ai,
1240 struct Curl_sockaddr_ex *addr,
1241 curl_socket_t *sockfd)
1243 struct SessionHandle *data = conn->data;
1244 struct Curl_sockaddr_ex dummy;
1247 /* if the caller doesn't want info back, use a local temp copy */
1251 * The Curl_sockaddr_ex structure is basically libcurl's external API
1252 * curl_sockaddr structure with enough space available to directly hold
1253 * any protocol-specific address structures. The variable declared here
1254 * will be used to pass / receive data to/from the fopensocket callback
1255 * if this has been set, before that, it is initialized from parameters.
1258 addr->family = ai->ai_family;
1259 addr->socktype = conn->socktype;
1260 addr->protocol = conn->socktype==SOCK_DGRAM?IPPROTO_UDP:ai->ai_protocol;
1261 addr->addrlen = ai->ai_addrlen;
1263 if(addr->addrlen > sizeof(struct Curl_sockaddr_storage))
1264 addr->addrlen = sizeof(struct Curl_sockaddr_storage);
1265 memcpy(&addr->sa_addr, ai->ai_addr, addr->addrlen);
1267 if(data->set.fopensocket)
1269 * If the opensocket callback is set, all the destination address
1270 * information is passed to the callback. Depending on this information the
1271 * callback may opt to abort the connection, this is indicated returning
1272 * CURL_SOCKET_BAD; otherwise it will return a not-connected socket. When
1273 * the callback returns a valid socket the destination address information
1274 * might have been changed and this 'new' address will actually be used
1277 *sockfd = data->set.fopensocket(data->set.opensocket_client,
1279 (struct curl_sockaddr *)addr);
1281 /* opensocket callback not set, so simply create the socket now */
1282 *sockfd = socket(addr->family, addr->socktype, addr->protocol);
1284 if(*sockfd == CURL_SOCKET_BAD)
1285 /* no socket, no connection */
1286 return CURLE_COULDNT_CONNECT;
1288 #if defined(ENABLE_IPV6) && defined(HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID)
1289 if(conn->scope && (addr->family == AF_INET6)) {
1290 struct sockaddr_in6 * const sa6 = (void *)&addr->sa_addr;
1291 sa6->sin6_scope_id = conn->scope;