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; }
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; }
/* * 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 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! */ }
/* * 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! */ }
/* * 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! */ }
static CURLcode bindlocal(struct connectdata *conn, curl_socket_t sockfd) { #ifdef HAVE_INET_NTOA bool bindworked = FALSE; struct SessionHandle *data = conn->data; /************************************************************* * Select device to bind socket to *************************************************************/ if (strlen(data->set.device)<255) { struct Curl_dns_entry *h=NULL; size_t size; 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; } 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) /* we know data->set.device is shorter than the myhost array */ strcpy(myhost, data->set.device); } 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, "We bind local end 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_ourerrno())); */ 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) { if ( h ) { Curl_addrinfo *addr = h->addr; Curl_resolv_unlock(data, h); /* we don't need it anymore after this function has returned */ if( bind(sockfd, addr->ai_addr, (socklen_t)addr->ai_addrlen) >= 0) { /* we succeeded to bind */ #ifdef ENABLE_IPV6 struct sockaddr_in6 add; #else struct sockaddr_in add; #endif bindworked = TRUE; size = sizeof(add); if(getsockname(sockfd, (struct sockaddr *) &add, (socklen_t *)&size)<0) { failf(data, "getsockname() failed"); return CURLE_HTTP_PORT_FAILED; } } if(!bindworked) { data->state.os_errno = Curl_ourerrno(); failf(data, "bind failure: %s", Curl_strerror(conn, data->state.os_errno)); return CURLE_HTTP_PORT_FAILED; } } /* end of if h */ else { failf(data,"could't find my own IP address (%s)", myhost); return CURLE_HTTP_PORT_FAILED; } } /* end of inet_addr */ else { failf(data, "could't find my own IP address (%s)", myhost); return CURLE_HTTP_PORT_FAILED; } return CURLE_OK; } /* end of device selection support */ #else (void)conn; (void)sockfd; #endif /* end of HAVE_INET_NTOA */ return CURLE_HTTP_PORT_FAILED; }
int Curl_resolv_timeout(struct connectdata *conn, const char *hostname, int port, struct Curl_dns_entry **entry, long timeoutms) { #ifdef USE_ALARM_TIMEOUT #ifdef HAVE_SIGACTION struct sigaction keep_sigact; /* store the old struct here */ volatile bool keep_copysig = FALSE; /* wether old sigact has been saved */ struct sigaction sigact; #else #ifdef HAVE_SIGNAL void (*keep_sigact)(int); /* store the old handler here */ #endif /* HAVE_SIGNAL */ #endif /* HAVE_SIGACTION */ volatile long timeout; volatile unsigned int prev_alarm = 0; struct SessionHandle *data = conn->data; #endif /* USE_ALARM_TIMEOUT */ int rc; *entry = NULL; #ifdef USE_ALARM_TIMEOUT if (data->set.no_signal) /* Ignore the timeout when signals are disabled */ timeout = 0; else timeout = timeoutms; if(!timeout) /* USE_ALARM_TIMEOUT defined, but no timeout actually requested */ return Curl_resolv(conn, hostname, port, entry); if(timeout < 1000) /* The alarm() function only provides integer second resolution, so if we want to wait less than one second we must bail out already now. */ return CURLRESOLV_TIMEDOUT; /************************************************************* * Set signal handler to catch SIGALRM * Store the old value to be able to set it back later! *************************************************************/ #ifdef HAVE_SIGACTION sigaction(SIGALRM, NULL, &sigact); keep_sigact = sigact; keep_copysig = TRUE; /* yes, we have a copy */ sigact.sa_handler = alarmfunc; #ifdef SA_RESTART /* HPUX doesn't have SA_RESTART but defaults to that behaviour! */ sigact.sa_flags &= ~SA_RESTART; #endif /* now set the new struct */ sigaction(SIGALRM, &sigact, NULL); #else /* HAVE_SIGACTION */ /* no sigaction(), revert to the much lamer signal() */ #ifdef HAVE_SIGNAL keep_sigact = signal(SIGALRM, alarmfunc); #endif #endif /* HAVE_SIGACTION */ /* alarm() makes a signal get sent when the timeout fires off, and that will abort system calls */ prev_alarm = alarm(curlx_sltoui(timeout/1000L)); /* This allows us to time-out from the name resolver, as the timeout will generate a signal and we will siglongjmp() from that here. This technique has problems (see alarmfunc). This should be the last thing we do before calling Curl_resolv(), as otherwise we'd have to worry about variables that get modified before we invoke Curl_resolv() (and thus use "volatile"). */ if(sigsetjmp(curl_jmpenv, 1)) { /* this is coming from a siglongjmp() after an alarm signal */ failf(data, "name lookup timed out"); rc = CURLRESOLV_ERROR; goto clean_up; } #else #ifndef CURLRES_ASYNCH if(timeoutms) infof(conn->data, "timeout on name lookup is not supported\n"); #else (void)timeoutms; /* timeoutms not used with an async resolver */ #endif #endif /* USE_ALARM_TIMEOUT */ /* Perform the actual name resolution. This might be interrupted by an * alarm if it takes too long. */ rc = Curl_resolv(conn, hostname, port, entry); #ifdef USE_ALARM_TIMEOUT clean_up: if(!prev_alarm) /* deactivate a possibly active alarm before uninstalling the handler */ alarm(0); #ifdef HAVE_SIGACTION if(keep_copysig) { /* we got a struct as it looked before, now put that one back nice and clean */ sigaction(SIGALRM, &keep_sigact, NULL); /* put it back */ } #else #ifdef HAVE_SIGNAL /* restore the previous SIGALRM handler */ signal(SIGALRM, keep_sigact); #endif #endif /* HAVE_SIGACTION */ /* switch back the alarm() to either zero or to what it was before minus the time we spent until now! */ if(prev_alarm) { /* there was an alarm() set before us, now put it back */ unsigned long elapsed_ms = Curl_tvdiff(Curl_tvnow(), conn->created); /* the alarm period is counted in even number of seconds */ unsigned long alarm_set = prev_alarm - elapsed_ms/1000; if(!alarm_set || ((alarm_set >= 0x80000000) && (prev_alarm < 0x80000000)) ) { /* if the alarm time-left reached zero or turned "negative" (counted with unsigned values), we should fire off a SIGALRM here, but we won't, and zero would be to switch it off so we never set it to less than 1! */ alarm(1); rc = CURLRESOLV_TIMEDOUT; failf(data, "Previous alarm fired off!"); } else alarm((unsigned int)alarm_set); } #endif /* USE_ALARM_TIMEOUT */ return rc; }
static CURLcode bindlocal(struct connectdata *conn, curl_socket_t sockfd, int af) { struct SessionHandle *data = conn->data; struct sockaddr_in me; #ifdef ENABLE_IPV6 struct sockaddr_in6 me6; #endif struct sockaddr *sock = NULL; /* bind to this address */ socklen_t socksize = 0; /* size of the data sock points to */ 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; /************************************************************* * Select device to bind socket to *************************************************************/ if(dev && (strlen(dev)<255) ) { char myhost[256] = ""; int rc; bool was_iface = FALSE; if(Curl_if2ip(af, dev, myhost, sizeof(myhost))) { /* * We now have the numerical IP address 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; } } if(!was_iface) { /* * 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; 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); } } if(!*myhost || !h) { /* need to fix this h=Curl_gethost(data, getmyhost(*myhost,sizeof(myhost)), hostent_buf, sizeof(hostent_buf)); */ failf(data, "Couldn't bind to '%s'", dev); if(h) Curl_resolv_unlock(data, h); return CURLE_INTERFACE_FAILED; } infof(data, "Bind local address to %s\n", myhost); sock = h->addr->ai_addr; socksize = h->addr->ai_addrlen; #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, dev, 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 if(port) { /* if a local port number is requested but no local IP, extract the address from the socket */ if(af == AF_INET) { memset(&me, 0, sizeof(me)); me.sin_family = AF_INET; me.sin_addr.s_addr = INADDR_ANY; sock = (struct sockaddr *)&me; socksize = sizeof(me); } #ifdef ENABLE_IPV6 else { /* AF_INET6 */ memset(&me6, 0, sizeof(me6)); me6.sin6_family = AF_INET6; /* in6addr_any isn't always available and since me6 has just been cleared, it's not strictly necessary to use it here */ /*me6.sin6_addr = in6addr_any;*/ sock = (struct sockaddr *)&me6; socksize = sizeof(me6); } #endif } 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) me.sin_port = htons(port); #ifdef ENABLE_IPV6 else me6.sin6_port = htons(port); #endif if( bind(sockfd, sock, socksize) >= 0) { /* we succeeded to bind */ struct Curl_sockaddr_storage add; 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)); if(h) Curl_resolv_unlock(data, h); return CURLE_INTERFACE_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); conn->bits.bound = TRUE; if(h) Curl_resolv_unlock(data, h); 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 = error = SOCKERRNO; failf(data, "bind failed with errno %d: %s", error, Curl_strerror(conn, error)); if(h) Curl_resolv_unlock(data, h); return CURLE_INTERFACE_FAILED; }
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 */ bool is_interface = FALSE; bool is_host = FALSE; static const char *if_prefix = "if!"; static const char *host_prefix = "host!"; /************************************************************* * 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) ) { if(strncmp(if_prefix, dev, strlen(if_prefix)) == 0) { dev += strlen(if_prefix); is_interface = TRUE; } else if(strncmp(host_prefix, dev, strlen(host_prefix)) == 0) { dev += strlen(host_prefix); is_host = TRUE; } /* interface */ if(!is_host) { return CURLE_UNSUPPORTED_PROTOCOL; } if(!is_interface) { /* * 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 = conn->ip_version; int rc; if(af == AF_INET) conn->ip_version = CURL_IPRESOLVE_V4; #ifdef ENABLE_IPV6 else if(af == AF_INET6) conn->ip_version = CURL_IPRESOLVE_V6; #endif rc = Curl_resolv(conn, dev, 0, &h); if(rc == CURLRESOLV_PENDING) (void)Curl_resolver_wait_resolv(conn, &h); conn->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) { #ifdef HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID char *scope_ptr = strchr(myhost, '%'); if(scope_ptr) *(scope_ptr++) = 0; #endif if(Curl_inet_pton(AF_INET6, myhost, &si6->sin6_addr) > 0) { si6->sin6_family = AF_INET6; si6->sin6_port = htons(port); #ifdef HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID if(scope_ptr) /* The "myhost" string either comes from Curl_if2ip or from Curl_printable_address. The latter returns only numeric scope IDs and the former returns none at all. So the scope ID, if present, is known to be numeric */ si6->sin6_scope_id = atoi(scope_ptr); #endif } 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; }
static CURLcode bindlocal(struct connectdata *conn, curl_socket_t sockfd) { #ifdef HAVE_INET_NTOA bool bindworked = FALSE; struct SessionHandle *data = conn->data; /************************************************************* * Select device to bind socket to *************************************************************/ if (strlen(data->set.device)<255) { struct Curl_dns_entry *h=NULL; size_t size; 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 == 1) (void)Curl_wait_for_resolv(conn, &h); if(h) was_iface = TRUE; } 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 == 1) (void)Curl_wait_for_resolv(conn, &h); if(h) /* we know data->set.device is shorter than the myhost array */ strcpy(myhost, data->set.device); } 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, "We bind local end 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, strerror(errno)); */ infof(data, "SO_BINDTODEVICE %s failed\n", data->set.device); /* This is typiclally "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) { if ( h ) { Curl_addrinfo *addr = h->addr; Curl_resolv_unlock(data, h); /* we don't need it anymore after this function has returned */ #ifdef ENABLE_IPV6 if( bind(sockfd, addr->ai_addr, addr->ai_addrlen) >= 0) { /* we succeeded to bind */ struct sockaddr_in6 add; bindworked = TRUE; size = sizeof(add); if(getsockname(sockfd, (struct sockaddr *) &add, (socklen_t *)&size)<0) { failf(data, "getsockname() failed"); return CURLE_HTTP_PORT_FAILED; } } #else { struct sockaddr_in sa; memset((char *)&sa, 0, sizeof(sa)); memcpy((char *)&sa.sin_addr, addr->h_addr, addr->h_length); sa.sin_family = AF_INET; sa.sin_addr.s_addr = in; sa.sin_port = 0; /* get any port */ if( bind(sockfd, (struct sockaddr *)&sa, sizeof(sa)) >= 0) { /* we succeeded to bind */ struct sockaddr_in add; bindworked = TRUE; size = sizeof(add); if(getsockname(sockfd, (struct sockaddr *) &add, (socklen_t *)&size)<0) { failf(data, "getsockname() failed"); return CURLE_HTTP_PORT_FAILED; } } } #endif if(!bindworked) { switch(errno) { case EBADF: failf(data, "Invalid descriptor: %d", errno); break; case EINVAL: failf(data, "Invalid request: %d", errno); break; case EACCES: failf(data, "Address is protected, user not superuser: %d", errno); break; case ENOTSOCK: failf(data, "Argument is a descriptor for a file, not a socket: %d", errno); break; case EFAULT: failf(data, "Inaccessable memory error: %d", errno); break; case ENAMETOOLONG: failf(data, "Address too long: %d", errno); break; case ENOMEM: failf(data, "Insufficient kernel memory was available: %d", errno); break; default: failf(data, "errno %d", errno); break; } /* end of switch(errno) */ return CURLE_HTTP_PORT_FAILED; } /* end of else */ } /* end of if h */ else { failf(data,"could't find my own IP address (%s)", myhost); return CURLE_HTTP_PORT_FAILED; } } /* end of inet_addr */ else { failf(data, "could't find my own IP address (%s)", myhost); return CURLE_HTTP_PORT_FAILED; } return CURLE_OK; } /* end of device selection support */ #endif /* end of HAVE_INET_NTOA */ return CURLE_HTTP_PORT_FAILED; }
static CURLcode bindlocal(struct connectdata *conn, int sockfd) { #if !defined(WIN32)||defined(__CYGWIN32__) /* We don't generally like checking for OS-versions, we should make this HAVE_XXXX based, although at the moment I don't have a decent test for this! */ #ifdef HAVE_INET_NTOA struct SessionHandle *data = conn->data; /************************************************************* * Select device to bind socket to *************************************************************/ if (strlen(data->set.device)<255) { struct sockaddr_in sa; struct Curl_dns_entry *h=NULL; size_t size; char myhost[256] = ""; in_addr_t in; /* 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 */ h = Curl_resolv(data, myhost, 0); } else { if(strlen(data->set.device)>1) { /* * This was not an interface, resolve the name as a host name * or IP number */ h = Curl_resolv(data, data->set.device, 0); if(h) { /* we know data->set.device is shorter than the myhost array */ strcpy(myhost, data->set.device); } } } if(! *myhost) { /* need to fix this h=Curl_gethost(data, getmyhost(*myhost,sizeof(myhost)), hostent_buf, sizeof(hostent_buf)); */ return CURLE_HTTP_PORT_FAILED; } infof(data, "We bind local end to %s\n", myhost); in=inet_addr(myhost); if (CURL_INADDR_NONE != in) { if ( h ) { Curl_addrinfo *addr = h->addr; Curl_resolv_unlock(data, h); /* we don't need it anymore after this function has returned */ #ifdef ENABLE_IPV6 (void)sa; /* prevent compiler warning */ if( bind(sockfd, addr->ai_addr, addr->ai_addrlen) >= 0) { /* we succeeded to bind */ struct sockaddr_in6 add; size = sizeof(add); if(getsockname(sockfd, (struct sockaddr *) &add, (socklen_t *)&size)<0) { failf(data, "getsockname() failed"); return CURLE_HTTP_PORT_FAILED; } } #else memset((char *)&sa, 0, sizeof(sa)); memcpy((char *)&sa.sin_addr, addr->h_addr, addr->h_length); sa.sin_family = AF_INET; sa.sin_addr.s_addr = in; sa.sin_port = 0; /* get any port */ if( bind(sockfd, (struct sockaddr *)&sa, sizeof(sa)) >= 0) { /* we succeeded to bind */ struct sockaddr_in add; size = sizeof(add); if(getsockname(sockfd, (struct sockaddr *) &add, (socklen_t *)&size)<0) { failf(data, "getsockname() failed"); return CURLE_HTTP_PORT_FAILED; } } #endif else { switch(errno) { case EBADF: failf(data, "Invalid descriptor: %d", errno); break; case EINVAL: failf(data, "Invalid request: %d", errno); break; case EACCES: failf(data, "Address is protected, user not superuser: %d", errno); break; case ENOTSOCK: failf(data, "Argument is a descriptor for a file, not a socket: %d", errno); break; case EFAULT: failf(data, "Inaccessable memory error: %d", errno); break; case ENAMETOOLONG: failf(data, "Address too long: %d", errno); break; case ENOMEM: failf(data, "Insufficient kernel memory was available: %d", errno); break; default: failf(data, "errno %d", errno); break; } /* end of switch(errno) */ return CURLE_HTTP_PORT_FAILED; } /* end of else */ } /* end of if h */ else { failf(data,"could't find my own IP address (%s)", myhost); return CURLE_HTTP_PORT_FAILED; } } /* end of inet_addr */ else { failf(data, "could't find my own IP address (%s)", myhost); return CURLE_HTTP_PORT_FAILED; } return CURLE_OK; } /* end of device selection support */ #endif /* end of HAVE_INET_NTOA */ #endif /* end of not WIN32 */ return CURLE_HTTP_PORT_FAILED; }
static CURLcode bindlocal(struct connectdata *conn, curl_socket_t sockfd, int af, unsigned int scope) { struct Curl_easy *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; /************************************************************* * 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) ) { char myhost[256] = ""; int done = 0; /* -1 for error, 1 for address found */ bool is_interface = FALSE; bool is_host = FALSE; static const char *if_prefix = "if!"; static const char *host_prefix = "host!"; if(strncmp(if_prefix, dev, strlen(if_prefix)) == 0) { dev += strlen(if_prefix); is_interface = TRUE; } else if(strncmp(host_prefix, dev, strlen(host_prefix)) == 0) { dev += strlen(host_prefix); is_host = TRUE; } /* interface */ if(!is_host) { #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. * * interface might be a VRF, eg: vrf-blue, which means it cannot be * converted to an IP address and would fail Curl_if2ip. Simply try * to use it straight away. */ if(setsockopt(sockfd, SOL_SOCKET, SO_BINDTODEVICE, dev, (curl_socklen_t)strlen(dev) + 1) == 0) { /* This is typically "errno 1, error: Operation not permitted" if * you're not running as root or another suitable privileged * user. * If it succeeds it means the parameter was a valid interface and * not an IP address. Return immediately. */ return CURLE_OK; } #endif switch(Curl_if2ip(af, scope, conn->scope_id, dev, myhost, sizeof(myhost))) { case IF2IP_NOT_FOUND: if(is_interface) { /* Do not fall back to treating it as a host name */ failf(data, "Couldn't bind to interface '%s'", dev); return CURLE_INTERFACE_FAILED; } break; case IF2IP_AF_NOT_SUPPORTED: /* Signal the caller to try another address family if available */ return CURLE_UNSUPPORTED_PROTOCOL; case IF2IP_FOUND: is_interface = TRUE; /* * 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; break; } } if(!is_interface) { /* * 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 = conn->ip_version; int rc; if(af == AF_INET) conn->ip_version = CURL_IPRESOLVE_V4; #ifdef ENABLE_IPV6 else if(af == AF_INET6) conn->ip_version = CURL_IPRESOLVE_V6; #endif rc = Curl_resolv(conn, dev, 0, &h); if(rc == CURLRESOLV_PENDING) (void)Curl_resolver_wait_resolv(conn, &h); conn->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) { #ifdef HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID char *scope_ptr = strchr(myhost, '%'); if(scope_ptr) *(scope_ptr++) = 0; #endif if(Curl_inet_pton(AF_INET6, myhost, &si6->sin6_addr) > 0) { si6->sin6_family = AF_INET6; si6->sin6_port = htons(port); #ifdef HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID if(scope_ptr) /* The "myhost" string either comes from Curl_if2ip or from Curl_printable_address. The latter returns only numeric scope IDs and the former returns none at all. So the scope ID, if present, is known to be numeric */ si6->sin6_scope_id = atoi(scope_ptr); #endif } 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) { /* errorbuf is set false so failf will overwrite any message already in the error buffer, so the user receives this error message instead of a generic resolve error. */ data->state.errorbuf = FALSE; 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; }