/* singleipconnect() connects to the given IP only, and it may return without having connected if used from the multi interface. */ static curl_socket_t singleipconnect(struct connectdata *conn, const Curl_addrinfo *ai, long timeout_ms, bool *connected) { struct Curl_sockaddr_ex addr; char addr_buf[128]; int rc; int error; bool isconnected; struct SessionHandle *data = conn->data; curl_socket_t sockfd; CURLcode res; const void *iptoprint; struct sockaddr_in * const sa4 = (void *)&addr.sa_addr; #ifdef ENABLE_IPV6 struct sockaddr_in6 * const sa6 = (void *)&addr.sa_addr; #endif /* * The Curl_sockaddr_ex structure is basically libcurl's external API * curl_sockaddr structure with enough space available to directly hold * any protocol-specific address structures. The variable declared here * will be used to pass / receive data to/from the fopensocket callback * if this has been set, before that, it is initialized from parameters. */ addr.family = ai->ai_family; addr.socktype = conn->socktype; addr.protocol = ai->ai_protocol; addr.addrlen = ai->ai_addrlen; if(addr.addrlen > sizeof(struct Curl_sockaddr_storage)) addr.addrlen = sizeof(struct Curl_sockaddr_storage); memcpy(&addr.sa_addr, ai->ai_addr, addr.addrlen); *connected = FALSE; /* default is not connected */ if(data->set.fopensocket) /* * If the opensocket callback is set, all the destination address * information is passed to the callback. Depending on this information the * callback may opt to abort the connection, this is indicated returning * CURL_SOCKET_BAD; otherwise it will return a not-connected socket. When * the callback returns a valid socket the destination address information * might have been changed and this 'new' address will actually be used * here to connect. */ sockfd = data->set.fopensocket(data->set.opensocket_client, CURLSOCKTYPE_IPCXN, (struct curl_sockaddr *)&addr); else /* opensocket callback not set, so simply create the socket now */ sockfd = socket(addr.family, addr.socktype, addr.protocol); if(sockfd == CURL_SOCKET_BAD) /* no socket, no connection */ return CURL_SOCKET_BAD; #if defined(ENABLE_IPV6) && defined(HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID) if (conn->scope && (addr.family == AF_INET6)) sa6->sin6_scope_id = conn->scope; #endif /* FIXME: do we have Curl_printable_address-like with struct sockaddr* as argument? */ #if defined(HAVE_SYS_UN_H) && defined(AF_UNIX) if(addr.family == AF_UNIX) { infof(data, " Trying %s... ", ((const struct sockaddr_un*)(&addr.sa_addr))->sun_path); snprintf(data->info.ip, MAX_IPADR_LEN, "%s", ((const struct sockaddr_un*)(&addr.sa_addr))->sun_path); strcpy(conn->ip_addr_str, data->info.ip); } else #endif { #ifdef ENABLE_IPV6 if(addr.family == AF_INET6) { iptoprint = &sa6->sin6_addr; conn->bits.ipv6 = TRUE; } else #endif { iptoprint = &sa4->sin_addr; } if(Curl_inet_ntop(addr.family, iptoprint, addr_buf, sizeof(addr_buf)) != NULL) { infof(data, " Trying %s... ", addr_buf); snprintf(data->info.ip, MAX_IPADR_LEN, "%s", addr_buf); strcpy(conn->ip_addr_str, data->info.ip); } } if(data->set.tcp_nodelay) tcpnodelay(conn, sockfd); nosigpipe(conn, sockfd); Curl_sndbufset(sockfd); if(data->set.fsockopt) { /* activate callback for setting socket options */ error = data->set.fsockopt(data->set.sockopt_client, sockfd, CURLSOCKTYPE_IPCXN); if(error) { sclose(sockfd); /* close the socket and bail out */ return CURL_SOCKET_BAD; } } /* possibly bind the local end to an IP, interface or port */ res = bindlocal(conn, sockfd, addr.family); if(res) { sclose(sockfd); /* close socket and bail out */ return CURL_SOCKET_BAD; } /* set socket non-blocking */ curlx_nonblock(sockfd, TRUE); /* Connect TCP sockets, bind UDP */ if(conn->socktype == SOCK_STREAM) rc = connect(sockfd, &addr.sa_addr, addr.addrlen); else rc = 0; if(-1 == rc) { error = SOCKERRNO; switch (error) { case EINPROGRESS: case EWOULDBLOCK: #if defined(EAGAIN) #if (EAGAIN) != (EWOULDBLOCK) /* On some platforms EAGAIN and EWOULDBLOCK are the * same value, and on others they are different, hence * the odd #if */ case EAGAIN: #endif #endif rc = waitconnect(conn, sockfd, timeout_ms); break; default: /* unknown error, fallthrough and try another address! */ failf(data, "Failed to connect to %s: %s", addr_buf, Curl_strerror(conn,error)); data->state.os_errno = error; break; } } /* The 'WAITCONN_TIMEOUT == rc' comes from the waitconnect(), and not from connect(). We can be sure of this since connect() cannot return 1. */ if((WAITCONN_TIMEOUT == rc) && (data->state.used_interface == Curl_if_multi)) { /* Timeout when running the multi interface */ return sockfd; } isconnected = verifyconnect(sockfd, &error); if(!rc && isconnected) { /* we are connected, awesome! */ *connected = TRUE; /* this is a true connect */ infof(data, "connected\n"); Curl_updateconninfo(conn, sockfd); return sockfd; } else if(WAITCONN_TIMEOUT == rc) infof(data, "Timeout\n"); else { data->state.os_errno = error; infof(data, "%s\n", Curl_strerror(conn, error)); } /* connect failed or timed out */ sclose(sockfd); return CURL_SOCKET_BAD; }
/* singleipconnect() connects to the given IP only, and it may return without having connected if used from the multi interface. */ static curl_socket_t singleipconnect(struct connectdata *conn, const Curl_addrinfo *ai, long timeout_ms, bool *connected) { char addr_buf[128]; int rc; int error; bool isconnected; struct SessionHandle *data = conn->data; curl_socket_t sockfd; CURLcode res; sockfd = socket(ai->ai_family, conn->socktype, ai->ai_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)); infof(data, " Trying %s... ", addr_buf); if(data->set.tcp_nodelay) tcpnodelay(conn, sockfd); nosigpipe(conn, sockfd); if(data->set.fsockopt) { /* activate callback for setting socket options */ error = data->set.fsockopt(data->set.sockopt_client, sockfd, CURLSOCKTYPE_IPCXN); if (error) { sclose(sockfd); /* close the socket and bail out */ return CURL_SOCKET_BAD; } } /* possibly bind the local end to an IP, interface or port */ res = bindlocal(conn, sockfd); if(res) { sclose(sockfd); /* close socket and bail out */ return CURL_SOCKET_BAD; } /* set socket non-blocking */ Curl_nonblock(sockfd, TRUE); /* Connect TCP sockets, bind UDP */ if(conn->socktype == SOCK_STREAM) rc = connect(sockfd, ai->ai_addr, ai->ai_addrlen); else rc = 0; if(-1 == rc) { error = Curl_sockerrno(); switch (error) { case EINPROGRESS: case EWOULDBLOCK: #if defined(EAGAIN) && EAGAIN != EWOULDBLOCK /* On some platforms EAGAIN and EWOULDBLOCK are the * same value, and on others they are different, hence * the odd #if */ case EAGAIN: #endif rc = waitconnect(sockfd, timeout_ms); break; default: /* unknown error, fallthrough and try another address! */ failf(data, "Failed to connect to %s: %s", addr_buf, Curl_strerror(conn,error)); data->state.os_errno = error; break; } } /* The 'WAITCONN_TIMEOUT == rc' comes from the waitconnect(), and not from connect(). We can be sure of this since connect() cannot return 1. */ if((WAITCONN_TIMEOUT == rc) && (data->state.used_interface == Curl_if_multi)) { /* Timeout when running the multi interface */ return sockfd; } isconnected = verifyconnect(sockfd, &error); if(!rc && isconnected) { /* we are connected, awesome! */ *connected = TRUE; /* this is a true connect */ infof(data, "connected\n"); return sockfd; } else if(WAITCONN_TIMEOUT == rc) infof(data, "Timeout\n"); else { data->state.os_errno = error; infof(data, "%s\n", Curl_strerror(conn, error)); } /* connect failed or timed out */ sclose(sockfd); return CURL_SOCKET_BAD; }
/* * singleipconnect() * * Note that even on connect fail it returns CURLE_OK, but with 'sock' set to * CURL_SOCKET_BAD. Other errors will however return proper errors. * * singleipconnect() connects to the given IP only, and it may return without * having connected if used from the multi interface. */ static CURLcode singleipconnect(struct connectdata *conn, const Curl_addrinfo *ai, long timeout_ms, curl_socket_t *sockp, bool *connected) { struct Curl_sockaddr_ex addr; int rc; int error = 0; bool isconnected = FALSE; struct SessionHandle *data = conn->data; curl_socket_t sockfd; CURLcode res = CURLE_OK; *sockp = CURL_SOCKET_BAD; *connected = FALSE; /* default is not connected */ res = Curl_socket(conn, ai, &addr, &sockfd); if(res) /* Failed to create the socket, but still return OK since we signal the lack of socket as well. This allows the parent function to keep looping over alternative addresses/socket families etc. */ return CURLE_OK; /* store remote address and port used in this connection attempt */ if(!getaddressinfo((struct sockaddr*)&addr.sa_addr, conn->primary_ip, &conn->primary_port)) { /* malformed address or bug in inet_ntop, try next address */ error = ERRNO; failf(data, "sa_addr inet_ntop() failed with errno %d: %s", error, Curl_strerror(conn, error)); Curl_closesocket(conn, sockfd); return CURLE_OK; } memcpy(conn->ip_addr_str, conn->primary_ip, MAX_IPADR_LEN); infof(data, " Trying %s...\n", conn->ip_addr_str); Curl_persistconninfo(conn); if(data->set.tcp_nodelay) tcpnodelay(conn, sockfd); nosigpipe(conn, sockfd); Curl_sndbufset(sockfd); if(data->set.tcp_keepalive) tcpkeepalive(data, sockfd); if(data->set.fsockopt) { /* activate callback for setting socket options */ error = data->set.fsockopt(data->set.sockopt_client, sockfd, CURLSOCKTYPE_IPCXN); if(error == CURL_SOCKOPT_ALREADY_CONNECTED) isconnected = TRUE; else if(error) { Curl_closesocket(conn, sockfd); /* close the socket and bail out */ return CURLE_ABORTED_BY_CALLBACK; } } /* possibly bind the local end to an IP, interface or port */ res = bindlocal(conn, sockfd, addr.family); if(res) { Curl_closesocket(conn, sockfd); /* close socket and bail out */ return res; } /* set socket non-blocking */ curlx_nonblock(sockfd, TRUE); /* Connect TCP sockets, bind UDP */ if(!isconnected && (conn->socktype == SOCK_STREAM)) { rc = connect(sockfd, &addr.sa_addr, addr.addrlen); if(-1 == rc) error = SOCKERRNO; conn->connecttime = Curl_tvnow(); if(conn->num_addr > 1) Curl_expire(data, conn->timeoutms_per_addr); } else rc = 0; if(-1 == rc) { switch (error) { case EINPROGRESS: case EWOULDBLOCK: #if defined(EAGAIN) #if (EAGAIN) != (EWOULDBLOCK) /* On some platforms EAGAIN and EWOULDBLOCK are the * same value, and on others they are different, hence * the odd #if */ case EAGAIN: #endif #endif rc = waitconnect(conn, sockfd, timeout_ms); if(WAITCONN_ABORTED == rc) { Curl_closesocket(conn, sockfd); return CURLE_ABORTED_BY_CALLBACK; } break; default: /* unknown error, fallthrough and try another address! */ failf(data, "Failed to connect to %s: %s", conn->ip_addr_str, Curl_strerror(conn,error)); data->state.os_errno = error; break; } } /* The 'WAITCONN_TIMEOUT == rc' comes from the waitconnect(), and not from connect(). We can be sure of this since connect() cannot return 1. */ if((WAITCONN_TIMEOUT == rc) && (data->state.used_interface == Curl_if_multi)) { /* Timeout when running the multi interface */ *sockp = sockfd; return CURLE_OK; } if(!isconnected) isconnected = verifyconnect(sockfd, &error); if(!rc && isconnected) { /* we are connected, awesome! */ *connected = TRUE; /* this is a true connect */ infof(data, "connected\n"); #ifdef ENABLE_IPV6 conn->bits.ipv6 = (addr.family == AF_INET6)?TRUE:FALSE; #endif Curl_updateconninfo(conn, sockfd); *sockp = sockfd; return CURLE_OK; } else if(WAITCONN_TIMEOUT == rc) infof(data, "Timeout\n"); else { data->state.os_errno = error; infof(data, "%s\n", Curl_strerror(conn, error)); } /* connect failed or timed out */ Curl_closesocket(conn, sockfd); return CURLE_OK; }
/* * singleipconnect() * * Note that even on connect fail it returns CURLE_OK, but with 'sock' set to * CURL_SOCKET_BAD. Other errors will however return proper errors. * * singleipconnect() connects to the given IP only, and it may return without * having connected. */ static CURLcode singleipconnect(struct connectdata *conn, const Curl_addrinfo *ai, curl_socket_t *sockp) { struct Curl_sockaddr_ex addr; int rc; int error = 0; bool isconnected = FALSE; struct SessionHandle *data = conn->data; curl_socket_t sockfd; CURLcode result; char ipaddress[MAX_IPADR_LEN]; long port; bool is_tcp; *sockp = CURL_SOCKET_BAD; result = Curl_socket(conn, ai, &addr, &sockfd); if(result) /* Failed to create the socket, but still return OK since we signal the lack of socket as well. This allows the parent function to keep looping over alternative addresses/socket families etc. */ return CURLE_OK; /* store remote address and port used in this connection attempt */ if(!getaddressinfo((struct sockaddr*)&addr.sa_addr, ipaddress, &port)) { /* malformed address or bug in inet_ntop, try next address */ error = ERRNO; failf(data, "sa_addr inet_ntop() failed with errno %d: %s", error, Curl_strerror(conn, error)); Curl_closesocket(conn, sockfd); return CURLE_OK; } infof(data, " Trying %s...\n", ipaddress); is_tcp = (addr.family == AF_INET || addr.family == AF_INET6) && addr.socktype == SOCK_STREAM; if(is_tcp && data->set.tcp_nodelay) tcpnodelay(conn, sockfd); nosigpipe(conn, sockfd); Curl_sndbufset(sockfd); if(is_tcp && data->set.tcp_keepalive) tcpkeepalive(data, sockfd); if(data->set.fsockopt) { /* activate callback for setting socket options */ error = data->set.fsockopt(data->set.sockopt_client, sockfd, CURLSOCKTYPE_IPCXN); if(error == CURL_SOCKOPT_ALREADY_CONNECTED) isconnected = TRUE; else if(error) { Curl_closesocket(conn, sockfd); /* close the socket and bail out */ return CURLE_ABORTED_BY_CALLBACK; } } /* possibly bind the local end to an IP, interface or port */ if(addr.family == AF_INET || addr.family == AF_INET6) { result = bindlocal(conn, sockfd, addr.family, Curl_ipv6_scope((struct sockaddr*)&addr.sa_addr)); if(result) { Curl_closesocket(conn, sockfd); /* close socket and bail out */ if(result == CURLE_UNSUPPORTED_PROTOCOL) { /* The address family is not supported on this interface. We can continue trying addresses */ return CURLE_COULDNT_CONNECT; } return result; } } /* set socket non-blocking */ (void)curlx_nonblock(sockfd, TRUE); conn->connecttime = Curl_tvnow(); if(conn->num_addr > 1) Curl_expire_latest(data, conn->timeoutms_per_addr); /* Connect TCP sockets, bind UDP */ if(!isconnected && (conn->socktype == SOCK_STREAM)) { rc = connect(sockfd, &addr.sa_addr, addr.addrlen); if(-1 == rc) error = SOCKERRNO; } else { *sockp = sockfd; return CURLE_OK; } #ifdef ENABLE_IPV6 conn->bits.ipv6 = (addr.family == AF_INET6)?TRUE:FALSE; #endif if(-1 == rc) { switch(error) { case EINPROGRESS: case EWOULDBLOCK: #if defined(EAGAIN) #if (EAGAIN) != (EWOULDBLOCK) /* On some platforms EAGAIN and EWOULDBLOCK are the * same value, and on others they are different, hence * the odd #if */ case EAGAIN: #endif #endif result = CURLE_OK; break; default: /* unknown error, fallthrough and try another address! */ infof(data, "Immediate connect fail for %s: %s\n", ipaddress, Curl_strerror(conn,error)); data->state.os_errno = error; /* connect failed */ Curl_closesocket(conn, sockfd); result = CURLE_COULDNT_CONNECT; } } if(!result) *sockp = sockfd; return result; }
/* * singleipconnect() * * Note that even on connect fail it returns CURLE_OK, but with 'sock' set to * CURL_SOCKET_BAD. Other errors will however return proper errors. * * singleipconnect() connects to the given IP only, and it may return without * having connected if used from the multi interface. */ static CURLcode singleipconnect(struct connectdata *conn, const Curl_addrinfo *ai, long timeout_ms, curl_socket_t *sockp, bool *connected) { struct Curl_sockaddr_ex addr; int rc; int error; bool isconnected = FALSE; struct SessionHandle *data = conn->data; curl_socket_t sockfd; CURLcode res = CURLE_OK; #if defined(ENABLE_IPV6) && defined(HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID) struct sockaddr_in6 * const sa6 = (void *)&addr.sa_addr; #endif *sockp = CURL_SOCKET_BAD; /* * The Curl_sockaddr_ex structure is basically libcurl's external API * curl_sockaddr structure with enough space available to directly hold * any protocol-specific address structures. The variable declared here * will be used to pass / receive data to/from the fopensocket callback * if this has been set, before that, it is initialized from parameters. */ addr.family = ai->ai_family; addr.socktype = conn->socktype; addr.protocol = conn->socktype==SOCK_DGRAM?IPPROTO_UDP:ai->ai_protocol; addr.addrlen = ai->ai_addrlen; if(addr.addrlen > sizeof(struct Curl_sockaddr_storage)) addr.addrlen = sizeof(struct Curl_sockaddr_storage); memcpy(&addr.sa_addr, ai->ai_addr, addr.addrlen); *connected = FALSE; /* default is not connected */ if(data->set.fopensocket) /* * If the opensocket callback is set, all the destination address * information is passed to the callback. Depending on this information the * callback may opt to abort the connection, this is indicated returning * CURL_SOCKET_BAD; otherwise it will return a not-connected socket. When * the callback returns a valid socket the destination address information * might have been changed and this 'new' address will actually be used * here to connect. */ sockfd = data->set.fopensocket(data->set.opensocket_client, CURLSOCKTYPE_IPCXN, (struct curl_sockaddr *)&addr); else /* opensocket callback not set, so simply create the socket now */ sockfd = socket(addr.family, addr.socktype, addr.protocol); if(sockfd == CURL_SOCKET_BAD) /* no socket, no connection */ return CURLE_OK; #if defined(ENABLE_IPV6) && defined(HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID) if (conn->scope && (addr.family == AF_INET6)) sa6->sin6_scope_id = conn->scope; #endif /* store remote address and port used in this connection attempt */ if(!getaddressinfo((struct sockaddr*)&addr.sa_addr, conn->primary_ip, &conn->primary_port)) { /* malformed address or bug in inet_ntop, try next address */ error = ERRNO; failf(data, "sa_addr inet_ntop() failed with errno %d: %s", error, Curl_strerror(conn, error)); sclose(sockfd); return CURLE_OK; } memcpy(conn->ip_addr_str, conn->primary_ip, MAX_IPADR_LEN); infof(data, " Trying %s... ", conn->ip_addr_str); Curl_persistconninfo(conn); #ifdef ENABLE_IPV6 if(addr.family == AF_INET6) conn->bits.ipv6 = TRUE; #endif if(data->set.tcp_nodelay) tcpnodelay(conn, sockfd); nosigpipe(conn, sockfd); Curl_sndbufset(sockfd); if(data->set.fsockopt) { /* activate callback for setting socket options */ error = data->set.fsockopt(data->set.sockopt_client, sockfd, CURLSOCKTYPE_IPCXN); if(error == CURL_SOCKOPT_ALREADY_CONNECTED) isconnected = TRUE; else if(error) { sclose(sockfd); /* close the socket and bail out */ return CURLE_ABORTED_BY_CALLBACK; } } /* possibly bind the local end to an IP, interface or port */ res = bindlocal(conn, sockfd, addr.family); if(res) { sclose(sockfd); /* close socket and bail out */ return res; } /* set socket non-blocking */ curlx_nonblock(sockfd, TRUE); /* Connect TCP sockets, bind UDP */ if(!isconnected && (conn->socktype == SOCK_STREAM)) { rc = connect(sockfd, &addr.sa_addr, addr.addrlen); conn->connecttime = Curl_tvnow(); if(conn->num_addr > 1) Curl_expire(data, conn->timeoutms_per_addr); } else rc = 0; if(-1 == rc) { error = SOCKERRNO; switch (error) { case EINPROGRESS: case EWOULDBLOCK: #if defined(EAGAIN) #if (EAGAIN) != (EWOULDBLOCK) /* On some platforms EAGAIN and EWOULDBLOCK are the * same value, and on others they are different, hence * the odd #if */ case EAGAIN: #endif #endif rc = waitconnect(conn, sockfd, timeout_ms); if(WAITCONN_ABORTED == rc) { sclose(sockfd); return CURLE_ABORTED_BY_CALLBACK; } break; default: /* unknown error, fallthrough and try another address! */ failf(data, "Failed to connect to %s: %s", conn->ip_addr_str, Curl_strerror(conn,error)); data->state.os_errno = error; break; } } /* The 'WAITCONN_TIMEOUT == rc' comes from the waitconnect(), and not from connect(). We can be sure of this since connect() cannot return 1. */ if((WAITCONN_TIMEOUT == rc) && (data->state.used_interface == Curl_if_multi)) { /* Timeout when running the multi interface */ *sockp = sockfd; return CURLE_OK; } if(!isconnected) isconnected = verifyconnect(sockfd, &error); if(!rc && isconnected) { /* we are connected, awesome! */ *connected = TRUE; /* this is a true connect */ infof(data, "connected\n"); Curl_updateconninfo(conn, sockfd); *sockp = sockfd; return CURLE_OK; } else if(WAITCONN_TIMEOUT == rc) infof(data, "Timeout\n"); else { data->state.os_errno = error; infof(data, "%s\n", Curl_strerror(conn, error)); } /* connect failed or timed out */ sclose(sockfd); return CURLE_OK; }
/* * singleipconnect() * * Note that even on connect fail it returns CURLE_OK, but with 'sock' set to * CURL_SOCKET_BAD. Other errors will however return proper errors. * * singleipconnect() connects to the given IP only, and it may return without * having connected. */ static CURLcode singleipconnect(struct connectdata *conn, const Curl_addrinfo *ai, curl_socket_t *sockp) { struct Curl_sockaddr_ex addr; int rc = -1; int error = 0; bool isconnected = FALSE; struct Curl_easy *data = conn->data; curl_socket_t sockfd; CURLcode result; char ipaddress[MAX_IPADR_LEN]; long port; bool is_tcp; *sockp = CURL_SOCKET_BAD; result = Curl_socket(conn, ai, &addr, &sockfd); if(result) /* Failed to create the socket, but still return OK since we signal the lack of socket as well. This allows the parent function to keep looping over alternative addresses/socket families etc. */ return CURLE_OK; /* store remote address and port used in this connection attempt */ if(!getaddressinfo((struct sockaddr*)&addr.sa_addr, ipaddress, &port)) { /* malformed address or bug in inet_ntop, try next address */ failf(data, "sa_addr inet_ntop() failed with errno %d: %s", errno, Curl_strerror(conn, errno)); Curl_closesocket(conn, sockfd); return CURLE_OK; } infof(data, " Trying %s...\n", ipaddress); #ifdef ENABLE_IPV6 is_tcp = (addr.family == AF_INET || addr.family == AF_INET6) && addr.socktype == SOCK_STREAM; #else is_tcp = (addr.family == AF_INET) && addr.socktype == SOCK_STREAM; #endif if(is_tcp && data->set.tcp_nodelay) Curl_tcpnodelay(conn, sockfd); nosigpipe(conn, sockfd); Curl_sndbufset(sockfd); if(is_tcp && data->set.tcp_keepalive) tcpkeepalive(data, sockfd); if(data->set.fsockopt) { /* activate callback for setting socket options */ error = data->set.fsockopt(data->set.sockopt_client, sockfd, CURLSOCKTYPE_IPCXN); if(error == CURL_SOCKOPT_ALREADY_CONNECTED) isconnected = TRUE; else if(error) { Curl_closesocket(conn, sockfd); /* close the socket and bail out */ return CURLE_ABORTED_BY_CALLBACK; } } /* possibly bind the local end to an IP, interface or port */ if(addr.family == AF_INET #ifdef ENABLE_IPV6 || addr.family == AF_INET6 #endif ) { result = bindlocal(conn, sockfd, addr.family, Curl_ipv6_scope((struct sockaddr*)&addr.sa_addr)); if(result) { Curl_closesocket(conn, sockfd); /* close socket and bail out */ if(result == CURLE_UNSUPPORTED_PROTOCOL) { /* The address family is not supported on this interface. We can continue trying addresses */ return CURLE_COULDNT_CONNECT; } return result; } } /* set socket non-blocking */ (void)curlx_nonblock(sockfd, TRUE); conn->connecttime = Curl_tvnow(); if(conn->num_addr > 1) Curl_expire(data, conn->timeoutms_per_addr, EXPIRE_DNS_PER_NAME); /* Connect TCP sockets, bind UDP */ if(!isconnected && (conn->socktype == SOCK_STREAM)) { if(conn->bits.tcp_fastopen) { #if defined(CONNECT_DATA_IDEMPOTENT) /* OS X */ #ifdef HAVE_BUILTIN_AVAILABLE if(__builtin_available(macOS 10.11, iOS 9.0, tvOS 9.0, watchOS 2.0, *)) { #endif /* HAVE_BUILTIN_AVAILABLE */ sa_endpoints_t endpoints; endpoints.sae_srcif = 0; endpoints.sae_srcaddr = NULL; endpoints.sae_srcaddrlen = 0; endpoints.sae_dstaddr = &addr.sa_addr; endpoints.sae_dstaddrlen = addr.addrlen; rc = connectx(sockfd, &endpoints, SAE_ASSOCID_ANY, CONNECT_RESUME_ON_READ_WRITE | CONNECT_DATA_IDEMPOTENT, NULL, 0, NULL, NULL); #ifdef HAVE_BUILTIN_AVAILABLE } else { rc = connect(sockfd, &addr.sa_addr, addr.addrlen); } #endif /* HAVE_BUILTIN_AVAILABLE */ #elif defined(MSG_FASTOPEN) /* Linux */ if(conn->given->flags & PROTOPT_SSL) rc = connect(sockfd, &addr.sa_addr, addr.addrlen); else rc = 0; /* Do nothing */ #endif } else {
/* singleipconnect() connects to the given IP only, and it may return without having connected if used from the multi interface. */ static curl_socket_t singleipconnect(struct connectdata *conn, Curl_addrinfo *ai, long timeout_ms, bool *connected) { char addr_buf[128]; int rc; int error; bool conected; struct SessionHandle *data = conn->data; curl_socket_t sockfd = socket(ai->ai_family, ai->ai_socktype, ai->ai_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)); infof(data, " Trying %s... ", addr_buf); if(data->set.tcp_nodelay) tcpnodelay(conn, sockfd); #ifdef SO_NOSIGPIPE nosigpipe(conn, sockfd); #endif if(conn->data->set.device) { /* user selected to bind the outgoing socket to a specified "device" before doing connect */ CURLcode res = bindlocal(conn, sockfd); if(res) { sclose(sockfd); /* close socket and bail out */ return CURL_SOCKET_BAD; } } /* set socket non-blocking */ Curl_nonblock(sockfd, TRUE); rc = connect(sockfd, ai->ai_addr, (socklen_t)ai->ai_addrlen); if(-1 == rc) { error = Curl_ourerrno(); switch (error) { case EINPROGRESS: case EWOULDBLOCK: #if defined(EAGAIN) && EAGAIN != EWOULDBLOCK /* On some platforms EAGAIN and EWOULDBLOCK are the * same value, and on others they are different, hence * the odd #if */ case EAGAIN: #endif rc = waitconnect(sockfd, timeout_ms); break; default: /* unknown error, fallthrough and try another address! */ failf(data, "Failed to connect to %s: %s", addr_buf, Curl_strerror(conn,error)); data->state.os_errno = error; break; } } /* The 'WAITCONN_TIMEOUT == rc' comes from the waitconnect(), and not from connect(). We can be sure of this since connect() cannot return 1. */ if((WAITCONN_TIMEOUT == rc) && (data->state.used_interface == Curl_if_multi)) { /* Timeout when running the multi interface */ return sockfd; } conected = verifyconnect(sockfd, &error); if(!rc && conected) { /* we are connected, awesome! */ *connected = TRUE; /* this is a true connect */ infof(data, "connected\n"); return sockfd; } else if(WAITCONN_TIMEOUT == rc) infof(data, "Timeout\n"); else { data->state.os_errno = error; infof(data, "%s\n", Curl_strerror(conn, error)); } /* connect failed or timed out */ sclose(sockfd); return CURL_SOCKET_BAD; }
CURLcode Curl_connecthost(struct connectdata *conn, /* context */ struct Curl_dns_entry *remotehost, /* use this one */ int port, /* connect to this */ curl_socket_t *sockconn, /* the connected socket */ Curl_ipconnect **addr, /* the one we used */ bool *connected) /* really connected? */ { struct SessionHandle *data = conn->data; int rc; curl_socket_t sockfd= CURL_SOCKET_BAD; int aliasindex=0; char *hostname; struct timeval after; struct timeval before = Curl_tvnow(); #ifdef ENABLE_IPV6 struct addrinfo *ai; #endif /************************************************************* * Figure out what maximum time we have left *************************************************************/ long timeout_ms=300000; /* milliseconds, default to five minutes */ *connected = FALSE; /* default to not connected */ if(data->set.timeout || data->set.connecttimeout) { double has_passed; /* Evaluate in milliseconds how much time that has passed */ has_passed = Curl_tvdiff(Curl_tvnow(), data->progress.start); #ifndef min #define min(a, b) ((a) < (b) ? (a) : (b)) #endif /* get the most strict timeout of the ones converted to milliseconds */ if(data->set.timeout && data->set.connecttimeout) { if (data->set.timeout < data->set.connecttimeout) timeout_ms = data->set.timeout*1000; else timeout_ms = data->set.connecttimeout*1000; } else if(data->set.timeout) timeout_ms = data->set.timeout*1000; else timeout_ms = data->set.connecttimeout*1000; /* subtract the passed time */ timeout_ms -= (long)has_passed; if(timeout_ms < 0) { /* a precaution, no need to continue if time already is up */ failf(data, "Connection time-out"); return CURLE_OPERATION_TIMEOUTED; } } hostname = data->change.proxy?conn->proxyhost:conn->hostname; infof(data, "About to connect() to %s port %d\n", hostname, port); #ifdef ENABLE_IPV6 /* * Connecting with a getaddrinfo chain */ for (ai = remotehost->addr; ai; ai = ai->ai_next, aliasindex++) { sockfd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); if (sockfd == CURL_SOCKET_BAD) continue; #else /* * Connecting with old style IPv4-only support */ /* This is the loop that attempts to connect to all IP-addresses we know for the given host. One by one. */ for(rc=-1, aliasindex=0; rc && (struct in_addr *)remotehost->addr->h_addr_list[aliasindex]; aliasindex++) { struct sockaddr_in serv_addr; /* create an IPv4 TCP socket */ sockfd = socket(AF_INET, SOCK_STREAM, 0); if(CURL_SOCKET_BAD == sockfd) { failf(data, "couldn't create socket"); return CURLE_COULDNT_CONNECT; /* big time error */ } /* nasty address work before connect can be made */ memset((char *) &serv_addr, '\0', sizeof(serv_addr)); memcpy((char *)&(serv_addr.sin_addr), (struct in_addr *)remotehost->addr->h_addr_list[aliasindex], sizeof(struct in_addr)); serv_addr.sin_family = remotehost->addr->h_addrtype; serv_addr.sin_port = htons((unsigned short)port); #endif if(conn->data->set.device) { /* user selected to bind the outgoing socket to a specified "device" before doing connect */ CURLcode res = bindlocal(conn, sockfd); if(res) return res; } /* set socket non-blocking */ Curl_nonblock(sockfd, TRUE); /* do not use #ifdef within the function arguments below, as connect() is a defined macro on some platforms and some compilers don't like to mix #ifdefs with macro usage! (AmigaOS is one such platform) */ #ifdef ENABLE_IPV6 rc = connect(sockfd, ai->ai_addr, ai->ai_addrlen); #else rc = connect(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)); #endif if(-1 == rc) { int error=Curl_ourerrno(); switch (error) { case EINPROGRESS: case EWOULDBLOCK: #if defined(EAGAIN) && EAGAIN != EWOULDBLOCK /* On some platforms EAGAIN and EWOULDBLOCK are the * same value, and on others they are different, hence * the odd #if */ case EAGAIN: #endif /* asynchronous connect, wait for connect or timeout */ if(data->state.used_interface == Curl_if_multi) /* don't hang when doing multi */ timeout_ms = 0; rc = waitconnect(sockfd, timeout_ms); break; default: /* unknown error, fallthrough and try another address! */ failf(data, "Failed to connect to %s IP number %d: %d", hostname, aliasindex+1, error); break; } } /* The '1 == rc' comes from the waitconnect(), and not from connect(). We can be sure of this since connect() cannot return 1. */ if((1 == rc) && (data->state.used_interface == Curl_if_multi)) { /* Timeout when running the multi interface, we return here with a CURLE_OK return code. */ rc = 0; break; } if(0 == rc) { if (verifyconnect(sockfd)) { /* we are connected, awesome! */ *connected = TRUE; /* this is a true connect */ break; } /* nope, not connected for real */ rc = -1; } /* connect failed or timed out */ sclose(sockfd); sockfd = -1; /* get a new timeout for next attempt */ after = Curl_tvnow(); timeout_ms -= Curl_tvdiff(after, before); if(timeout_ms < 0) { failf(data, "connect() timed out!"); return CURLE_OPERATION_TIMEOUTED; } before = after; } if (sockfd == CURL_SOCKET_BAD) { /* no good connect was made */ *sockconn = -1; failf(data, "Connect failed"); return CURLE_COULDNT_CONNECT; } /* leave the socket in non-blocking mode */ /* store the address we use */ if(addr) { #ifdef ENABLE_IPV6 *addr = ai; #else *addr = (struct in_addr *)remotehost->addr->h_addr_list[aliasindex]; #endif } /* allow NULL-pointers to get passed in */ if(sockconn) *sockconn = sockfd; /* the socket descriptor we've connected */ return CURLE_OK; }
CURLcode Curl_connecthost(struct connectdata *conn, /* context */ struct Curl_dns_entry *remotehost, /* use this one */ int port, /* connect to this */ int *sockconn, /* the connected socket */ Curl_ipconnect **addr, /* the one we used */ bool *connected) /* really connected? */ { struct SessionHandle *data = conn->data; int rc; int sockfd=-1; int aliasindex=0; char *hostname; struct timeval after; struct timeval before = Curl_tvnow(); /************************************************************* * Figure out what maximum time we have left *************************************************************/ long timeout_ms=300000; /* milliseconds, default to five minutes */ *connected = FALSE; /* default to not connected */ if(data->set.timeout || data->set.connecttimeout) { double has_passed; /* Evaluate in milliseconds how much time that has passed */ has_passed = Curl_tvdiff(Curl_tvnow(), data->progress.start); #ifndef min #define min(a, b) ((a) < (b) ? (a) : (b)) #endif /* get the most strict timeout of the ones converted to milliseconds */ if(data->set.timeout && data->set.connecttimeout) { if (data->set.timeout < data->set.connecttimeout) timeout_ms = data->set.timeout*1000; else timeout_ms = data->set.connecttimeout*1000; } else if(data->set.timeout) timeout_ms = data->set.timeout*1000; else timeout_ms = data->set.connecttimeout*1000; /* subtract the passed time */ timeout_ms -= (long)has_passed; if(timeout_ms < 0) { /* a precaution, no need to continue if time already is up */ failf(data, "Connection time-out"); return CURLE_OPERATION_TIMEOUTED; } } hostname = data->change.proxy?conn->proxyhost:conn->hostname; infof(data, "About to connect() to %s%s%s:%d\n", conn->bits.ipv6_ip?"[":"", hostname, conn->bits.ipv6_ip?"]":"", port); #ifdef ENABLE_IPV6 /* * Connecting with IPv6 support is so much easier and cleanly done */ { struct addrinfo *ai; port =0; /* prevent compiler warning */ for (ai = remotehost->addr; ai; ai = ai->ai_next, aliasindex++) { sockfd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); if (sockfd < 0) continue; if(conn->data->set.device) { /* user selected to bind the outgoing socket to a specified "device" before doing connect */ CURLcode res = bindlocal(conn, sockfd); if(res) return res; } /* set socket non-blocking */ Curl_nonblock(sockfd, TRUE); rc = connect(sockfd, ai->ai_addr, ai->ai_addrlen); if(-1 == rc) { int error=Curl_ourerrno(); switch (error) { case EINPROGRESS: case EWOULDBLOCK: #if defined(EAGAIN) && EAGAIN != EWOULDBLOCK /* On some platforms EAGAIN and EWOULDBLOCK are the * same value, and on others they are different, hence * the odd #if */ case EAGAIN: #endif case EINTR: /* asynchronous connect, wait for connect or timeout */ if(data->state.used_interface == Curl_if_multi) /* don't hang when doing multi */ timeout_ms = 0; rc = waitconnect(sockfd, timeout_ms); break; case ECONNREFUSED: /* no one listening */ default: /* unknown error, fallthrough and try another address! */ failf(data, "Failed connect to %s: %d", hostname, error); break; } } if(0 == rc) { /* we might be connected, if the socket says it is OK! Ask it! */ int err; err = socketerror(sockfd); if ((0 == err) || (EISCONN == err)) { /* we are connected, awesome! */ *connected = TRUE; /* this is truly a connect */ break; } failf(data, "socket error: %d", err); /* we are _not_ connected, it was a false alert, continue please */ } else if(2 == rc) /* waitconnect() returned error */ ; else if(data->state.used_interface == Curl_if_multi) { /* When running the multi interface, we bail out here */ rc = 0; break; } /* connect failed or timed out */ sclose(sockfd); sockfd = -1; /* get a new timeout for next attempt */ after = Curl_tvnow(); timeout_ms -= Curl_tvdiff(after, before); if(timeout_ms < 0) { failf(data, "connect() timed out!"); return CURLE_OPERATION_TIMEOUTED; } before = after; continue; } if (sockfd < 0) return CURLE_COULDNT_CONNECT; /* leave the socket in non-blocking mode */ if(addr) *addr = ai; /* the address we ended up connected to */ } #else /* * Connecting with IPv4-only support */ if(!remotehost->addr->h_addr_list[0]) { /* If there is no addresses in the address list, then we return error right away */ failf(data, "no address available"); return CURLE_COULDNT_CONNECT; } /* create an IPv4 TCP socket */ sockfd = socket(AF_INET, SOCK_STREAM, 0); if(-1 == sockfd) { failf(data, "couldn't create socket"); return CURLE_COULDNT_CONNECT; /* big time error */ } if(conn->data->set.device) { /* user selected to bind the outgoing socket to a specified "device" before doing connect */ CURLcode res = bindlocal(conn, sockfd); if(res) return res; } /* Convert socket to non-blocking type */ Curl_nonblock(sockfd, TRUE); /* This is the loop that attempts to connect to all IP-addresses we know for the given host. One by one. */ for(rc=-1, aliasindex=0; rc && (struct in_addr *)remotehost->addr->h_addr_list[aliasindex]; aliasindex++) { struct sockaddr_in serv_addr; /* do this nasty work to do the connect */ memset((char *) &serv_addr, '\0', sizeof(serv_addr)); memcpy((char *)&(serv_addr.sin_addr), (struct in_addr *)remotehost->addr->h_addr_list[aliasindex], sizeof(struct in_addr)); serv_addr.sin_family = remotehost->addr->h_addrtype; serv_addr.sin_port = htons((unsigned short)port); rc = connect(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)); if(-1 == rc) { int error=Curl_ourerrno(); switch (error) { case EINPROGRESS: case EWOULDBLOCK: #if defined(EAGAIN) && EAGAIN != EWOULDBLOCK /* On some platforms EAGAIN and EWOULDBLOCK are the * same value, and on others they are different, hence * the odd #if */ case EAGAIN: #endif /* asynchronous connect, wait for connect or timeout */ if(data->state.used_interface == Curl_if_multi) /* don't hang when doing multi */ timeout_ms = 0; rc = waitconnect(sockfd, timeout_ms); break; default: /* unknown error, fallthrough and try another address! */ failf(data, "Failed to connect to %s IP number %d: %d", hostname, aliasindex+1, error); break; } } /* The '1 == rc' comes from the waitconnect(), and not from connect(). We can be sure of this since connect() cannot return 1. */ if((1 == rc) && (data->state.used_interface == Curl_if_multi)) { /* Timeout when running the multi interface, we return here with a CURLE_OK return code. */ rc = 0; break; } if(0 == rc) { int err = socketerror(sockfd); if ((0 == err) || (EISCONN == err)) { /* we are connected, awesome! */ *connected = TRUE; /* this is a true connect */ break; } /* nope, not connected for real */ rc = -1; } if(0 != rc) { /* get a new timeout for next attempt */ after = Curl_tvnow(); timeout_ms -= Curl_tvdiff(after, before); if(timeout_ms < 0) { failf(data, "Connect timeout on IP number %d", aliasindex+1); break; } before = after; continue; /* try next address */ } break; } if(0 != rc) { /* no good connect was made */ sclose(sockfd); *sockconn = -1; failf(data, "Connect failed"); return CURLE_COULDNT_CONNECT; } /* leave the socket in non-blocking mode */ if(addr) /* this is the address we've connected to */ *addr = (struct in_addr *)remotehost->addr->h_addr_list[aliasindex]; #endif /* allow NULL-pointers to get passed in */ if(sockconn) *sockconn = sockfd; /* the socket descriptor we've connected */ return CURLE_OK; }