/* retrieves the start/end point information of a socket of an established connection */ void Curl_updateconninfo(struct connectdata *conn, curl_socket_t sockfd) { curl_socklen_t len; struct Curl_sockaddr_storage ssrem; struct Curl_sockaddr_storage ssloc; struct SessionHandle *data = conn->data; if(conn->socktype == SOCK_DGRAM) /* there's no connection! */ return; if(!conn->bits.reuse) { int error; len = sizeof(struct Curl_sockaddr_storage); if(getpeername(sockfd, (struct sockaddr*) &ssrem, &len)) { error = SOCKERRNO; failf(data, "getpeername() failed with errno %d: %s", error, Curl_strerror(conn, error)); return; } len = sizeof(struct Curl_sockaddr_storage); memset(&ssloc, 0, sizeof(ssloc)); if(getsockname(sockfd, (struct sockaddr*) &ssloc, &len)) { error = SOCKERRNO; failf(data, "getsockname() failed with errno %d: %s", error, Curl_strerror(conn, error)); return; } if(!getaddressinfo((struct sockaddr*)&ssrem, conn->primary_ip, &conn->primary_port)) { error = ERRNO; failf(data, "ssrem inet_ntop() failed with errno %d: %s", error, Curl_strerror(conn, error)); return; } memcpy(conn->ip_addr_str, conn->primary_ip, MAX_IPADR_LEN); if(!getaddressinfo((struct sockaddr*)&ssloc, conn->local_ip, &conn->local_port)) { error = ERRNO; failf(data, "ssloc inet_ntop() failed with errno %d: %s", error, Curl_strerror(conn, error)); return; } } /* persist connection info in session handle */ Curl_persistconninfo(conn); }
/* retrieves the start/end point information of a socket of an established connection */ void Curl_updateconninfo(struct connectdata *conn, curl_socket_t sockfd) { int error; curl_socklen_t len; struct Curl_sockaddr_storage ssrem; struct Curl_sockaddr_storage ssloc; struct SessionHandle *data = conn->data; if(!conn->bits.reuse) { len = sizeof(struct Curl_sockaddr_storage); if(getpeername(sockfd, (struct sockaddr*) &ssrem, &len)) { error = SOCKERRNO; failf(data, "getpeername() failed with errno %d: %s", error, Curl_strerror(conn, error)); return; } len = sizeof(struct Curl_sockaddr_storage); if(getsockname(sockfd, (struct sockaddr*) &ssloc, &len)) { error = SOCKERRNO; failf(data, "getsockname() failed with errno %d: %s", error, Curl_strerror(conn, error)); return; } if(!getaddressinfo((struct sockaddr*)&ssrem, conn->primary_ip, &conn->primary_port)) { error = ERRNO; failf(data, "ssrem inet_ntop() failed with errno %d: %s", error, Curl_strerror(conn, error)); return; } if(!getaddressinfo((struct sockaddr*)&ssloc, conn->local_ip, &conn->local_port)) { error = ERRNO; failf(data, "ssloc inet_ntop() failed with errno %d: %s", error, Curl_strerror(conn, error)); return; } } /* persist connection info in session handle */ Curl_persistconninfo(conn); }
/* * 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 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; }