/* * This function logs in to a SOCKS5 proxy and sends the specifics to the final * destination server. */ CURLcode Curl_SOCKS5(const char *proxy_name, const char *proxy_password, const char *hostname, int remote_port, int sockindex, struct connectdata *conn) { /* According to the RFC1928, section "6. Replies". This is what a SOCK5 replies: +----+-----+-------+------+----------+----------+ |VER | REP | RSV | ATYP | BND.ADDR | BND.PORT | +----+-----+-------+------+----------+----------+ | 1 | 1 | X'00' | 1 | Variable | 2 | +----+-----+-------+------+----------+----------+ Where: o VER protocol version: X'05' o REP Reply field: o X'00' succeeded */ unsigned char socksreq[600]; /* room for large user/pw (255 max each) */ ssize_t actualread; ssize_t written; int result; CURLcode code; curl_socket_t sock = conn->sock[sockindex]; struct SessionHandle *data = conn->data; long timeout; bool socks5_resolve_local = (bool)(data->set.proxytype == CURLPROXY_SOCKS5); const size_t hostname_len = strlen(hostname); ssize_t packetsize = 0; /* RFC1928 chapter 5 specifies max 255 chars for domain name in packet */ if(!socks5_resolve_local && hostname_len > 255) { infof(conn->data,"SOCKS5: server resolving disabled for hostnames of " "length > 255 [actual len=%d]\n", hostname_len); socks5_resolve_local = TRUE; } /* get timeout */ timeout = Curl_timeleft(conn, NULL, TRUE); if(timeout < 0) { /* time-out, bail out, go home */ failf(data, "Connection time-out"); return CURLE_OPERATION_TIMEDOUT; } Curl_nonblock(sock, TRUE); /* wait until socket gets connected */ result = Curl_socket_ready(CURL_SOCKET_BAD, sock, (int)timeout); if(-1 == result) { failf(conn->data, "SOCKS5: no connection here"); return CURLE_COULDNT_CONNECT; } else if(0 == result) { failf(conn->data, "SOCKS5: connection timeout"); return CURLE_OPERATION_TIMEDOUT; } if(result & CURL_CSELECT_ERR) { failf(conn->data, "SOCKS5: error occured during connection"); return CURLE_COULDNT_CONNECT; } socksreq[0] = 5; /* version */ socksreq[1] = (char)(proxy_name ? 2 : 1); /* number of methods (below) */ socksreq[2] = 0; /* no authentication */ socksreq[3] = 2; /* username/password */ Curl_nonblock(sock, FALSE); code = Curl_write_plain(conn, sock, (char *)socksreq, (2 + (int)socksreq[1]), &written); if((code != CURLE_OK) || (written != (2 + (int)socksreq[1]))) { failf(data, "Unable to send initial SOCKS5 request."); return CURLE_COULDNT_CONNECT; } Curl_nonblock(sock, TRUE); result = Curl_socket_ready(sock, CURL_SOCKET_BAD, (int)timeout); if(-1 == result) { failf(conn->data, "SOCKS5 nothing to read"); return CURLE_COULDNT_CONNECT; } else if(0 == result) { failf(conn->data, "SOCKS5 read timeout"); return CURLE_OPERATION_TIMEDOUT; } if(result & CURL_CSELECT_ERR) { failf(conn->data, "SOCKS5 read error occured"); return CURLE_RECV_ERROR; } Curl_nonblock(sock, FALSE); result=blockread_all(conn, sock, (char *)socksreq, 2, &actualread, timeout); if((result != CURLE_OK) || (actualread != 2)) { failf(data, "Unable to receive initial SOCKS5 response."); return CURLE_COULDNT_CONNECT; } if(socksreq[0] != 5) { failf(data, "Received invalid version in initial SOCKS5 response."); return CURLE_COULDNT_CONNECT; } if(socksreq[1] == 0) { /* Nothing to do, no authentication needed */ ; } else if(socksreq[1] == 2) { /* Needs user name and password */ size_t userlen, pwlen; int len; if(proxy_name && proxy_password) { userlen = strlen(proxy_name); pwlen = strlen(proxy_password); } else { userlen = 0; pwlen = 0; } /* username/password request looks like * +----+------+----------+------+----------+ * |VER | ULEN | UNAME | PLEN | PASSWD | * +----+------+----------+------+----------+ * | 1 | 1 | 1 to 255 | 1 | 1 to 255 | * +----+------+----------+------+----------+ */ len = 0; socksreq[len++] = 1; /* username/pw subnegotiation version */ socksreq[len++] = (char) userlen; memcpy(socksreq + len, proxy_name, (int) userlen); len += userlen; socksreq[len++] = (char) pwlen; memcpy(socksreq + len, proxy_password, (int) pwlen); len += pwlen; code = Curl_write_plain(conn, sock, (char *)socksreq, len, &written); if((code != CURLE_OK) || (len != written)) { failf(data, "Failed to send SOCKS5 sub-negotiation request."); return CURLE_COULDNT_CONNECT; } result=blockread_all(conn, sock, (char *)socksreq, 2, &actualread, timeout); if((result != CURLE_OK) || (actualread != 2)) { failf(data, "Unable to receive SOCKS5 sub-negotiation response."); return CURLE_COULDNT_CONNECT; } /* ignore the first (VER) byte */ if(socksreq[1] != 0) { /* status */ failf(data, "User was rejected by the SOCKS5 server (%d %d).", socksreq[0], socksreq[1]); return CURLE_COULDNT_CONNECT; } /* Everything is good so far, user was authenticated! */ } else { /* error */ if(socksreq[1] == 1) { failf(data, "SOCKS5 GSSAPI per-message authentication is not supported."); return CURLE_COULDNT_CONNECT; } else if(socksreq[1] == 255) { if(!proxy_name || !*proxy_name) { failf(data, "No authentication method was acceptable. (It is quite likely" " that the SOCKS5 server wanted a username/password, since none" " was supplied to the server on this connection.)"); } else { failf(data, "No authentication method was acceptable."); } return CURLE_COULDNT_CONNECT; } else { failf(data, "Undocumented SOCKS5 mode attempted to be used by server."); return CURLE_COULDNT_CONNECT; } } /* Authentication is complete, now specify destination to the proxy */ socksreq[0] = 5; /* version (SOCKS5) */ socksreq[1] = 1; /* connect */ socksreq[2] = 0; /* must be zero */ if(!socks5_resolve_local) { packetsize = (ssize_t)(5 + hostname_len + 2); socksreq[3] = 3; /* ATYP: domain name = 3 */ socksreq[4] = (char) hostname_len; /* address length */ memcpy(&socksreq[5], hostname, hostname_len); /* address bytes w/o NULL */ *((unsigned short*)&socksreq[hostname_len+5]) = htons((unsigned short)remote_port); } else { struct Curl_dns_entry *dns; Curl_addrinfo *hp=NULL; int rc = Curl_resolv(conn, hostname, remote_port, &dns); packetsize = 10; socksreq[3] = 1; /* IPv4 = 1 */ if(rc == CURLRESOLV_ERROR) return CURLE_COULDNT_RESOLVE_HOST; if(rc == CURLRESOLV_PENDING) /* this requires that we're in "wait for resolve" state */ rc = Curl_wait_for_resolv(conn, &dns); /* * We cannot use 'hostent' as a struct that Curl_resolv() returns. It * returns a Curl_addrinfo pointer that may not always look the same. */ if(dns) hp=dns->addr; if(hp) { char buf[64]; unsigned short ip[4]; Curl_printable_address(hp, buf, sizeof(buf)); if(4 == sscanf( buf, "%hu.%hu.%hu.%hu", &ip[0], &ip[1], &ip[2], &ip[3])) { socksreq[4] = (unsigned char)ip[0]; socksreq[5] = (unsigned char)ip[1]; socksreq[6] = (unsigned char)ip[2]; socksreq[7] = (unsigned char)ip[3]; } else hp = NULL; /* fail! */ Curl_resolv_unlock(data, dns); /* not used anymore from now on */ } if(!hp) { failf(data, "Failed to resolve \"%s\" for SOCKS5 connect.", hostname); return CURLE_COULDNT_RESOLVE_HOST; } *((unsigned short*)&socksreq[8]) = htons((unsigned short)remote_port); } code = Curl_write_plain(conn, sock, (char *)socksreq, packetsize, &written); if((code != CURLE_OK) || (written != packetsize)) { failf(data, "Failed to send SOCKS5 connect request."); return CURLE_COULDNT_CONNECT; } packetsize = 10; /* minimum packet size is 10 */ result = blockread_all(conn, sock, (char *)socksreq, packetsize, &actualread, timeout); if((result != CURLE_OK) || (actualread != packetsize)) { failf(data, "Failed to receive SOCKS5 connect request ack."); return CURLE_COULDNT_CONNECT; } if(socksreq[0] != 5) { /* version */ failf(data, "SOCKS5 reply has wrong version, version should be 5."); return CURLE_COULDNT_CONNECT; } if(socksreq[1] != 0) { /* Anything besides 0 is an error */ failf(data, "Can't complete SOCKS5 connection to %d.%d.%d.%d:%d. (%d)", (unsigned char)socksreq[4], (unsigned char)socksreq[5], (unsigned char)socksreq[6], (unsigned char)socksreq[7], (unsigned int)ntohs(*(unsigned short*)(&socksreq[8])), socksreq[1]); return CURLE_COULDNT_CONNECT; } /* Fix: in general, returned BND.ADDR is variable length parameter by RFC 1928, so the reply packet should be read until the end to avoid errors at subsequent protocol level. +----+-----+-------+------+----------+----------+ |VER | REP | RSV | ATYP | BND.ADDR | BND.PORT | +----+-----+-------+------+----------+----------+ | 1 | 1 | X'00' | 1 | Variable | 2 | +----+-----+-------+------+----------+----------+ ATYP: o IP v4 address: X'01', BND.ADDR = 4 byte o domain name: X'03', BND.ADDR = [ 1 byte length, string ] o IP v6 address: X'04', BND.ADDR = 16 byte */ /* Calculate real packet size */ if(socksreq[3] == 3) { /* domain name */ int addrlen = (int) socksreq[4]; packetsize = 5 + addrlen + 2; } else if(socksreq[3] == 4) { /* IPv6 */ packetsize = 4 + 16 + 2; } /* At this point we already read first 10 bytes */ if(packetsize > 10) { packetsize -= 10; result = blockread_all(conn, sock, (char *)&socksreq[10], packetsize, &actualread, timeout); if((result != CURLE_OK) || (actualread != packetsize)) { failf(data, "Failed to receive SOCKS5 connect request ack."); return CURLE_COULDNT_CONNECT; } } Curl_nonblock(sock, TRUE); return CURLE_OK; /* Proxy was successful! */ }
/* 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; }
/* * This function logs in to a SOCKS5 proxy and sends the specifics to the final * destination server. */ CURLcode Curl_SOCKS5(const char *proxy_name, const char *proxy_password, struct connectdata *conn) { /* According to the RFC1928, section "6. Replies". This is what a SOCK5 replies: +----+-----+-------+------+----------+----------+ |VER | REP | RSV | ATYP | BND.ADDR | BND.PORT | +----+-----+-------+------+----------+----------+ | 1 | 1 | X'00' | 1 | Variable | 2 | +----+-----+-------+------+----------+----------+ Where: o VER protocol version: X'05' o REP Reply field: o X'00' succeeded */ unsigned char socksreq[600]; /* room for large user/pw (255 max each) */ ssize_t actualread; ssize_t written; int result; CURLcode code; curl_socket_t sock = conn->sock[FIRSTSOCKET]; struct SessionHandle *data = conn->data; long timeout; /* get timeout */ if(data->set.timeout && data->set.connecttimeout) { if (data->set.timeout < data->set.connecttimeout) timeout = data->set.timeout*1000; else timeout = data->set.connecttimeout*1000; } else if(data->set.timeout) timeout = data->set.timeout*1000; else if(data->set.connecttimeout) timeout = data->set.connecttimeout*1000; else timeout = DEFAULT_CONNECT_TIMEOUT; Curl_nonblock(sock, TRUE); /* wait until socket gets connected */ result = Curl_select(CURL_SOCKET_BAD, sock, (int)timeout); if(-1 == result) { failf(conn->data, "SOCKS5: no connection here"); return CURLE_COULDNT_CONNECT; } else if(0 == result) { failf(conn->data, "SOCKS5: connection timeout"); return CURLE_OPERATION_TIMEDOUT; } if(result & CSELECT_ERR) { failf(conn->data, "SOCKS5: error occured during connection"); return CURLE_COULDNT_CONNECT; } socksreq[0] = 5; /* version */ socksreq[1] = (char)(proxy_name ? 2 : 1); /* number of methods (below) */ socksreq[2] = 0; /* no authentication */ socksreq[3] = 2; /* username/password */ Curl_nonblock(sock, FALSE); code = Curl_write(conn, sock, (char *)socksreq, (2 + (int)socksreq[1]), &written); if ((code != CURLE_OK) || (written != (2 + (int)socksreq[1]))) { failf(data, "Unable to send initial SOCKS5 request."); return CURLE_COULDNT_CONNECT; } Curl_nonblock(sock, TRUE); result = Curl_select(sock, CURL_SOCKET_BAD, (int)timeout); if(-1 == result) { failf(conn->data, "SOCKS5 nothing to read"); return CURLE_COULDNT_CONNECT; } else if(0 == result) { failf(conn->data, "SOCKS5 read timeout"); return CURLE_OPERATION_TIMEDOUT; } if(result & CSELECT_ERR) { failf(conn->data, "SOCKS5 read error occured"); return CURLE_RECV_ERROR; } Curl_nonblock(sock, FALSE); result=blockread_all(conn, sock, (char *)socksreq, 2, &actualread, timeout); if ((result != CURLE_OK) || (actualread != 2)) { failf(data, "Unable to receive initial SOCKS5 response."); return CURLE_COULDNT_CONNECT; } if (socksreq[0] != 5) { failf(data, "Received invalid version in initial SOCKS5 response."); return CURLE_COULDNT_CONNECT; } if (socksreq[1] == 0) { /* Nothing to do, no authentication needed */ ; } else if (socksreq[1] == 2) { /* Needs user name and password */ size_t userlen, pwlen; int len; if(proxy_name && proxy_password) { userlen = strlen(proxy_name); pwlen = proxy_password?strlen(proxy_password):0; } else { userlen = 0; pwlen = 0; } /* username/password request looks like * +----+------+----------+------+----------+ * |VER | ULEN | UNAME | PLEN | PASSWD | * +----+------+----------+------+----------+ * | 1 | 1 | 1 to 255 | 1 | 1 to 255 | * +----+------+----------+------+----------+ */ len = 0; socksreq[len++] = 1; /* username/pw subnegotiation version */ socksreq[len++] = (char) userlen; memcpy(socksreq + len, proxy_name, (int) userlen); len += userlen; socksreq[len++] = (char) pwlen; memcpy(socksreq + len, proxy_password, (int) pwlen); len += pwlen; code = Curl_write(conn, sock, (char *)socksreq, len, &written); if ((code != CURLE_OK) || (len != written)) { failf(data, "Failed to send SOCKS5 sub-negotiation request."); return CURLE_COULDNT_CONNECT; } result=blockread_all(conn, sock, (char *)socksreq, 2, &actualread, timeout); if ((result != CURLE_OK) || (actualread != 2)) { failf(data, "Unable to receive SOCKS5 sub-negotiation response."); return CURLE_COULDNT_CONNECT; } /* ignore the first (VER) byte */ if (socksreq[1] != 0) { /* status */ failf(data, "User was rejected by the SOCKS5 server (%d %d).", socksreq[0], socksreq[1]); return CURLE_COULDNT_CONNECT; } /* Everything is good so far, user was authenticated! */ } else { /* error */ if (socksreq[1] == 1) { failf(data, "SOCKS5 GSSAPI per-message authentication is not supported."); return CURLE_COULDNT_CONNECT; } else if (socksreq[1] == 255) { if (!proxy_name || !*proxy_name) { failf(data, "No authentication method was acceptable. (It is quite likely" " that the SOCKS5 server wanted a username/password, since none" " was supplied to the server on this connection.)"); } else { failf(data, "No authentication method was acceptable."); } return CURLE_COULDNT_CONNECT; } else { failf(data, "Undocumented SOCKS5 mode attempted to be used by server."); return CURLE_COULDNT_CONNECT; } } /* Authentication is complete, now specify destination to the proxy */ socksreq[0] = 5; /* version (SOCKS5) */ socksreq[1] = 1; /* connect */ socksreq[2] = 0; /* must be zero */ socksreq[3] = 1; /* IPv4 = 1 */ { struct Curl_dns_entry *dns; Curl_addrinfo *hp=NULL; int rc = Curl_resolv(conn, conn->host.name, (int)conn->remote_port, &dns); if(rc == CURLRESOLV_ERROR) return CURLE_COULDNT_RESOLVE_HOST; if(rc == CURLRESOLV_PENDING) /* this requires that we're in "wait for resolve" state */ rc = Curl_wait_for_resolv(conn, &dns); /* * We cannot use 'hostent' as a struct that Curl_resolv() returns. It * returns a Curl_addrinfo pointer that may not always look the same. */ if(dns) hp=dns->addr; if (hp) { char buf[64]; unsigned short ip[4]; Curl_printable_address(hp, buf, sizeof(buf)); if(4 == sscanf( buf, "%hu.%hu.%hu.%hu", &ip[0], &ip[1], &ip[2], &ip[3])) { socksreq[4] = (unsigned char)ip[0]; socksreq[5] = (unsigned char)ip[1]; socksreq[6] = (unsigned char)ip[2]; socksreq[7] = (unsigned char)ip[3]; } else hp = NULL; /* fail! */ Curl_resolv_unlock(data, dns); /* not used anymore from now on */ } if(!hp) { failf(data, "Failed to resolve \"%s\" for SOCKS5 connect.", conn->host.name); return CURLE_COULDNT_RESOLVE_HOST; } } *((unsigned short*)&socksreq[8]) = htons(conn->remote_port); { const int packetsize = 10; code = Curl_write(conn, sock, (char *)socksreq, packetsize, &written); if ((code != CURLE_OK) || (written != packetsize)) { failf(data, "Failed to send SOCKS5 connect request."); return CURLE_COULDNT_CONNECT; } result = blockread_all(conn, sock, (char *)socksreq, packetsize, &actualread, timeout); if ((result != CURLE_OK) || (actualread != packetsize)) { failf(data, "Failed to receive SOCKS5 connect request ack."); return CURLE_COULDNT_CONNECT; } if (socksreq[0] != 5) { /* version */ failf(data, "SOCKS5 reply has wrong version, version should be 5."); return CURLE_COULDNT_CONNECT; } if (socksreq[1] != 0) { /* Anything besides 0 is an error */ failf(data, "Can't complete SOCKS5 connection to %d.%d.%d.%d:%d. (%d)", (unsigned char)socksreq[4], (unsigned char)socksreq[5], (unsigned char)socksreq[6], (unsigned char)socksreq[7], (unsigned int)ntohs(*(unsigned short*)(&socksreq[8])), socksreq[1]); return CURLE_COULDNT_CONNECT; } } Curl_nonblock(sock, TRUE); return CURLE_OK; /* Proxy was successful! */ }
/* * This function logs in to a SOCKS4 proxy and sends the specifics to the final * destination server. * * Reference : * http://socks.permeo.com/protocol/socks4.protocol * * Note : * Set protocol4a=true for "SOCKS 4A (Simple Extension to SOCKS 4 Protocol)" * Nonsupport "Identification Protocol (RFC1413)" */ CURLcode Curl_SOCKS4(const char *proxy_name, const char *hostname, int remote_port, int sockindex, struct connectdata *conn, bool protocol4a) { #define SOCKS4REQLEN 262 unsigned char socksreq[SOCKS4REQLEN]; /* room for SOCKS4 request incl. user id */ int result; CURLcode code; curl_socket_t sock = conn->sock[sockindex]; long timeout; struct SessionHandle *data = conn->data; /* get timeout */ timeout = Curl_timeleft(conn, NULL, TRUE); if(timeout < 0) { /* time-out, bail out, go home */ failf(data, "Connection time-out"); return CURLE_OPERATION_TIMEDOUT; } Curl_nonblock(sock, FALSE); /* * Compose socks4 request * * Request format * * +----+----+----+----+----+----+----+----+----+----+....+----+ * | VN | CD | DSTPORT | DSTIP | USERID |NULL| * +----+----+----+----+----+----+----+----+----+----+....+----+ * # of bytes: 1 1 2 4 variable 1 */ socksreq[0] = 4; /* version (SOCKS4) */ socksreq[1] = 1; /* connect */ *((unsigned short*)&socksreq[2]) = htons((unsigned short)remote_port); /* DNS resolve only for SOCKS4, not SOCKS4a */ if (!protocol4a) { struct Curl_dns_entry *dns; Curl_addrinfo *hp=NULL; int rc; rc = Curl_resolv(conn, hostname, remote_port, &dns); if(rc == CURLRESOLV_ERROR) return CURLE_COULDNT_RESOLVE_PROXY; if(rc == CURLRESOLV_PENDING) /* this requires that we're in "wait for resolve" state */ rc = Curl_wait_for_resolv(conn, &dns); /* * We cannot use 'hostent' as a struct that Curl_resolv() returns. It * returns a Curl_addrinfo pointer that may not always look the same. */ if(dns) hp=dns->addr; if(hp) { char buf[64]; unsigned short ip[4]; Curl_printable_address(hp, buf, sizeof(buf)); if(4 == sscanf( buf, "%hu.%hu.%hu.%hu", &ip[0], &ip[1], &ip[2], &ip[3])) { /* Set DSTIP */ socksreq[4] = (unsigned char)ip[0]; socksreq[5] = (unsigned char)ip[1]; socksreq[6] = (unsigned char)ip[2]; socksreq[7] = (unsigned char)ip[3]; } else hp = NULL; /* fail! */ Curl_resolv_unlock(data, dns); /* not used anymore from now on */ } if(!hp) { failf(data, "Failed to resolve \"%s\" for SOCKS4 connect.", hostname); return CURLE_COULDNT_RESOLVE_HOST; } } /* * This is currently not supporting "Identification Protocol (RFC1413)". */ socksreq[8] = 0; /* ensure empty userid is NUL-terminated */ if(proxy_name) strlcat((char*)socksreq + 8, proxy_name, sizeof(socksreq) - 8); /* * Make connection */ { ssize_t actualread; ssize_t written; ssize_t hostnamelen = 0; int packetsize = 9 + (int)strlen((char*)socksreq + 8); /* size including NUL */ /* If SOCKS4a, set special invalid IP address 0.0.0.x */ if (protocol4a) { socksreq[4] = 0; socksreq[5] = 0; socksreq[6] = 0; socksreq[7] = 1; /* If still enough room in buffer, also append hostname */ hostnamelen = (ssize_t)strlen(hostname) + 1; /* length including NUL */ if (packetsize + hostnamelen <= SOCKS4REQLEN) strcpy((char*)socksreq + packetsize, hostname); else hostnamelen = 0; /* Flag: hostname did not fit in buffer */ } /* Send request */ code = Curl_write_plain(conn, sock, (char *)socksreq, packetsize + hostnamelen, &written); if((code != CURLE_OK) || (written != packetsize + hostnamelen)) { failf(data, "Failed to send SOCKS4 connect request."); return CURLE_COULDNT_CONNECT; } if (protocol4a && hostnamelen == 0) { /* SOCKS4a with very long hostname - send that name separately */ hostnamelen = (ssize_t)strlen(hostname) + 1; code = Curl_write_plain(conn, sock, (char *)hostname, hostnamelen, &written); if((code != CURLE_OK) || (written != hostnamelen)) { failf(data, "Failed to send SOCKS4 connect request."); return CURLE_COULDNT_CONNECT; } } packetsize = 8; /* receive data size */ /* Receive response */ result = blockread_all(conn, sock, (char *)socksreq, packetsize, &actualread, timeout); if((result != CURLE_OK) || (actualread != packetsize)) { failf(data, "Failed to receive SOCKS4 connect request ack."); return CURLE_COULDNT_CONNECT; } /* * Response format * * +----+----+----+----+----+----+----+----+ * | VN | CD | DSTPORT | DSTIP | * +----+----+----+----+----+----+----+----+ * # of bytes: 1 1 2 4 * * VN is the version of the reply code and should be 0. CD is the result * code with one of the following values: * * 90: request granted * 91: request rejected or failed * 92: request rejected because SOCKS server cannot connect to * identd on the client * 93: request rejected because the client program and identd * report different user-ids */ /* wrong version ? */ if(socksreq[0] != 0) { failf(data, "SOCKS4 reply has wrong version, version should be 4."); return CURLE_COULDNT_CONNECT; } /* Result */ switch(socksreq[1]) { case 90: if (protocol4a) infof(data, "SOCKS4a request granted.\n"); else infof(data, "SOCKS4 request granted.\n"); break; case 91: failf(data, "Can't complete SOCKS4 connection to %d.%d.%d.%d:%d. (%d)" ", request rejected or failed.", (unsigned char)socksreq[4], (unsigned char)socksreq[5], (unsigned char)socksreq[6], (unsigned char)socksreq[7], (unsigned int)ntohs(*(unsigned short*)(&socksreq[8])), socksreq[1]); return CURLE_COULDNT_CONNECT; case 92: failf(data, "Can't complete SOCKS4 connection to %d.%d.%d.%d:%d. (%d)" ", request rejected because SOCKS server cannot connect to " "identd on the client.", (unsigned char)socksreq[4], (unsigned char)socksreq[5], (unsigned char)socksreq[6], (unsigned char)socksreq[7], (unsigned int)ntohs(*(unsigned short*)(&socksreq[8])), socksreq[1]); return CURLE_COULDNT_CONNECT; case 93: failf(data, "Can't complete SOCKS4 connection to %d.%d.%d.%d:%d. (%d)" ", request rejected because the client program and identd " "report different user-ids.", (unsigned char)socksreq[4], (unsigned char)socksreq[5], (unsigned char)socksreq[6], (unsigned char)socksreq[7], (unsigned int)ntohs(*(unsigned short*)(&socksreq[8])), socksreq[1]); return CURLE_COULDNT_CONNECT; default: failf(data, "Can't complete SOCKS4 connection to %d.%d.%d.%d:%d. (%d)" ", Unknown.", (unsigned char)socksreq[4], (unsigned char)socksreq[5], (unsigned char)socksreq[6], (unsigned char)socksreq[7], (unsigned int)ntohs(*(unsigned short*)(&socksreq[8])), socksreq[1]); return CURLE_COULDNT_CONNECT; } } Curl_nonblock(sock, TRUE); return CURLE_OK; /* Proxy was successful! */ }
/* * This function logs in to a SOCKS4 proxy and sends the specifics to the final * destination server. * * Reference : * http://socks.permeo.com/protocol/socks4.protocol * * Note : * Nonsupport "SOCKS 4A (Simple Extension to SOCKS 4 Protocol)" * Nonsupport "Identification Protocol (RFC1413)" */ CURLcode Curl_SOCKS4(const char *proxy_name, struct connectdata *conn) { unsigned char socksreq[262]; /* room for SOCKS4 request incl. user id */ int result; CURLcode code; curl_socket_t sock = conn->sock[FIRSTSOCKET]; long timeout; struct SessionHandle *data = conn->data; /* get timeout */ if(data->set.timeout && data->set.connecttimeout) { if (data->set.timeout < data->set.connecttimeout) timeout = data->set.timeout*1000; else timeout = data->set.connecttimeout*1000; } else if(data->set.timeout) timeout = data->set.timeout*1000; else if(data->set.connecttimeout) timeout = data->set.connecttimeout*1000; else timeout = DEFAULT_CONNECT_TIMEOUT; Curl_nonblock(sock, FALSE); /* * Compose socks4 request * * Request format * * +----+----+----+----+----+----+----+----+----+----+....+----+ * | VN | CD | DSTPORT | DSTIP | USERID |NULL| * +----+----+----+----+----+----+----+----+----+----+....+----+ * # of bytes: 1 1 2 4 variable 1 */ socksreq[0] = 4; /* version (SOCKS4) */ socksreq[1] = 1; /* connect */ *((unsigned short*)&socksreq[2]) = htons(conn->remote_port); /* DNS resolve */ { struct Curl_dns_entry *dns; Curl_addrinfo *hp=NULL; int rc; rc = Curl_resolv(conn, conn->host.name, (int)conn->remote_port, &dns); if(rc == CURLRESOLV_ERROR) return CURLE_COULDNT_RESOLVE_PROXY; if(rc == CURLRESOLV_PENDING) /* this requires that we're in "wait for resolve" state */ rc = Curl_wait_for_resolv(conn, &dns); /* * We cannot use 'hostent' as a struct that Curl_resolv() returns. It * returns a Curl_addrinfo pointer that may not always look the same. */ if(dns) hp=dns->addr; if (hp) { char buf[64]; unsigned short ip[4]; Curl_printable_address(hp, buf, sizeof(buf)); if(4 == sscanf( buf, "%hu.%hu.%hu.%hu", &ip[0], &ip[1], &ip[2], &ip[3])) { /* Set DSTIP */ socksreq[4] = (unsigned char)ip[0]; socksreq[5] = (unsigned char)ip[1]; socksreq[6] = (unsigned char)ip[2]; socksreq[7] = (unsigned char)ip[3]; } else hp = NULL; /* fail! */ Curl_resolv_unlock(data, dns); /* not used anymore from now on */ } if(!hp) { failf(data, "Failed to resolve \"%s\" for SOCKS4 connect.", conn->host.name); return CURLE_COULDNT_RESOLVE_HOST; } } /* * This is currently not supporting "Identification Protocol (RFC1413)". */ socksreq[8] = 0; /* ensure empty userid is NUL-terminated */ if (proxy_name) strlcat((char*)socksreq + 8, proxy_name, sizeof(socksreq) - 8); /* * Make connection */ { ssize_t actualread; ssize_t written; int packetsize = 9 + (int)strlen((char*)socksreq + 8); /* size including NUL */ /* Send request */ code = Curl_write(conn, sock, (char *)socksreq, packetsize, &written); if ((code != CURLE_OK) || (written != packetsize)) { failf(data, "Failed to send SOCKS4 connect request."); return CURLE_COULDNT_CONNECT; } packetsize = 8; /* receive data size */ /* Receive response */ result = blockread_all(conn, sock, (char *)socksreq, packetsize, &actualread, timeout); if ((result != CURLE_OK) || (actualread != packetsize)) { failf(data, "Failed to receive SOCKS4 connect request ack."); return CURLE_COULDNT_CONNECT; } /* * Response format * * +----+----+----+----+----+----+----+----+ * | VN | CD | DSTPORT | DSTIP | * +----+----+----+----+----+----+----+----+ * # of bytes: 1 1 2 4 * * VN is the version of the reply code and should be 0. CD is the result * code with one of the following values: * * 90: request granted * 91: request rejected or failed * 92: request rejected because SOCKS server cannot connect to * identd on the client * 93: request rejected because the client program and identd * report different user-ids */ /* wrong version ? */ if (socksreq[0] != 0) { failf(data, "SOCKS4 reply has wrong version, version should be 4."); return CURLE_COULDNT_CONNECT; } /* Result */ switch(socksreq[1]) { case 90: infof(data, "SOCKS4 request granted.\n"); break; case 91: failf(data, "Can't complete SOCKS4 connection to %d.%d.%d.%d:%d. (%d)" ", request rejected or failed.", (unsigned char)socksreq[4], (unsigned char)socksreq[5], (unsigned char)socksreq[6], (unsigned char)socksreq[7], (unsigned int)ntohs(*(unsigned short*)(&socksreq[8])), socksreq[1]); return CURLE_COULDNT_CONNECT; case 92: failf(data, "Can't complete SOCKS4 connection to %d.%d.%d.%d:%d. (%d)" ", request rejected because SOCKS server cannot connect to " "identd on the client.", (unsigned char)socksreq[4], (unsigned char)socksreq[5], (unsigned char)socksreq[6], (unsigned char)socksreq[7], (unsigned int)ntohs(*(unsigned short*)(&socksreq[8])), socksreq[1]); return CURLE_COULDNT_CONNECT; case 93: failf(data, "Can't complete SOCKS4 connection to %d.%d.%d.%d:%d. (%d)" ", request rejected because the client program and identd " "report different user-ids.", (unsigned char)socksreq[4], (unsigned char)socksreq[5], (unsigned char)socksreq[6], (unsigned char)socksreq[7], (unsigned int)ntohs(*(unsigned short*)(&socksreq[8])), socksreq[1]); return CURLE_COULDNT_CONNECT; default: failf(data, "Can't complete SOCKS4 connection to %d.%d.%d.%d:%d. (%d)" ", Unknown.", (unsigned char)socksreq[4], (unsigned char)socksreq[5], (unsigned char)socksreq[6], (unsigned char)socksreq[7], (unsigned int)ntohs(*(unsigned short*)(&socksreq[8])), socksreq[1]); return CURLE_COULDNT_CONNECT; } } Curl_nonblock(sock, TRUE); return CURLE_OK; /* Proxy was successful! */ }
/* 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; }
/* 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; #ifdef ENABLE_IPV6 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); 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 */ Curl_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) && 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; }
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; }