bool Curl_if_is_interface_name(const char *interf) { /* This is here just to support the old interfaces */ char buf[256]; return (Curl_if2ip(AF_INET, 0 /* unused */, 0, interf, buf, sizeof(buf)) == IF2IP_NOT_FOUND) ? FALSE : TRUE; }
bool Curl_if_is_interface_name(const char *interf) { /* This is here just to support the old interfaces */ char buf[256]; char *ip = Curl_if2ip(AF_INET, interf, buf, sizeof(buf)); return (ip != NULL) ? TRUE : FALSE; }
static CURLcode bindlocal(struct connectdata *conn, curl_socket_t sockfd) { struct SessionHandle *data = conn->data; struct sockaddr_in me; struct sockaddr *sock = NULL; /* bind to this address */ socklen_t socksize; /* size of the data sock points to */ unsigned short port = data->set.localport; /* use this port number, 0 for "random" */ /* how many port numbers to try to bind to, increasing one at a time */ int portnum = data->set.localportrange; /************************************************************* * Select device to bind socket to *************************************************************/ if (data->set.device && (strlen(data->set.device)<255) ) { struct Curl_dns_entry *h=NULL; char myhost[256] = ""; in_addr_t in; int rc; bool was_iface = FALSE; /* First check if the given name is an IP address */ in=inet_addr(data->set.device); if((in == CURL_INADDR_NONE) && Curl_if2ip(data->set.device, myhost, sizeof(myhost))) { /* * We now have the numerical IPv4-style x.y.z.w in the 'myhost' buffer */ rc = Curl_resolv(conn, myhost, 0, &h); if(rc == CURLRESOLV_PENDING) (void)Curl_wait_for_resolv(conn, &h); if(h) { was_iface = TRUE; Curl_resolv_unlock(data, h); } } if(!was_iface) { /* * This was not an interface, resolve the name as a host name * or IP number */ rc = Curl_resolv(conn, data->set.device, 0, &h); if(rc == CURLRESOLV_PENDING) (void)Curl_wait_for_resolv(conn, &h); if(h) { if(in == CURL_INADDR_NONE) /* convert the resolved address, sizeof myhost >= INET_ADDRSTRLEN */ Curl_inet_ntop(h->addr->ai_addr->sa_family, &((struct sockaddr_in*)h->addr->ai_addr)->sin_addr, myhost, sizeof myhost); else /* we know data->set.device is shorter than the myhost array */ strcpy(myhost, data->set.device); Curl_resolv_unlock(data, h); } } if(! *myhost) { /* need to fix this h=Curl_gethost(data, getmyhost(*myhost,sizeof(myhost)), hostent_buf, sizeof(hostent_buf)); */ failf(data, "Couldn't bind to '%s'", data->set.device); return CURLE_HTTP_PORT_FAILED; } infof(data, "Bind local address to %s\n", myhost); #ifdef SO_BINDTODEVICE /* I am not sure any other OSs than Linux that provide this feature, and * at the least I cannot test. --Ben * * This feature allows one to tightly bind the local socket to a * particular interface. This will force even requests to other local * interfaces to go out the external interface. * */ if (was_iface) { /* Only bind to the interface when specified as interface, not just as a * hostname or ip address. */ if (setsockopt(sockfd, SOL_SOCKET, SO_BINDTODEVICE, data->set.device, strlen(data->set.device)+1) != 0) { /* printf("Failed to BINDTODEVICE, socket: %d device: %s error: %s\n", sockfd, data->set.device, Curl_strerror(Curl_sockerrno())); */ infof(data, "SO_BINDTODEVICE %s failed\n", data->set.device); /* This is typically "errno 1, error: Operation not permitted" if you're not running as root or another suitable privileged user */ } } #endif in=inet_addr(myhost); if (CURL_INADDR_NONE == in) { failf(data,"couldn't find my own IP address (%s)", myhost); return CURLE_HTTP_PORT_FAILED; } /* end of inet_addr */ if ( h ) { Curl_addrinfo *addr = h->addr; sock = addr->ai_addr; socksize = addr->ai_addrlen; } else return CURLE_HTTP_PORT_FAILED; } else if(port) { /* if a local port number is requested but no local IP, extract the address from the socket */ memset(&me, 0, sizeof(struct sockaddr)); me.sin_family = AF_INET; me.sin_addr.s_addr = INADDR_ANY; sock = (struct sockaddr *)&me; socksize = sizeof(struct sockaddr); } else /* no local kind of binding was requested */ return CURLE_OK; do { /* Set port number to bind to, 0 makes the system pick one */ if(sock->sa_family == AF_INET) ((struct sockaddr_in *)sock)->sin_port = htons(port); #ifdef ENABLE_IPV6 else ((struct sockaddr_in6 *)sock)->sin6_port = htons(port); #endif if( bind(sockfd, sock, socksize) >= 0) { /* we succeeded to bind */ struct Curl_sockaddr_storage add; size_t size; size = sizeof(add); if(getsockname(sockfd, (struct sockaddr *) &add, (socklen_t *)&size)<0) { failf(data, "getsockname() failed"); return CURLE_HTTP_PORT_FAILED; } /* We re-use/clobber the port variable here below */ if(((struct sockaddr *)&add)->sa_family == AF_INET) port = ntohs(((struct sockaddr_in *)&add)->sin_port); #ifdef ENABLE_IPV6 else port = ntohs(((struct sockaddr_in6 *)&add)->sin6_port); #endif infof(data, "Local port: %d\n", port); return CURLE_OK; } if(--portnum > 0) { infof(data, "Bind to local port %d failed, trying next\n", port); port++; /* try next port */ } else break; } while(1); data->state.os_errno = Curl_sockerrno(); failf(data, "bind failure: %s", Curl_strerror(conn, data->state.os_errno)); return CURLE_HTTP_PORT_FAILED; }
static CURLcode 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) { #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; }
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) { #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; }