1 /***************************************************************************
3 * Project ___| | | | _ \| |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
8 * Copyright (C) 1998 - 2013, 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 "sslgen.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;
559 family = (firstfamily == AF_INET) ? AF_INET6 : AF_INET;
560 ai = conn->tempaddr[0]->ai_next;
563 while(ai && ai->ai_family != family)
566 rc = singleipconnect(conn, ai, &conn->tempsock[tempindex]);
567 conn->tempaddr[tempindex] = ai;
571 if(fd_to_close != CURL_SOCKET_BAD)
572 Curl_closesocket(conn, fd_to_close);
577 /* Copies connection info into the session handle to make it available
578 when the session handle is no longer associated with a connection. */
579 void Curl_persistconninfo(struct connectdata *conn)
581 memcpy(conn->data->info.conn_primary_ip, conn->primary_ip, MAX_IPADR_LEN);
582 memcpy(conn->data->info.conn_local_ip, conn->local_ip, MAX_IPADR_LEN);
583 conn->data->info.conn_primary_port = conn->primary_port;
584 conn->data->info.conn_local_port = conn->local_port;
587 /* retrieves ip address and port from a sockaddr structure */
588 static bool getaddressinfo(struct sockaddr* sa, char* addr,
591 unsigned short us_port;
592 struct sockaddr_in* si = NULL;
594 struct sockaddr_in6* si6 = NULL;
596 #if defined(HAVE_SYS_UN_H) && defined(AF_UNIX)
597 struct sockaddr_un* su = NULL;
600 switch (sa->sa_family) {
602 si = (struct sockaddr_in*) sa;
603 if(Curl_inet_ntop(sa->sa_family, &si->sin_addr,
604 addr, MAX_IPADR_LEN)) {
605 us_port = ntohs(si->sin_port);
612 si6 = (struct sockaddr_in6*)sa;
613 if(Curl_inet_ntop(sa->sa_family, &si6->sin6_addr,
614 addr, MAX_IPADR_LEN)) {
615 us_port = ntohs(si6->sin6_port);
621 #if defined(HAVE_SYS_UN_H) && defined(AF_UNIX)
623 su = (struct sockaddr_un*)sa;
624 snprintf(addr, MAX_IPADR_LEN, "%s", su->sun_path);
638 /* retrieves the start/end point information of a socket of an established
640 void Curl_updateconninfo(struct connectdata *conn, curl_socket_t sockfd)
644 struct Curl_sockaddr_storage ssrem;
645 struct Curl_sockaddr_storage ssloc;
646 struct SessionHandle *data = conn->data;
648 if(!conn->bits.reuse) {
650 len = sizeof(struct Curl_sockaddr_storage);
651 if(getpeername(sockfd, (struct sockaddr*) &ssrem, &len)) {
653 failf(data, "getpeername() failed with errno %d: %s",
654 error, Curl_strerror(conn, error));
658 len = sizeof(struct Curl_sockaddr_storage);
659 if(getsockname(sockfd, (struct sockaddr*) &ssloc, &len)) {
661 failf(data, "getsockname() failed with errno %d: %s",
662 error, Curl_strerror(conn, error));
666 if(!getaddressinfo((struct sockaddr*)&ssrem,
667 conn->primary_ip, &conn->primary_port)) {
669 failf(data, "ssrem inet_ntop() failed with errno %d: %s",
670 error, Curl_strerror(conn, error));
673 memcpy(conn->ip_addr_str, conn->primary_ip, MAX_IPADR_LEN);
675 if(!getaddressinfo((struct sockaddr*)&ssloc,
676 conn->local_ip, &conn->local_port)) {
678 failf(data, "ssloc inet_ntop() failed with errno %d: %s",
679 error, Curl_strerror(conn, error));
685 /* persist connection info in session handle */
686 Curl_persistconninfo(conn);
690 * Curl_is_connected() checks if the socket has connected.
693 CURLcode Curl_is_connected(struct connectdata *conn,
697 struct SessionHandle *data = conn->data;
698 CURLcode code = CURLE_OK;
705 DEBUGASSERT(sockindex >= FIRSTSOCKET && sockindex <= SECONDARYSOCKET);
707 *connected = FALSE; /* a very negative world view is best */
709 if(conn->bits.tcpconnect[sockindex]) {
710 /* we are connected already! */
717 /* figure out how long time we have left to connect */
718 allow = Curl_timeleft(data, &now, TRUE);
721 /* time-out, bail out, go home */
722 failf(data, "Connection time-out");
723 return CURLE_OPERATION_TIMEDOUT;
727 if(conn->tempsock[i] == CURL_SOCKET_BAD)
731 /* Call this function once now, and ignore the results. We do this to
732 "clear" the error state on the socket so that we can later read it
733 reliably. This is reported necessary on the MPE/iX operating system. */
734 (void)verifyconnect(conn->tempsock[i], NULL);
737 /* check socket for connect */
738 result = Curl_socket_ready(CURL_SOCKET_BAD, conn->tempsock[i], 0);
740 if(result == 0) { /* no connection yet */
741 if(curlx_tvdiff(now, conn->connecttime) >= conn->timeoutms_per_addr) {
742 infof(data, "After %ldms connect time, move on!\n",
743 conn->timeoutms_per_addr);
747 /* should we try another protocol family? */
748 if(i == 0 && conn->tempaddr[1] == NULL &&
749 curlx_tvdiff(now, conn->connecttime) >= HAPPY_EYEBALLS_TIMEOUT) {
750 trynextip(conn, sockindex, 1);
753 else if(result == CURL_CSELECT_OUT) {
754 if(verifyconnect(conn->tempsock[i], &error)) {
755 /* we are connected with TCP, awesome! */
758 /* use this socket from now on */
759 conn->sock[sockindex] = conn->tempsock[i];
760 conn->ip_addr = conn->tempaddr[i];
761 conn->tempsock[i] = CURL_SOCKET_BAD;
763 /* close the other socket, if open */
764 if(conn->tempsock[other] != CURL_SOCKET_BAD) {
765 Curl_closesocket(conn, conn->tempsock[other]);
766 conn->tempsock[other] = CURL_SOCKET_BAD;
769 /* see if we need to do any proxy magic first once we connected */
770 code = Curl_connected_proxy(conn, sockindex);
774 conn->bits.tcpconnect[sockindex] = TRUE;
777 if(sockindex == FIRSTSOCKET)
778 Curl_pgrsTime(data, TIMER_CONNECT); /* connect done */
779 Curl_updateconninfo(conn, conn->sock[sockindex]);
780 Curl_verboseconnect(conn);
785 infof(data, "Connection failed\n");
787 else if(result & CURL_CSELECT_ERR)
788 (void)verifyconnect(conn->tempsock[i], &error);
791 * The connection failed here, we should attempt to connect to the "next
792 * address" for the given host. But first remember the latest error.
795 char ipaddress[MAX_IPADR_LEN];
796 data->state.os_errno = error;
797 SET_SOCKERRNO(error);
798 Curl_printable_address(conn->tempaddr[i], ipaddress, MAX_IPADR_LEN);
799 infof(data, "connect to %s port %ld failed: %s\n",
800 ipaddress, conn->port, Curl_strerror(conn, error));
802 conn->timeoutms_per_addr = conn->tempaddr[i]->ai_next == NULL ?
805 code = trynextip(conn, sockindex, i);
810 /* no more addresses to try */
812 /* if the first address family runs out of addresses to try before
813 the happy eyeball timeout, go ahead and try the next family now */
814 if(conn->tempaddr[1] == NULL) {
816 rc = trynextip(conn, sockindex, 1);
821 failf(data, "Failed to connect to %s port %ld: %s",
822 conn->host.name, conn->port, Curl_strerror(conn, error));
828 static void tcpnodelay(struct connectdata *conn,
829 curl_socket_t sockfd)
832 struct SessionHandle *data= conn->data;
833 curl_socklen_t onoff = (curl_socklen_t) data->set.tcp_nodelay;
834 int level = IPPROTO_TCP;
837 /* The use of getprotobyname() is disabled since it isn't thread-safe on
838 numerous systems. On these getprotobyname_r() should be used instead, but
839 that exists in at least one 4 arg version and one 5 arg version, and
840 since the proto number rarely changes anyway we now just use the hard
841 coded number. The "proper" fix would need a configure check for the
842 correct function much in the same style the gethostbyname_r versions are
844 struct protoent *pe = getprotobyname("tcp");
849 if(setsockopt(sockfd, level, TCP_NODELAY, (void *)&onoff,
851 infof(data, "Could not set TCP_NODELAY: %s\n",
852 Curl_strerror(conn, SOCKERRNO));
854 infof(data,"TCP_NODELAY set\n");
862 /* The preferred method on Mac OS X (10.2 and later) to prevent SIGPIPEs when
863 sending data to a dead peer (instead of relying on the 4th argument to send
864 being MSG_NOSIGNAL). Possibly also existing and in use on other BSD
866 static void nosigpipe(struct connectdata *conn,
867 curl_socket_t sockfd)
869 struct SessionHandle *data= conn->data;
871 if(setsockopt(sockfd, SOL_SOCKET, SO_NOSIGPIPE, (void *)&onoff,
873 infof(data, "Could not set SO_NOSIGPIPE: %s\n",
874 Curl_strerror(conn, SOCKERRNO));
877 #define nosigpipe(x,y) Curl_nop_stmt
881 /* When you run a program that uses the Windows Sockets API, you may
882 experience slow performance when you copy data to a TCP server.
884 http://support.microsoft.com/kb/823764
886 Work-around: Make the Socket Send Buffer Size Larger Than the Program Send
889 The problem described in this knowledge-base is applied only to pre-Vista
890 Windows. Following function trying to detect OS version and skips
891 SO_SNDBUF adjustment for Windows Vista and above.
893 #define DETECT_OS_NONE 0
894 #define DETECT_OS_PREVISTA 1
895 #define DETECT_OS_VISTA_OR_LATER 2
897 void Curl_sndbufset(curl_socket_t sockfd)
899 int val = CURL_MAX_WRITE_SIZE + 32;
901 int curlen = sizeof(curval);
904 static int detectOsState = DETECT_OS_NONE;
906 if(detectOsState == DETECT_OS_NONE) {
907 memset(&osver, 0, sizeof(osver));
908 osver.dwOSVersionInfoSize = sizeof(osver);
909 detectOsState = DETECT_OS_PREVISTA;
910 if(GetVersionEx(&osver)) {
911 if(osver.dwMajorVersion >= 6)
912 detectOsState = DETECT_OS_VISTA_OR_LATER;
915 if(detectOsState == DETECT_OS_VISTA_OR_LATER)
918 if(getsockopt(sockfd, SOL_SOCKET, SO_SNDBUF, (char *)&curval, &curlen) == 0)
922 setsockopt(sockfd, SOL_SOCKET, SO_SNDBUF, (const char *)&val, sizeof(val));
930 * Note that even on connect fail it returns CURLE_OK, but with 'sock' set to
931 * CURL_SOCKET_BAD. Other errors will however return proper errors.
933 * singleipconnect() connects to the given IP only, and it may return without
937 singleipconnect(struct connectdata *conn,
938 const Curl_addrinfo *ai,
939 curl_socket_t *sockp)
941 struct Curl_sockaddr_ex addr;
944 bool isconnected = FALSE;
945 struct SessionHandle *data = conn->data;
946 curl_socket_t sockfd;
947 CURLcode res = CURLE_OK;
948 char ipaddress[MAX_IPADR_LEN];
951 *sockp = CURL_SOCKET_BAD;
953 res = Curl_socket(conn, ai, &addr, &sockfd);
955 /* Failed to create the socket, but still return OK since we signal the
956 lack of socket as well. This allows the parent function to keep looping
957 over alternative addresses/socket families etc. */
960 /* store remote address and port used in this connection attempt */
961 if(!getaddressinfo((struct sockaddr*)&addr.sa_addr,
963 /* malformed address or bug in inet_ntop, try next address */
965 failf(data, "sa_addr inet_ntop() failed with errno %d: %s",
966 error, Curl_strerror(conn, error));
967 Curl_closesocket(conn, sockfd);
970 infof(data, " Trying %s...\n", ipaddress);
972 if(data->set.tcp_nodelay)
973 tcpnodelay(conn, sockfd);
975 nosigpipe(conn, sockfd);
977 Curl_sndbufset(sockfd);
979 if(data->set.tcp_keepalive)
980 tcpkeepalive(data, sockfd);
982 if(data->set.fsockopt) {
983 /* activate callback for setting socket options */
984 error = data->set.fsockopt(data->set.sockopt_client,
988 if(error == CURL_SOCKOPT_ALREADY_CONNECTED)
991 Curl_closesocket(conn, sockfd); /* close the socket and bail out */
992 return CURLE_ABORTED_BY_CALLBACK;
996 /* possibly bind the local end to an IP, interface or port */
997 res = bindlocal(conn, sockfd, addr.family);
999 Curl_closesocket(conn, sockfd); /* close socket and bail out */
1000 if(res == CURLE_UNSUPPORTED_PROTOCOL) {
1001 /* The address family is not supported on this interface.
1002 We can continue trying addresses */
1008 /* set socket non-blocking */
1009 curlx_nonblock(sockfd, TRUE);
1011 conn->connecttime = Curl_tvnow();
1012 if(conn->num_addr > 1)
1013 Curl_expire(data, conn->timeoutms_per_addr);
1015 /* Connect TCP sockets, bind UDP */
1016 if(!isconnected && (conn->socktype == SOCK_STREAM)) {
1017 rc = connect(sockfd, &addr.sa_addr, addr.addrlen);
1027 conn->bits.ipv6 = (addr.family == AF_INET6)?TRUE:FALSE;
1035 #if (EAGAIN) != (EWOULDBLOCK)
1036 /* On some platforms EAGAIN and EWOULDBLOCK are the
1037 * same value, and on others they are different, hence
1047 /* unknown error, fallthrough and try another address! */
1048 infof(data, "Immediate connect fail for %s: %s\n",
1049 ipaddress, Curl_strerror(conn,error));
1050 data->state.os_errno = error;
1052 /* connect failed */
1053 return CURLE_COULDNT_CONNECT;
1063 * TCP connect to the given host with timeout, proxy or remote doesn't matter.
1064 * There might be more than one IP address to try out. Fill in the passed
1065 * pointer with the connected socket.
1068 CURLcode Curl_connecthost(struct connectdata *conn, /* context */
1069 const struct Curl_dns_entry *remotehost)
1071 struct SessionHandle *data = conn->data;
1072 struct timeval before = Curl_tvnow();
1075 long timeout_ms = Curl_timeleft(data, &before, TRUE);
1077 if(timeout_ms < 0) {
1078 /* a precaution, no need to continue if time already is up */
1079 failf(data, "Connection time-out");
1080 return CURLE_OPERATION_TIMEDOUT;
1083 conn->num_addr = Curl_num_addresses(remotehost->addr);
1084 conn->tempaddr[0] = remotehost->addr;
1085 conn->tempaddr[1] = NULL;
1086 conn->tempsock[0] = CURL_SOCKET_BAD;
1087 conn->tempsock[1] = CURL_SOCKET_BAD;
1088 Curl_expire(conn->data,
1089 HAPPY_EYEBALLS_TIMEOUT + (MULTI_TIMEOUT_INACCURACY/1000));
1091 /* Max time for the next connection attempt */
1092 conn->timeoutms_per_addr =
1093 conn->tempaddr[0]->ai_next == NULL ? timeout_ms : timeout_ms / 2;
1095 /* start connecting to first IP */
1096 res = singleipconnect(conn, conn->tempaddr[0], &(conn->tempsock[0]));
1097 while(res != CURLE_OK &&
1098 conn->tempaddr[0] &&
1099 conn->tempaddr[0]->ai_next &&
1100 conn->tempsock[0] == CURL_SOCKET_BAD)
1101 res = trynextip(conn, FIRSTSOCKET, 0);
1103 if(conn->tempsock[0] == CURL_SOCKET_BAD)
1106 data->info.numconnects++; /* to track the number of connections made */
1112 struct connectdata *tofind;
1116 static int conn_is_conn(struct connectdata *conn, void *param)
1118 struct connfind *f = (struct connfind *)param;
1119 if(conn == f->tofind) {
1127 * Used to extract socket and connectdata struct for the most recent
1128 * transfer on the given SessionHandle.
1130 * The returned socket will be CURL_SOCKET_BAD in case of failure!
1132 curl_socket_t Curl_getconnectinfo(struct SessionHandle *data,
1133 struct connectdata **connp)
1135 curl_socket_t sockfd;
1139 /* this only works for an easy handle that has been used for
1140 curl_easy_perform()! */
1141 if(data->state.lastconnect && data->multi_easy) {
1142 struct connectdata *c = data->state.lastconnect;
1143 struct connfind find;
1144 find.tofind = data->state.lastconnect;
1147 Curl_conncache_foreach(data->multi_easy->conn_cache, &find, conn_is_conn);
1150 data->state.lastconnect = NULL;
1151 return CURL_SOCKET_BAD;
1155 /* only store this if the caller cares for it */
1157 sockfd = c->sock[FIRSTSOCKET];
1158 /* we have a socket connected, let's determine if the server shut down */
1159 /* determine if ssl */
1160 if(c->ssl[FIRSTSOCKET].use) {
1161 /* use the SSL context */
1162 if(!Curl_ssl_check_cxn(c))
1163 return CURL_SOCKET_BAD; /* FIN received */
1165 /* Minix 3.1 doesn't support any flags on recv; just assume socket is OK */
1168 /* use the socket */
1170 if(recv((RECV_TYPE_ARG1)c->sock[FIRSTSOCKET], (RECV_TYPE_ARG2)&buf,
1171 (RECV_TYPE_ARG3)1, (RECV_TYPE_ARG4)MSG_PEEK) == 0) {
1172 return CURL_SOCKET_BAD; /* FIN received */
1178 return CURL_SOCKET_BAD;
1186 * 'conn' can be NULL, beware!
1188 int Curl_closesocket(struct connectdata *conn,
1191 if(conn && conn->fclosesocket) {
1192 if((sock == conn->sock[SECONDARYSOCKET]) &&
1193 conn->sock_accepted[SECONDARYSOCKET])
1194 /* if this socket matches the second socket, and that was created with
1195 accept, then we MUST NOT call the callback but clear the accepted
1197 conn->sock_accepted[SECONDARYSOCKET] = FALSE;
1199 return conn->fclosesocket(conn->closesocket_client, sock);
1204 /* tell the multi-socket code about this */
1205 Curl_multi_closed(conn, sock);
1211 * Create a socket based on info from 'conn' and 'ai'.
1213 * 'addr' should be a pointer to the correct struct to get data back, or NULL.
1214 * 'sockfd' must be a pointer to a socket descriptor.
1216 * If the open socket callback is set, used that!
1219 CURLcode Curl_socket(struct connectdata *conn,
1220 const Curl_addrinfo *ai,
1221 struct Curl_sockaddr_ex *addr,
1222 curl_socket_t *sockfd)
1224 struct SessionHandle *data = conn->data;
1225 struct Curl_sockaddr_ex dummy;
1228 /* if the caller doesn't want info back, use a local temp copy */
1232 * The Curl_sockaddr_ex structure is basically libcurl's external API
1233 * curl_sockaddr structure with enough space available to directly hold
1234 * any protocol-specific address structures. The variable declared here
1235 * will be used to pass / receive data to/from the fopensocket callback
1236 * if this has been set, before that, it is initialized from parameters.
1239 addr->family = ai->ai_family;
1240 addr->socktype = conn->socktype;
1241 addr->protocol = conn->socktype==SOCK_DGRAM?IPPROTO_UDP:ai->ai_protocol;
1242 addr->addrlen = ai->ai_addrlen;
1244 if(addr->addrlen > sizeof(struct Curl_sockaddr_storage))
1245 addr->addrlen = sizeof(struct Curl_sockaddr_storage);
1246 memcpy(&addr->sa_addr, ai->ai_addr, addr->addrlen);
1248 if(data->set.fopensocket)
1250 * If the opensocket callback is set, all the destination address
1251 * information is passed to the callback. Depending on this information the
1252 * callback may opt to abort the connection, this is indicated returning
1253 * CURL_SOCKET_BAD; otherwise it will return a not-connected socket. When
1254 * the callback returns a valid socket the destination address information
1255 * might have been changed and this 'new' address will actually be used
1258 *sockfd = data->set.fopensocket(data->set.opensocket_client,
1260 (struct curl_sockaddr *)addr);
1262 /* opensocket callback not set, so simply create the socket now */
1263 *sockfd = socket(addr->family, addr->socktype, addr->protocol);
1265 if(*sockfd == CURL_SOCKET_BAD)
1266 /* no socket, no connection */
1267 return CURLE_COULDNT_CONNECT;
1269 #if defined(ENABLE_IPV6) && defined(HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID)
1270 if(conn->scope && (addr->family == AF_INET6)) {
1271 struct sockaddr_in6 * const sa6 = (void *)&addr->sa_addr;
1272 sa6->sin6_scope_id = conn->scope;