/* The preferred method on Mac OS X (10.2 and later) to prevent SIGPIPEs when sending data to a dead peer (instead of relying on the 4th argument to send being MSG_NOSIGNAL). Possibly also existing and in use on other BSD systems? */ static void nosigpipe(struct connectdata *conn, curl_socket_t sockfd) { struct SessionHandle *data= conn->data; int onoff = 1; if(setsockopt(sockfd, SOL_SOCKET, SO_NOSIGPIPE, (void *)&onoff, sizeof(onoff)) < 0) infof(data, "Could not set SO_NOSIGPIPE: %s\n", Curl_strerror(conn, SOCKERRNO)); }
/* * Curl_wait_for_resolv() waits for a resolve to finish. This function should * be avoided since using this risk getting the multi interface to "hang". * * If 'entry' is non-NULL, make it point to the resolved dns entry * * This is the version for resolves-in-a-thread. */ CURLcode Curl_wait_for_resolv(struct connectdata *conn, struct Curl_dns_entry **entry) { struct thread_data *td = (struct thread_data*) conn->async.os_specific; struct SessionHandle *data = conn->data; CURLcode rc = CURLE_OK; DEBUGASSERT(conn && td); /* wait for the thread to resolve the name */ if (Curl_thread_join(&td->thread_hnd)) { rc = getaddrinfo_complete(conn); } else { DEBUGASSERT(0); } conn->async.done = TRUE; if(entry) *entry = conn->async.dns; if(!conn->async.dns) { /* a name was not resolved */ if (conn->bits.httpproxy) { failf(data, "Could not resolve proxy: %s; %s", conn->async.hostname, Curl_strerror(conn, conn->async.status)); rc = CURLE_COULDNT_RESOLVE_PROXY; } else { failf(data, "Could not resolve host: %s; %s", conn->async.hostname, Curl_strerror(conn, conn->async.status)); rc = CURLE_COULDNT_RESOLVE_HOST; } } Curl_destroy_thread_data(&conn->async); if(!conn->async.dns) conn->bits.close = TRUE; return (rc); }
/* NetWare has getaddrinfo but lacks gai_strerror. Windows has a gai_strerror but it is bad (not thread-safe) and the generic socket error string function can be used for this pupose. */ static const char *gai_strerror(int ecode) { switch (ecode) { case EAI_AGAIN: return "The name could not be resolved at this time"; case EAI_BADFLAGS: return "The flags parameter had an invalid value"; case EAI_FAIL: return "A non-recoverable error occurred when attempting to " "resolve the name"; case EAI_FAMILY: return "The address family was not recognized"; case EAI_MEMORY: return "Out of memory"; case EAI_NONAME: return "The name does not resolve for the supplied parameters"; case EAI_SERVICE: return "The service passed was not recognized for the " "specified socket type" case EAI_SOCKTYPE: return "The intended socket type was not recognized" case EAI_SYSTEM: return "A system error occurred"; case EAI_OVERFLOW: return "An argument buffer overflowed"; default: return "Unknown error"; /* define this now as this is a private implementation of said function */ #define HAVE_GAI_STRERROR } #endif /* * resolver_error() calls failf() with the appropriate message after a resolve * error */ static void resolver_error(struct connectdata *conn, const char *host_or_proxy) { failf(conn->data, "Could not resolve %s: %s; %s", host_or_proxy, conn->async.hostname, #ifdef HAVE_GAI_STRERROR /* NetWare doesn't have gai_strerror and on Windows it isn't deemed thread-safe */ gai_strerror(conn->async.status) #else Curl_strerror(conn, conn->async.status) #endif ); }
ssize_t Curl_send_plain(struct connectdata *conn, int num, const void *mem, size_t len, CURLcode *code) { curl_socket_t sockfd = conn->sock[num]; ssize_t bytes_written; /* WinSock will destroy unread received data if send() is failed. To avoid lossage of received data, recv() must be performed before every send() if any incoming data is available. */ pre_receive_plain(conn, num); #if defined(MSG_FASTOPEN) && !defined(TCP_FASTOPEN_CONNECT) /* Linux */ if(conn->bits.tcp_fastopen) { bytes_written = sendto(sockfd, mem, len, MSG_FASTOPEN, conn->ip_addr->ai_addr, conn->ip_addr->ai_addrlen); conn->bits.tcp_fastopen = FALSE; } else #endif bytes_written = swrite(sockfd, mem, len); *code = CURLE_OK; if(-1 == bytes_written) { int err = SOCKERRNO; if( #ifdef WSAEWOULDBLOCK /* This is how Windows does it */ (WSAEWOULDBLOCK == err) #else /* errno may be EWOULDBLOCK or on some systems EAGAIN when it returned due to its inability to send off data without blocking. We therefore treat both error codes the same here */ (EWOULDBLOCK == err) || (EAGAIN == err) || (EINTR == err) || (EINPROGRESS == err) #endif ) { /* this is just a case of EWOULDBLOCK */ bytes_written = 0; *code = CURLE_AGAIN; } else { char buffer[STRERROR_LEN]; failf(conn->data, "Send failure: %s", Curl_strerror(err, buffer, sizeof(buffer))); conn->data->state.os_errno = err; *code = CURLE_SEND_ERROR; } } return bytes_written; }
static void dump_addrinfo (struct connectdata *conn, const struct addrinfo *ai) { TRACE(("dump_addrinfo:\n")); for ( ; ai; ai = ai->ai_next) { char buf [INET6_ADDRSTRLEN]; trace_it(" fam %2d, CNAME %s, ", ai->ai_family, ai->ai_canonname ? ai->ai_canonname : "<none>"); if (Curl_printable_address(ai, buf, sizeof(buf))) trace_it("%s\n", buf); else trace_it("failed; %s\n", Curl_strerror(conn, SOCKERRNO)); } }
/* * init_resolve_thread() starts a new thread that performs the actual * resolve. This function returns before the resolve is done. * * Returns FALSE in case of failure, otherwise TRUE. */ static bool init_resolve_thread (struct connectdata *conn, const char *hostname, int port, const Curl_addrinfo *hints) { struct thread_data *td = calloc(sizeof(*td), 1); if (!td) { SetLastError(ENOMEM); return FALSE; } Curl_safefree(conn->async.hostname); conn->async.hostname = strdup(hostname); if (!conn->async.hostname) { free(td); SetLastError(ENOMEM); return FALSE; } conn->async.port = port; conn->async.done = FALSE; conn->async.status = 0; conn->async.dns = NULL; conn->async.os_specific = (void*) td; td->dummy_sock = CURL_SOCKET_BAD; td->stderr_file = stderr; td->thread_hnd = (HANDLE) _beginthreadex(NULL, 0, THREAD_FUNC, conn, 0, &td->thread_id); #ifdef CURLRES_IPV6 curlassert(hints); td->hints = *hints; #else (void) hints; #endif if (!td->thread_hnd) { SetLastError(errno); TRACE(("_beginthreadex() failed; %s\n", Curl_strerror(conn,errno))); destroy_thread_data(&conn->async); return FALSE; } /* This socket is only to keep Curl_fdset() and select() happy; should never * become signalled for read/write since it's unbound but Windows needs * atleast 1 socket in select(). */ td->dummy_sock = socket(AF_INET, SOCK_DGRAM, 0); return TRUE; }
/* * Curl_write() is an internal write function that sends plain (binary) data * to the server. Works with plain sockets, SSL or kerberos. */ CURLcode Curl_write(struct connectdata *conn, curl_socket_t sockfd, void *mem, size_t len, ssize_t *written) { ssize_t bytes_written; CURLcode retcode; int num = (sockfd == conn->sock[SECONDARYSOCKET]); if (conn->ssl[num].use) /* only TRUE if SSL enabled */ bytes_written = Curl_ssl_send(conn, num, mem, len); else { if(conn->sec_complete) /* only TRUE if krb4 enabled */ bytes_written = Curl_sec_write(conn, sockfd, mem, len); else bytes_written = (ssize_t)swrite(sockfd, mem, len); if(-1 == bytes_written) { int err = Curl_ourerrno(); if( #ifdef WSAEWOULDBLOCK /* This is how Windows does it */ (WSAEWOULDBLOCK == err) #else /* As pointed out by Christophe Demory on March 11 2003, errno may be EWOULDBLOCK or on some systems EAGAIN when it returned due to its inability to send off data without blocking. We therefor treat both error codes the same here */ (EWOULDBLOCK == err) || (EAGAIN == err) || (EINTR == err) #endif ) /* this is just a case of EWOULDBLOCK */ bytes_written=0; else failf(conn->data, "Send failure: %s", Curl_strerror(conn, err)); } } *written = bytes_written; retcode = (-1 != bytes_written)?CURLE_OK:CURLE_SEND_ERROR; return retcode; }
static void tcpnodelay(struct connectdata *conn, curl_socket_t sockfd) { #ifdef TCP_NODELAY struct SessionHandle *data= conn->data; socklen_t onoff = (socklen_t) data->set.tcp_nodelay; if(setsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY, (void *)&onoff, sizeof(onoff)) < 0) infof(data, "Could not set TCP_NODELAY: %s\n", Curl_strerror(conn, Curl_ourerrno())); else infof(data,"TCP_NODELAY set\n"); #else (void)conn; (void)sockfd; #endif }
ssize_t Curl_recv_plain(struct connectdata *conn, int num, char *buf, size_t len, CURLcode *code) { curl_socket_t sockfd = conn->sock[num]; ssize_t nread; /* Check and return data that already received and storied in internal intermediate buffer */ nread = get_pre_recved(conn, num, buf, len); if(nread > 0) { *code = CURLE_OK; return nread; } nread = sread(sockfd, buf, len); *code = CURLE_OK; if(-1 == nread) { int err = SOCKERRNO; if( #ifdef WSAEWOULDBLOCK /* This is how Windows does it */ (WSAEWOULDBLOCK == err) #else /* errno may be EWOULDBLOCK or on some systems EAGAIN when it returned due to its inability to send off data without blocking. We therefore treat both error codes the same here */ (EWOULDBLOCK == err) || (EAGAIN == err) || (EINTR == err) #endif ) { /* this is just a case of EWOULDBLOCK */ *code = CURLE_AGAIN; } else { char buffer[STRERROR_LEN]; failf(conn->data, "Recv failure: %s", Curl_strerror(err, buffer, sizeof(buffer))); conn->data->state.os_errno = err; *code = CURLE_RECV_ERROR; } } return nread; }
void Curl_tcpnodelay(struct connectdata *conn, curl_socket_t sockfd) { #if defined(TCP_NODELAY) curl_socklen_t onoff = (curl_socklen_t) 1; int level = IPPROTO_TCP; (void) conn; if(setsockopt(sockfd, level, TCP_NODELAY, (void *)&onoff, sizeof(onoff)) < 0) infof(conn->data, "Could not set TCP_NODELAY: %s\n", Curl_strerror(conn, SOCKERRNO)); else infof(conn->data, "TCP_NODELAY set\n"); #else (void)conn; (void)sockfd; #endif }
/* * Curl_is_resolved() is called repeatedly to check if a previous name resolve * request has completed. It should also make sure to time-out if the * operation seems to take too long. */ CURLcode Curl_is_resolved(struct connectdata *conn, struct Curl_dns_entry **entry) { struct SessionHandle *data = conn->data; *entry = NULL; if(conn->async.done) { /* we're done */ Curl_destroy_thread_data(&conn->async); if(!conn->async.dns) { failf(data, "Could not resolve host: %s; %s", conn->host.name, Curl_strerror(conn, conn->async.status)); return CURLE_COULDNT_RESOLVE_HOST; } *entry = conn->async.dns; } return CURLE_OK; }
void Curl_tcpnodelay(struct connectdata *conn, curl_socket_t sockfd) { #if defined(TCP_NODELAY) #if !defined(CURL_DISABLE_VERBOSE_STRINGS) struct SessionHandle *data = conn->data; #endif curl_socklen_t onoff = (curl_socklen_t) 1; int level = IPPROTO_TCP; #if 0 /* The use of getprotobyname() is disabled since it isn't thread-safe on numerous systems. On these getprotobyname_r() should be used instead, but that exists in at least one 4 arg version and one 5 arg version, and since the proto number rarely changes anyway we now just use the hard coded number. The "proper" fix would need a configure check for the correct function much in the same style the gethostbyname_r versions are detected. */ struct protoent *pe = getprotobyname("tcp"); if(pe) level = pe->p_proto; #endif #if defined(CURL_DISABLE_VERBOSE_STRINGS) (void) conn; #endif if(setsockopt(sockfd, level, TCP_NODELAY, (void *)&onoff, sizeof(onoff)) < 0) infof(data, "Could not set TCP_NODELAY: %s\n", Curl_strerror(conn, SOCKERRNO)); else infof(data, "TCP_NODELAY set\n"); #else (void)conn; (void)sockfd; #endif }
void Curl_tcpnodelay(struct connectdata *conn, curl_socket_t sockfd) { #if defined(TCP_NODELAY) #if !defined(CURL_DISABLE_VERBOSE_STRINGS) struct Curl_easy *data = conn->data; #endif curl_socklen_t onoff = (curl_socklen_t) 1; int level = IPPROTO_TCP; #if defined(CURL_DISABLE_VERBOSE_STRINGS) (void) conn; #endif if(setsockopt(sockfd, level, TCP_NODELAY, (void *)&onoff, sizeof(onoff)) < 0) infof(data, "Could not set TCP_NODELAY: %s\n", Curl_strerror(conn, SOCKERRNO)); else infof(data, "TCP_NODELAY set\n"); #else (void)conn; (void)sockfd; #endif }
CURLcode Curl_is_connected(struct connectdata *conn, int sockindex, bool *connected) { int rc; struct SessionHandle *data = conn->data; CURLcode code = CURLE_OK; curl_socket_t sockfd = conn->sock[sockindex]; long allow = DEFAULT_CONNECT_TIMEOUT; DEBUGASSERT(sockindex >= FIRSTSOCKET && sockindex <= SECONDARYSOCKET); *connected = FALSE; /* a very negative world view is best */ if(conn->bits.tcpconnect) { /* we are connected already! */ long allow_total = 0; /* subtract the most strict timeout of the ones */ if(data->set.timeout) allow_total = data->set.timeout; Curl_expire(data, allow_total); *connected = TRUE; return CURLE_OK; } /* figure out how long time we have left to connect */ allow = Curl_timeleft(conn, NULL, TRUE); if(allow < 0) { /* time-out, bail out, go home */ failf(data, "Connection time-out"); return CURLE_OPERATION_TIMEDOUT; } Curl_expire(data, allow); /* check for connect without timeout as we want to return immediately */ rc = waitconnect(conn, sockfd, 0); if(WAITCONN_CONNECTED == rc) { int error; if(verifyconnect(sockfd, &error)) { /* we are connected, awesome! */ conn->bits.tcpconnect = TRUE; *connected = TRUE; Curl_pgrsTime(data, TIMER_CONNECT); /* connect done */ Curl_verboseconnect(conn); Curl_updateconninfo(conn, sockfd); return CURLE_OK; } /* nope, not connected for real */ data->state.os_errno = error; infof(data, "Connection failed\n"); if(trynextip(conn, sockindex, connected)) { failf(data, "Failed connect to %s:%ld; %s", conn->host.name, conn->port, Curl_strerror(conn, error)); code = CURLE_COULDNT_CONNECT; } } else if(WAITCONN_TIMEOUT != rc) { int error = 0; /* nope, not connected */ if(WAITCONN_FDSET_ERROR == rc) { (void)verifyconnect(sockfd, &error); data->state.os_errno = error; infof(data, "%s\n",Curl_strerror(conn,error)); } else infof(data, "Connection failed\n"); if(trynextip(conn, sockindex, connected)) { error = SOCKERRNO; data->state.os_errno = error; failf(data, "Failed connect to %s:%ld; %s", conn->host.name, conn->port, Curl_strerror(conn, error)); code = CURLE_COULDNT_CONNECT; } } /* * If the connection failed here, we should attempt to connect to the "next * address" for the given host. */ return code; }
static CURLcode bindlocal(struct connectdata *conn, curl_socket_t sockfd, int af) { struct SessionHandle *data = conn->data; struct Curl_sockaddr_storage sa; struct sockaddr *sock = (struct sockaddr *)&sa; /* bind to this address */ curl_socklen_t sizeof_sa = 0; /* size of the data sock points to */ struct sockaddr_in *si4 = (struct sockaddr_in *)&sa; #ifdef ENABLE_IPV6 struct sockaddr_in6 *si6 = (struct sockaddr_in6 *)&sa; #endif struct Curl_dns_entry *h=NULL; unsigned short port = data->set.localport; /* use this port number, 0 for "random" */ /* how many port numbers to try to bind to, increasing one at a time */ int portnum = data->set.localportrange; const char *dev = data->set.str[STRING_DEVICE]; int error; char myhost[256] = ""; int done = 0; /* -1 for error, 1 for address found */ /************************************************************* * Select device to bind socket to *************************************************************/ if ( !dev && !port ) /* no local kind of binding was requested */ return CURLE_OK; memset(&sa, 0, sizeof(struct Curl_sockaddr_storage)); if(dev && (strlen(dev)<255) ) { /* interface */ if(Curl_if2ip(af, dev, myhost, sizeof(myhost))) { /* * We now have the numerical IP address in the 'myhost' buffer */ infof(data, "Local Interface %s is ip %s using address family %i\n", dev, myhost, af); done = 1; #ifdef SO_BINDTODEVICE /* I am not sure any other OSs than Linux that provide this feature, and * at the least I cannot test. --Ben * * This feature allows one to tightly bind the local socket to a * particular interface. This will force even requests to other local * interfaces to go out the external interface. * * * Only bind to the interface when specified as interface, not just as a * hostname or ip address. */ if(setsockopt(sockfd, SOL_SOCKET, SO_BINDTODEVICE, dev, (curl_socklen_t)strlen(dev)+1) != 0) { error = SOCKERRNO; infof(data, "SO_BINDTODEVICE %s failed with errno %d: %s;" " will do regular bind\n", dev, error, Curl_strerror(conn, error)); /* This is typically "errno 1, error: Operation not permitted" if you're not running as root or another suitable privileged user */ } #endif } else { /* * This was not an interface, resolve the name as a host name * or IP number * * Temporarily force name resolution to use only the address type * of the connection. The resolve functions should really be changed * to take a type parameter instead. */ long ipver = data->set.ip_version; int rc; if (af == AF_INET) data->set.ip_version = CURL_IPRESOLVE_V4; #ifdef ENABLE_IPV6 else if (af == AF_INET6) data->set.ip_version = CURL_IPRESOLVE_V6; #endif rc = Curl_resolv(conn, dev, 0, &h); if(rc == CURLRESOLV_PENDING) (void)Curl_wait_for_resolv(conn, &h); data->set.ip_version = ipver; if(h) { /* convert the resolved address, sizeof myhost >= INET_ADDRSTRLEN */ Curl_printable_address(h->addr, myhost, sizeof(myhost)); infof(data, "Name '%s' family %i resolved to '%s' family %i\n", dev, af, myhost, h->addr->ai_family); Curl_resolv_unlock(data, h); done = 1; } else { /* * provided dev was no interface (or interfaces are not supported * e.g. solaris) no ip address and no domain we fail here */ done = -1; } } if(done > 0) { #ifdef ENABLE_IPV6 /* ipv6 address */ if((af == AF_INET6) && (Curl_inet_pton(AF_INET6, myhost, &si6->sin6_addr) > 0)) { si6->sin6_family = AF_INET6; si6->sin6_port = htons(port); sizeof_sa = sizeof(struct sockaddr_in6); } else #endif /* ipv4 address */ if((af == AF_INET) && (Curl_inet_pton(AF_INET, myhost, &si4->sin_addr) > 0)) { si4->sin_family = AF_INET; si4->sin_port = htons(port); sizeof_sa = sizeof(struct sockaddr_in); } } if(done < 1) { failf(data, "Couldn't bind to '%s'", dev); return CURLE_INTERFACE_FAILED; } } else { /* no device was given, prepare sa to match af's needs */ #ifdef ENABLE_IPV6 if ( af == AF_INET6 ) { si6->sin6_family = AF_INET6; si6->sin6_port = htons(port); sizeof_sa = sizeof(struct sockaddr_in6); } else #endif if ( af == AF_INET ) { si4->sin_family = AF_INET; si4->sin_port = htons(port); sizeof_sa = sizeof(struct sockaddr_in); } } for(;;) { if( bind(sockfd, sock, sizeof_sa) >= 0) { /* we succeeded to bind */ struct Curl_sockaddr_storage add; curl_socklen_t size = sizeof(add); memset(&add, 0, sizeof(struct Curl_sockaddr_storage)); if(getsockname(sockfd, (struct sockaddr *) &add, &size) < 0) { data->state.os_errno = error = SOCKERRNO; failf(data, "getsockname() failed with errno %d: %s", error, Curl_strerror(conn, error)); return CURLE_INTERFACE_FAILED; } infof(data, "Local port: %hu\n", port); conn->bits.bound = TRUE; return CURLE_OK; } if(--portnum > 0) { infof(data, "Bind to local port %hu failed, trying next\n", port); port++; /* try next port */ /* We re-use/clobber the port variable here below */ if(sock->sa_family == AF_INET) si4->sin_port = ntohs(port); #ifdef ENABLE_IPV6 else si6->sin6_port = ntohs(port); #endif } else break; } data->state.os_errno = error = SOCKERRNO; failf(data, "bind failed with errno %d: %s", error, Curl_strerror(conn, error)); return CURLE_INTERFACE_FAILED; }
/* * 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; }
CURLcode Curl_is_connected(struct connectdata *conn, int sockindex, bool *connected) { struct SessionHandle *data = conn->data; CURLcode result = CURLE_OK; long allow; int error = 0; struct timeval now; int rc; int i; DEBUGASSERT(sockindex >= FIRSTSOCKET && sockindex <= SECONDARYSOCKET); *connected = FALSE; /* a very negative world view is best */ if(conn->bits.tcpconnect[sockindex]) { /* we are connected already! */ *connected = TRUE; return CURLE_OK; } now = Curl_tvnow(); /* figure out how long time we have left to connect */ allow = Curl_timeleft(data, &now, TRUE); if(allow < 0) { /* time-out, bail out, go home */ failf(data, "Connection time-out"); return CURLE_OPERATION_TIMEDOUT; } for(i=0; i<2; i++) { if(conn->tempsock[i] == CURL_SOCKET_BAD) continue; #ifdef mpeix /* Call this function once now, and ignore the results. We do this to "clear" the error state on the socket so that we can later read it reliably. This is reported necessary on the MPE/iX operating system. */ (void)verifyconnect(conn->tempsock[i], NULL); #endif /* check socket for connect */ rc = Curl_socket_ready(CURL_SOCKET_BAD, conn->tempsock[i], 0); if(rc == 0) { /* no connection yet */ if(curlx_tvdiff(now, conn->connecttime) >= conn->timeoutms_per_addr) { infof(data, "After %ldms connect time, move on!\n", conn->timeoutms_per_addr); error = ETIMEDOUT; } /* should we try another protocol family? */ if(i == 0 && conn->tempaddr[1] == NULL && curlx_tvdiff(now, conn->connecttime) >= HAPPY_EYEBALLS_TIMEOUT) { trynextip(conn, sockindex, 1); } } else if(rc == CURL_CSELECT_OUT) { if(verifyconnect(conn->tempsock[i], &error)) { /* we are connected with TCP, awesome! */ int other = i ^ 1; /* use this socket from now on */ conn->sock[sockindex] = conn->tempsock[i]; conn->ip_addr = conn->tempaddr[i]; conn->tempsock[i] = CURL_SOCKET_BAD; /* close the other socket, if open */ if(conn->tempsock[other] != CURL_SOCKET_BAD) { Curl_closesocket(conn, conn->tempsock[other]); conn->tempsock[other] = CURL_SOCKET_BAD; } /* see if we need to do any proxy magic first once we connected */ result = Curl_connected_proxy(conn, sockindex); if(result) return result; conn->bits.tcpconnect[sockindex] = TRUE; *connected = TRUE; if(sockindex == FIRSTSOCKET) Curl_pgrsTime(data, TIMER_CONNECT); /* connect done */ Curl_updateconninfo(conn, conn->sock[sockindex]); Curl_verboseconnect(conn); return CURLE_OK; } else infof(data, "Connection failed\n"); } else if(rc & CURL_CSELECT_ERR) (void)verifyconnect(conn->tempsock[i], &error); /* * The connection failed here, we should attempt to connect to the "next * address" for the given host. But first remember the latest error. */ if(error) { data->state.os_errno = error; SET_SOCKERRNO(error); if(conn->tempaddr[i]) { char ipaddress[MAX_IPADR_LEN]; Curl_printable_address(conn->tempaddr[i], ipaddress, MAX_IPADR_LEN); infof(data, "connect to %s port %ld failed: %s\n", ipaddress, conn->port, Curl_strerror(conn, error)); conn->timeoutms_per_addr = conn->tempaddr[i]->ai_next == NULL ? allow : allow / 2; result = trynextip(conn, sockindex, i); } } } if(result) { /* no more addresses to try */ /* if the first address family runs out of addresses to try before the happy eyeball timeout, go ahead and try the next family now */ if(conn->tempaddr[1] == NULL) { result = trynextip(conn, sockindex, 1); if(!result) return result; } failf(data, "Failed to connect to %s port %ld: %s", conn->bits.proxy?conn->proxy.name:conn->host.name, conn->port, Curl_strerror(conn, error)); } return result; }
/* * Curl_getaddrinfo() - for Windows threading IPv6 enabled */ Curl_addrinfo *Curl_getaddrinfo(struct connectdata *conn, const char *hostname, int port, int *waitp) { struct addrinfo hints, *res; int error; char sbuf[NI_MAXSERV]; curl_socket_t s; int pf; struct SessionHandle *data = conn->data; *waitp = FALSE; /* default to synch response */ /* see if we have an IPv6 stack */ s = socket(PF_INET6, SOCK_DGRAM, 0); if (s == CURL_SOCKET_BAD) { /* Some non-IPv6 stacks have been found to make very slow name resolves * when PF_UNSPEC is used, so thus we switch to a mere PF_INET lookup if * the stack seems to be a non-ipv6 one. */ pf = PF_INET; } else { /* This seems to be an IPv6-capable stack, use PF_UNSPEC for the widest * possible checks. And close the socket again. */ sclose(s); /* * Check if a more limited name resolve has been requested. */ switch(data->set.ip_version) { case CURL_IPRESOLVE_V4: pf = PF_INET; break; case CURL_IPRESOLVE_V6: pf = PF_INET6; break; default: pf = PF_UNSPEC; break; } } memset(&hints, 0, sizeof(hints)); hints.ai_family = pf; hints.ai_socktype = conn->socktype; #if 0 /* removed nov 8 2005 before 7.15.1 */ hints.ai_flags = AI_CANONNAME; #endif itoa(port, sbuf, 10); /* fire up a new resolver thread! */ if (init_resolve_thread(conn, hostname, port, &hints)) { *waitp = TRUE; /* please wait for the response */ return NULL; } /* fall-back to blocking version */ infof(data, "init_resolve_thread() failed for %s; %s\n", hostname, Curl_strerror(conn, ERRNO)); error = getaddrinfo(hostname, sbuf, &hints, &res); if (error) { infof(data, "getaddrinfo() failed for %s:%d; %s\n", hostname, port, Curl_strerror(conn, SOCKERRNO)); return NULL; } return res; }
/* * Curl_wait_for_resolv() waits for a resolve to finish. This function should * be avoided since using this risk getting the multi interface to "hang". * * If 'entry' is non-NULL, make it point to the resolved dns entry * * This is the version for resolves-in-a-thread. */ CURLcode Curl_wait_for_resolv(struct connectdata *conn, struct Curl_dns_entry **entry) { struct thread_data *td = (struct thread_data*) conn->async.os_specific; struct SessionHandle *data = conn->data; long timeout; DWORD status, ticks; CURLcode rc; DEBUGASSERT(conn && td); /* now, see if there's a connect timeout or a regular timeout to use instead of the default one */ timeout = conn->data->set.connecttimeout ? conn->data->set.connecttimeout : conn->data->set.timeout ? conn->data->set.timeout : CURL_TIMEOUT_RESOLVE * 1000; /* default name resolve timeout */ ticks = GetTickCount(); /* wait for the thread to resolve the name */ status = WaitForSingleObject(td->event_resolved, timeout); /* mark that we are now done waiting */ ReleaseMutex(td->mutex_waiting); /* close our handle to the mutex, no point in hanging on to it */ CloseHandle(td->mutex_waiting); td->mutex_waiting = NULL; /* close the event handle, it's useless now */ CloseHandle(td->event_resolved); td->event_resolved = NULL; /* has the resolver thread succeeded in resolving our query ? */ if (status == WAIT_OBJECT_0) { /* wait for the thread to exit, it's in the callback sequence */ if (WaitForSingleObject(td->thread_hnd, 5000) == WAIT_TIMEOUT) { TerminateThread(td->thread_hnd, 0); conn->async.done = TRUE; td->thread_status = (DWORD)-1; TRACE(("%s() thread stuck?!, ", THREAD_NAME)); } else { /* Thread finished before timeout; propagate Winsock error to this * thread. 'conn->async.done = TRUE' is set in * Curl_addrinfo4/6_callback(). */ SET_SOCKERRNO(conn->async.status); GetExitCodeThread(td->thread_hnd, &td->thread_status); TRACE(("%s() status %lu, thread retval %lu, ", THREAD_NAME, status, td->thread_status)); } } else { conn->async.done = TRUE; td->thread_status = (DWORD)-1; TRACE(("%s() timeout, ", THREAD_NAME)); } TRACE(("elapsed %lu ms\n", GetTickCount()-ticks)); if(entry) *entry = conn->async.dns; rc = CURLE_OK; if (!conn->async.dns) { /* a name was not resolved */ if (td->thread_status == CURLE_OUT_OF_MEMORY) { rc = CURLE_OUT_OF_MEMORY; failf(data, "Could not resolve host: %s", curl_easy_strerror(rc)); } else if(conn->async.done) { if(conn->bits.httpproxy) { failf(data, "Could not resolve proxy: %s; %s", conn->proxy.dispname, Curl_strerror(conn, conn->async.status)); rc = CURLE_COULDNT_RESOLVE_PROXY; } else { failf(data, "Could not resolve host: %s; %s", conn->host.name, Curl_strerror(conn, conn->async.status)); rc = CURLE_COULDNT_RESOLVE_HOST; } } else if (td->thread_status == (DWORD)-1 || conn->async.status == NO_DATA) { failf(data, "Resolving host timed out: %s", conn->host.name); rc = CURLE_OPERATION_TIMEDOUT; } else rc = CURLE_OPERATION_TIMEDOUT; } Curl_destroy_thread_data(&conn->async); if(!conn->async.dns) conn->bits.close = TRUE; return (rc); }
/* * init_resolve_thread() starts a new thread that performs the actual * resolve. This function returns before the resolve is done. * * Returns FALSE in case of failure, otherwise TRUE. */ static bool init_resolve_thread (struct connectdata *conn, const char *hostname, int port, const Curl_addrinfo *hints) { struct thread_data *td = calloc(sizeof(*td), 1); HANDLE thread_and_event[2] = {0}; if (!td) { SET_ERRNO(ENOMEM); return FALSE; } Curl_safefree(conn->async.hostname); conn->async.hostname = strdup(hostname); if (!conn->async.hostname) { free(td); SET_ERRNO(ENOMEM); return FALSE; } conn->async.port = port; conn->async.done = FALSE; conn->async.status = 0; conn->async.dns = NULL; conn->async.os_specific = (void*) td; td->dummy_sock = CURL_SOCKET_BAD; /* Create the mutex used to inform the resolver thread that we're * still waiting, and take initial ownership. */ td->mutex_waiting = CreateMutex(NULL, TRUE, NULL); if (td->mutex_waiting == NULL) { Curl_destroy_thread_data(&conn->async); SET_ERRNO(EAGAIN); return FALSE; } /* Create the event that the thread uses to inform us that it's * done resolving. Do not signal it. */ td->event_resolved = CreateEvent(NULL, TRUE, FALSE, NULL); if (td->event_resolved == NULL) { Curl_destroy_thread_data(&conn->async); SET_ERRNO(EAGAIN); return FALSE; } /* Create the mutex used to serialize access to event_terminated * between us and resolver thread. */ td->mutex_terminate = CreateMutex(NULL, FALSE, NULL); if (td->mutex_terminate == NULL) { Curl_destroy_thread_data(&conn->async); SET_ERRNO(EAGAIN); return FALSE; } /* Create the event used to signal thread that it should terminate. */ td->event_terminate = CreateEvent(NULL, TRUE, FALSE, NULL); if (td->event_terminate == NULL) { Curl_destroy_thread_data(&conn->async); SET_ERRNO(EAGAIN); return FALSE; } /* Create the event used by thread to inform it has initialized its own data. */ td->event_thread_started = CreateEvent(NULL, TRUE, FALSE, NULL); if (td->event_thread_started == NULL) { Curl_destroy_thread_data(&conn->async); SET_ERRNO(EAGAIN); return FALSE; } #ifdef _WIN32_WCE td->thread_hnd = (HANDLE) CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) THREAD_FUNC, conn, 0, &td->thread_id); #else td->thread_hnd = (HANDLE) _beginthreadex(NULL, 0, THREAD_FUNC, conn, 0, &td->thread_id); #endif #ifdef CURLRES_IPV6 DEBUGASSERT(hints); td->hints = *hints; #else (void) hints; #endif if (!td->thread_hnd) { #ifdef _WIN32_WCE TRACE(("CreateThread() failed; %s\n", Curl_strerror(conn, ERRNO))); #else SET_ERRNO(errno); TRACE(("_beginthreadex() failed; %s\n", Curl_strerror(conn, ERRNO))); #endif Curl_destroy_thread_data(&conn->async); return FALSE; } /* Waiting until the thread will initialize its data or it will exit due errors. */ thread_and_event[0] = td->thread_hnd; thread_and_event[1] = td->event_thread_started; if (WaitForMultipleObjects(sizeof(thread_and_event) / sizeof(thread_and_event[0]), (const HANDLE*)thread_and_event, FALSE, INFINITE) == WAIT_FAILED) { /* The resolver thread has been created, * most probably it works now - ignoring this "minor" error */ } /* This socket is only to keep Curl_resolv_fdset() and select() happy; * should never become signalled for read/write since it's unbound but * Windows needs atleast 1 socket in select(). */ td->dummy_sock = socket(AF_INET, SOCK_DGRAM, 0); return TRUE; }
/* * 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; }
CURLcode Curl_is_connected(struct connectdata *conn, int sockindex, bool *connected) { int rc; struct SessionHandle *data = conn->data; CURLcode code = CURLE_OK; curl_socket_t sockfd = conn->sock[sockindex]; long allow = DEFAULT_CONNECT_TIMEOUT; int error = 0; struct timeval now; DEBUGASSERT(sockindex >= FIRSTSOCKET && sockindex <= SECONDARYSOCKET); *connected = FALSE; /* a very negative world view is best */ if(conn->bits.tcpconnect[sockindex]) { /* we are connected already! */ *connected = TRUE; return CURLE_OK; } now = Curl_tvnow(); /* figure out how long time we have left to connect */ allow = Curl_timeleft(data, &now, TRUE); if(allow < 0) { /* time-out, bail out, go home */ failf(data, "Connection time-out"); return CURLE_OPERATION_TIMEDOUT; } /* check for connect without timeout as we want to return immediately */ rc = waitconnect(conn, sockfd, 0); if(WAITCONN_TIMEOUT == rc) { if(curlx_tvdiff(now, conn->connecttime) >= conn->timeoutms_per_addr) { infof(data, "After %ldms connect time, move on!\n", conn->timeoutms_per_addr); goto next; } /* not an error, but also no connection yet */ return code; } if(WAITCONN_CONNECTED == rc) { if(verifyconnect(sockfd, &error)) { /* we are connected with TCP, awesome! */ /* see if we need to do any proxy magic first once we connected */ code = Curl_connected_proxy(conn); if(code) return code; conn->bits.tcpconnect[sockindex] = TRUE; *connected = TRUE; if(sockindex == FIRSTSOCKET) Curl_pgrsTime(data, TIMER_CONNECT); /* connect done */ Curl_verboseconnect(conn); Curl_updateconninfo(conn, sockfd); return CURLE_OK; } /* nope, not connected for real */ } else { /* nope, not connected */ if(WAITCONN_FDSET_ERROR == rc) { (void)verifyconnect(sockfd, &error); infof(data, "%s\n",Curl_strerror(conn, error)); } else infof(data, "Connection failed\n"); } /* * The connection failed here, we should attempt to connect to the "next * address" for the given host. But first remember the latest error. */ if(error) { data->state.os_errno = error; SET_SOCKERRNO(error); } next: conn->timeoutms_per_addr = conn->ip_addr->ai_next == NULL ? allow : allow / 2; code = trynextip(conn, sockindex, connected); if(code) { error = SOCKERRNO; data->state.os_errno = error; failf(data, "Failed connect to %s:%ld; %s", conn->host.name, conn->port, Curl_strerror(conn, error)); } return code; }
CURLcode Curl_is_connected(struct connectdata *conn, int sockindex, bool *connected) { int rc; struct SessionHandle *data = conn->data; CURLcode code = CURLE_OK; curl_socket_t sockfd = conn->sock[sockindex]; long allow = DEFAULT_CONNECT_TIMEOUT; long allow_total = 0; long has_passed; curlassert(sockindex >= FIRSTSOCKET && sockindex <= SECONDARYSOCKET); *connected = FALSE; /* a very negative world view is best */ /* Evaluate in milliseconds how much time that has passed */ has_passed = Curl_tvdiff(Curl_tvnow(), data->progress.t_startsingle); /* subtract the most strict timeout of the ones */ if(data->set.timeout && data->set.connecttimeout) { if (data->set.timeout < data->set.connecttimeout) allow_total = allow = data->set.timeout*1000; else allow = data->set.connecttimeout*1000; } else if(data->set.timeout) { allow_total = allow = data->set.timeout*1000; } else if(data->set.connecttimeout) { allow = data->set.connecttimeout*1000; } if(has_passed > allow ) { /* time-out, bail out, go home */ failf(data, "Connection time-out after %ld ms", has_passed); return CURLE_OPERATION_TIMEOUTED; } if(conn->bits.tcpconnect) { /* we are connected already! */ Curl_expire(data, allow_total); *connected = TRUE; return CURLE_OK; } Curl_expire(data, allow); /* check for connect without timeout as we want to return immediately */ rc = waitconnect(sockfd, 0); if(WAITCONN_CONNECTED == rc) { int error; if (verifyconnect(sockfd, &error)) { /* we are connected, awesome! */ *connected = TRUE; return CURLE_OK; } /* nope, not connected for real */ data->state.os_errno = error; infof(data, "Connection failed\n"); if(trynextip(conn, sockindex, connected)) { code = CURLE_COULDNT_CONNECT; } } else if(WAITCONN_TIMEOUT != rc) { int error = 0; /* nope, not connected */ if (WAITCONN_FDSET_ERROR == rc) { (void)verifyconnect(sockfd, &error); data->state.os_errno = error; infof(data, "%s\n",Curl_strerror(conn,error)); } else infof(data, "Connection failed\n"); if(trynextip(conn, sockindex, connected)) { error = Curl_sockerrno(); data->state.os_errno = error; failf(data, "Failed connect to %s:%d; %s", conn->host.name, conn->port, Curl_strerror(conn,error)); code = CURLE_COULDNT_CONNECT; } } /* * If the connection failed here, we should attempt to connect to the "next * address" for the given host. */ return code; }
/* 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() * * 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; }
/* * Curl_wait_for_resolv() waits for a resolve to finish. This function should * be avoided since using this risk getting the multi interface to "hang". * * If 'entry' is non-NULL, make it point to the resolved dns entry * * This is the version for resolves-in-a-thread. */ CURLcode Curl_wait_for_resolv(struct connectdata *conn, struct Curl_dns_entry **entry) { struct thread_data *td = (struct thread_data*) conn->async.os_specific; struct SessionHandle *data = conn->data; long timeout; DWORD status, ticks; CURLcode rc; curlassert (conn && td); /* now, see if there's a connect timeout or a regular timeout to use instead of the default one */ timeout = conn->data->set.connecttimeout ? conn->data->set.connecttimeout : conn->data->set.timeout ? conn->data->set.timeout : CURL_TIMEOUT_RESOLVE; /* default name resolve timeout */ ticks = GetTickCount(); (void)ticks; status = WaitForSingleObject(td->thread_hnd, 1000UL*timeout); if (status == WAIT_OBJECT_0 || status == WAIT_ABANDONED) { /* Thread finished before timeout; propagate Winsock error to this thread. * 'conn->async.done = TRUE' is set in Curl_addrinfo4/6_callback(). */ WSASetLastError(conn->async.status); GetExitCodeThread(td->thread_hnd, &td->thread_status); TRACE(("%s() status %lu, thread retval %lu, ", THREAD_NAME, status, td->thread_status)); } else { conn->async.done = TRUE; td->thread_status = (DWORD)-1; TRACE(("%s() timeout, ", THREAD_NAME)); } TRACE(("elapsed %lu ms\n", GetTickCount()-ticks)); CloseHandle(td->thread_hnd); if(entry) *entry = conn->async.dns; rc = CURLE_OK; if (!conn->async.dns) { /* a name was not resolved */ if (td->thread_status == (DWORD)-1 || conn->async.status == NO_DATA) { failf(data, "Resolving host timed out: %s", conn->host.name); rc = CURLE_OPERATION_TIMEDOUT; } else if(conn->async.done) { failf(data, "Could not resolve host: %s; %s", conn->host.name, Curl_strerror(conn,conn->async.status)); rc = CURLE_COULDNT_RESOLVE_HOST; } else rc = CURLE_OPERATION_TIMEDOUT; } destroy_thread_data(&conn->async); if(CURLE_OK != rc) /* close the connection, since we must not return failure from here without cleaning up this connection properly */ Curl_disconnect(conn); return (rc); }
static CURLcode bindlocal(struct connectdata *conn, curl_socket_t sockfd) { struct SessionHandle *data = conn->data; struct sockaddr_in me; struct sockaddr *sock = NULL; /* bind to this address */ socklen_t socksize; /* size of the data sock points to */ unsigned short port = data->set.localport; /* use this port number, 0 for "random" */ /* how many port numbers to try to bind to, increasing one at a time */ int portnum = data->set.localportrange; /************************************************************* * Select device to bind socket to *************************************************************/ if (data->set.device && (strlen(data->set.device)<255) ) { struct Curl_dns_entry *h=NULL; char myhost[256] = ""; in_addr_t in; int rc; bool was_iface = FALSE; /* First check if the given name is an IP address */ in=inet_addr(data->set.device); if((in == CURL_INADDR_NONE) && Curl_if2ip(data->set.device, myhost, sizeof(myhost))) { /* * We now have the numerical IPv4-style x.y.z.w in the 'myhost' buffer */ rc = Curl_resolv(conn, myhost, 0, &h); if(rc == CURLRESOLV_PENDING) (void)Curl_wait_for_resolv(conn, &h); if(h) { was_iface = TRUE; Curl_resolv_unlock(data, h); } } if(!was_iface) { /* * This was not an interface, resolve the name as a host name * or IP number */ rc = Curl_resolv(conn, data->set.device, 0, &h); if(rc == CURLRESOLV_PENDING) (void)Curl_wait_for_resolv(conn, &h); if(h) { if(in == CURL_INADDR_NONE) /* convert the resolved address, sizeof myhost >= INET_ADDRSTRLEN */ Curl_inet_ntop(h->addr->ai_addr->sa_family, &((struct sockaddr_in*)h->addr->ai_addr)->sin_addr, myhost, sizeof myhost); else /* we know data->set.device is shorter than the myhost array */ strcpy(myhost, data->set.device); Curl_resolv_unlock(data, h); } } if(! *myhost) { /* need to fix this h=Curl_gethost(data, getmyhost(*myhost,sizeof(myhost)), hostent_buf, sizeof(hostent_buf)); */ failf(data, "Couldn't bind to '%s'", data->set.device); return CURLE_HTTP_PORT_FAILED; } infof(data, "Bind local address to %s\n", myhost); #ifdef SO_BINDTODEVICE /* I am not sure any other OSs than Linux that provide this feature, and * at the least I cannot test. --Ben * * This feature allows one to tightly bind the local socket to a * particular interface. This will force even requests to other local * interfaces to go out the external interface. * */ if (was_iface) { /* Only bind to the interface when specified as interface, not just as a * hostname or ip address. */ if (setsockopt(sockfd, SOL_SOCKET, SO_BINDTODEVICE, data->set.device, strlen(data->set.device)+1) != 0) { /* printf("Failed to BINDTODEVICE, socket: %d device: %s error: %s\n", sockfd, data->set.device, Curl_strerror(Curl_sockerrno())); */ infof(data, "SO_BINDTODEVICE %s failed\n", data->set.device); /* This is typically "errno 1, error: Operation not permitted" if you're not running as root or another suitable privileged user */ } } #endif in=inet_addr(myhost); if (CURL_INADDR_NONE == in) { failf(data,"couldn't find my own IP address (%s)", myhost); return CURLE_HTTP_PORT_FAILED; } /* end of inet_addr */ if ( h ) { Curl_addrinfo *addr = h->addr; sock = addr->ai_addr; socksize = addr->ai_addrlen; } else return CURLE_HTTP_PORT_FAILED; } else if(port) { /* if a local port number is requested but no local IP, extract the address from the socket */ memset(&me, 0, sizeof(struct sockaddr)); me.sin_family = AF_INET; me.sin_addr.s_addr = INADDR_ANY; sock = (struct sockaddr *)&me; socksize = sizeof(struct sockaddr); } else /* no local kind of binding was requested */ return CURLE_OK; do { /* Set port number to bind to, 0 makes the system pick one */ if(sock->sa_family == AF_INET) ((struct sockaddr_in *)sock)->sin_port = htons(port); #ifdef ENABLE_IPV6 else ((struct sockaddr_in6 *)sock)->sin6_port = htons(port); #endif if( bind(sockfd, sock, socksize) >= 0) { /* we succeeded to bind */ struct Curl_sockaddr_storage add; size_t size; size = sizeof(add); if(getsockname(sockfd, (struct sockaddr *) &add, (socklen_t *)&size)<0) { failf(data, "getsockname() failed"); return CURLE_HTTP_PORT_FAILED; } /* We re-use/clobber the port variable here below */ if(((struct sockaddr *)&add)->sa_family == AF_INET) port = ntohs(((struct sockaddr_in *)&add)->sin_port); #ifdef ENABLE_IPV6 else port = ntohs(((struct sockaddr_in6 *)&add)->sin6_port); #endif infof(data, "Local port: %d\n", port); return CURLE_OK; } if(--portnum > 0) { infof(data, "Bind to local port %d failed, trying next\n", port); port++; /* try next port */ } else break; } while(1); data->state.os_errno = Curl_sockerrno(); failf(data, "bind failure: %s", Curl_strerror(conn, data->state.os_errno)); return CURLE_HTTP_PORT_FAILED; }
static CURLcode ntlm_wb_init(struct connectdata *conn, const char *userp) { curl_socket_t sockfds[2]; pid_t child_pid; const char *username; char *slash, *domain = NULL; const char *ntlm_auth = NULL; char *ntlm_auth_alloc = NULL; #if defined(HAVE_GETPWUID_R) && defined(HAVE_GETEUID) struct passwd pw, *pw_res; char pwbuf[1024]; #endif int error; /* Return if communication with ntlm_auth already set up */ if(conn->ntlm_auth_hlpr_socket != CURL_SOCKET_BAD || conn->ntlm_auth_hlpr_pid) return CURLE_OK; username = userp; /* The real ntlm_auth really doesn't like being invoked with an empty username. It won't make inferences for itself, and expects the client to do so (mostly because it's really designed for servers like squid to use for auth, and client support is an afterthought for it). So try hard to provide a suitable username if we don't already have one. But if we can't, provide the empty one anyway. Perhaps they have an implementation of the ntlm_auth helper which *doesn't* need it so we might as well try */ if(!username || !username[0]) { username = getenv("NTLMUSER"); if(!username || !username[0]) username = getenv("LOGNAME"); if(!username || !username[0]) username = getenv("USER"); #if defined(HAVE_GETPWUID_R) && defined(HAVE_GETEUID) if((!username || !username[0]) && !getpwuid_r(geteuid(), &pw, pwbuf, sizeof(pwbuf), &pw_res) && pw_res) { username = pw.pw_name; } #endif if(!username || !username[0]) username = userp; } slash = strpbrk(username, "\\/"); if(slash) { if((domain = strdup(username)) == NULL) return CURLE_OUT_OF_MEMORY; slash = domain + (slash - username); *slash = '\0'; username = username + (slash - domain) + 1; } /* For testing purposes, when DEBUGBUILD is defined and environment variable CURL_NTLM_WB_FILE is set a fake_ntlm is used to perform NTLM challenge/response which only accepts commands and output strings pre-written in test case definitions */ #ifdef DEBUGBUILD ntlm_auth_alloc = curl_getenv("CURL_NTLM_WB_FILE"); if(ntlm_auth_alloc) ntlm_auth = ntlm_auth_alloc; else #endif ntlm_auth = NTLM_WB_FILE; if(access(ntlm_auth, X_OK) != 0) { error = ERRNO; failf(conn->data, "Could not access ntlm_auth: %s errno %d: %s", ntlm_auth, error, Curl_strerror(conn, error)); goto done; } if(socketpair(AF_UNIX, SOCK_STREAM, 0, sockfds)) { error = ERRNO; failf(conn->data, "Could not open socket pair. errno %d: %s", error, Curl_strerror(conn, error)); goto done; } child_pid = fork(); if(child_pid == -1) { error = ERRNO; sclose(sockfds[0]); sclose(sockfds[1]); failf(conn->data, "Could not fork. errno %d: %s", error, Curl_strerror(conn, error)); goto done; } else if(!child_pid) { /* * child process */ /* Don't use sclose in the child since it fools the socket leak detector */ sclose_nolog(sockfds[0]); if(dup2(sockfds[1], STDIN_FILENO) == -1) { error = ERRNO; failf(conn->data, "Could not redirect child stdin. errno %d: %s", error, Curl_strerror(conn, error)); exit(1); } if(dup2(sockfds[1], STDOUT_FILENO) == -1) { error = ERRNO; failf(conn->data, "Could not redirect child stdout. errno %d: %s", error, Curl_strerror(conn, error)); exit(1); } if(domain) execl(ntlm_auth, ntlm_auth, "--helper-protocol", "ntlmssp-client-1", "--use-cached-creds", "--username", username, "--domain", domain, NULL); else execl(ntlm_auth, ntlm_auth, "--helper-protocol", "ntlmssp-client-1", "--use-cached-creds", "--username", username, NULL); error = ERRNO; sclose_nolog(sockfds[1]); failf(conn->data, "Could not execl(). errno %d: %s", error, Curl_strerror(conn, error)); exit(1); } sclose(sockfds[1]); conn->ntlm_auth_hlpr_socket = sockfds[0]; conn->ntlm_auth_hlpr_pid = child_pid; free(domain); free(ntlm_auth_alloc); return CURLE_OK; done: free(domain); free(ntlm_auth_alloc); return CURLE_REMOTE_ACCESS_DENIED; }
/* 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; }
/* * Curl_resolver_getaddrinfo() - for getaddrinfo */ Curl_addrinfo *Curl_resolver_getaddrinfo(struct connectdata *conn, const char *hostname, int port, int *waitp) { struct addrinfo hints; struct in_addr in; Curl_addrinfo *res; int error; char sbuf[12]; int pf = PF_INET; #ifdef CURLRES_IPV6 struct in6_addr in6; #endif /* CURLRES_IPV6 */ *waitp = 0; /* default to synchronous response */ #ifndef USE_RESOLVE_ON_IPS /* First check if this is an IPv4 address string */ if(Curl_inet_pton(AF_INET, hostname, &in) > 0) /* This is a dotted IP address 123.123.123.123-style */ return Curl_ip2addr(AF_INET, &in, hostname, port); #ifdef CURLRES_IPV6 /* check if this is an IPv6 address string */ if(Curl_inet_pton(AF_INET6, hostname, &in6) > 0) /* This is an IPv6 address literal */ return Curl_ip2addr(AF_INET6, &in6, hostname, port); #endif /* CURLRES_IPV6 */ #endif /* !USE_RESOLVE_ON_IPS */ #ifdef CURLRES_IPV6 /* * Check if a limited name resolve has been requested. */ switch(conn->ip_version) { case CURL_IPRESOLVE_V4: pf = PF_INET; break; case CURL_IPRESOLVE_V6: pf = PF_INET6; break; default: pf = PF_UNSPEC; break; } if((pf != PF_INET) && !Curl_ipv6works()) /* The stack seems to be a non-IPv6 one */ pf = PF_INET; #endif /* CURLRES_IPV6 */ memset(&hints, 0, sizeof(hints)); hints.ai_family = pf; hints.ai_socktype = conn->socktype; snprintf(sbuf, sizeof(sbuf), "%d", port); /* fire up a new resolver thread! */ if(init_resolve_thread(conn, hostname, port, &hints)) { *waitp = 1; /* expect asynchronous response */ return NULL; } /* fall-back to blocking version */ infof(conn->data, "init_resolve_thread() failed for %s; %s\n", hostname, Curl_strerror(conn, ERRNO)); error = Curl_getaddrinfo_ex(hostname, sbuf, &hints, &res); if(error) { infof(conn->data, "getaddrinfo() failed for %s:%d; %s\n", hostname, port, Curl_strerror(conn, SOCKERRNO)); return NULL; } else { Curl_addrinfo_set_port(res, port); } return res; }