283
277
static CURLcode bindlocal(struct connectdata *conn,
284
curl_socket_t sockfd)
278
curl_socket_t sockfd, int af)
280
struct SessionHandle *data = conn->data;
282
struct Curl_sockaddr_storage sa;
283
struct sockaddr *sock = (struct sockaddr *)&sa; /* bind to this address */
284
socklen_t sizeof_sa = 0; /* size of the data sock points to */
285
struct sockaddr_in *si4 = (struct sockaddr_in *)&sa;
286
286
#ifdef ENABLE_IPV6
287
struct sockaddr_in6 *si6 = (struct sockaddr_in6 *)&sa;
289
struct SessionHandle *data = conn->data;
290
struct sockaddr_in me;
291
struct sockaddr *sock = NULL; /* bind to this address */
292
socklen_t socksize; /* size of the data sock points to */
290
struct Curl_dns_entry *h=NULL;
293
291
unsigned short port = data->set.localport; /* use this port number, 0 for
295
293
/* how many port numbers to try to bind to, increasing one at a time */
296
294
int portnum = data->set.localportrange;
297
295
const char *dev = data->set.str[STRING_DEVICE];
297
char myhost[256] = "";
298
int done = 0; /* -1 for error, 1 for address found */
299
300
/*************************************************************
300
301
* Select device to bind socket to
301
302
*************************************************************/
304
/* no local kind of binding was requested */
307
memset(&sa, 0, sizeof(struct Curl_sockaddr_storage));
302
309
if(dev && (strlen(dev)<255) ) {
303
struct Curl_dns_entry *h=NULL;
304
char myhost[256] = "";
307
bool was_iface = FALSE;
310
/* First check if the given name is an IP address */
311
in=inet_addr((char *) dev);
313
if((in == CURL_INADDR_NONE) &&
314
Curl_if2ip(dev, myhost, sizeof(myhost))) {
316
* We now have the numerical IPv4-style x.y.z.w in the 'myhost' buffer
318
rc = Curl_resolv(conn, myhost, 0, &h);
319
if(rc == CURLRESOLV_PENDING)
320
(void)Curl_wait_for_resolv(conn, &h);
324
Curl_resolv_unlock(data, h);
330
* This was not an interface, resolve the name as a host name
333
rc = Curl_resolv(conn, dev, 0, &h);
334
if(rc == CURLRESOLV_PENDING)
335
(void)Curl_wait_for_resolv(conn, &h);
338
if(in == CURL_INADDR_NONE)
339
/* convert the resolved address, sizeof myhost >= INET_ADDRSTRLEN */
340
Curl_inet_ntop(h->addr->ai_addr->sa_family,
341
&((struct sockaddr_in*)h->addr->ai_addr)->sin_addr,
342
myhost, sizeof myhost);
344
/* we know data->set.device is shorter than the myhost array */
346
Curl_resolv_unlock(data, h);
353
getmyhost(*myhost,sizeof(myhost)),
355
sizeof(hostent_buf));
357
failf(data, "Couldn't bind to '%s'", dev);
358
return CURLE_INTERFACE_FAILED;
361
infof(data, "Bind local address to %s\n", myhost);
312
if(Curl_if2ip(af, dev, myhost, sizeof(myhost))) {
314
* We now have the numerical IP address in the 'myhost' buffer
316
infof(data, "Local Interface %s is ip %s using address family %i\n",
363
320
#ifdef SO_BINDTODEVICE
364
/* I am not sure any other OSs than Linux that provide this feature, and
365
* at the least I cannot test. --Ben
367
* This feature allows one to tightly bind the local socket to a
368
* particular interface. This will force even requests to other local
369
* interfaces to go out the external interface.
373
/* Only bind to the interface when specified as interface, not just as a
321
/* I am not sure any other OSs than Linux that provide this feature, and
322
* at the least I cannot test. --Ben
324
* This feature allows one to tightly bind the local socket to a
325
* particular interface. This will force even requests to other local
326
* interfaces to go out the external interface.
329
* Only bind to the interface when specified as interface, not just as a
374
330
* hostname or ip address.
376
332
if(setsockopt(sockfd, SOL_SOCKET, SO_BINDTODEVICE,
377
dev, strlen(dev)+1) != 0) {
378
/* printf("Failed to BINDTODEVICE, socket: %d device: %s error: %s\n",
379
sockfd, dev, Curl_strerror(SOCKERRNO)); */
380
infof(data, "SO_BINDTODEVICE %s failed\n", dev);
333
dev, strlen(dev)+1) != 0) {
335
infof(data, "SO_BINDTODEVICE %s failed with errno %d: %s;"
336
" will do regular bind\n",
337
dev, error, Curl_strerror(conn, error));
381
338
/* This is typically "errno 1, error: Operation not permitted" if
382
339
you're not running as root or another suitable privileged user */
387
in=inet_addr(myhost);
390
in6 = Curl_inet_pton (AF_INET6, myhost, (void *)&ipv6_addr);
392
if(CURL_INADDR_NONE == in && -1 == in6) {
393
failf(data,"couldn't find my own IP address (%s)", myhost);
345
* This was not an interface, resolve the name as a host name
348
* Temporarily force name resolution to use only the address type
349
* of the connection. The resolve functions should really be changed
350
* to take a type parameter instead.
352
long ipver = data->set.ip_version;
356
data->set.ip_version = CURL_IPRESOLVE_V4;
358
else if (af == AF_INET6)
359
data->set.ip_version = CURL_IPRESOLVE_V6;
362
rc = Curl_resolv(conn, dev, 0, &h);
363
if(rc == CURLRESOLV_PENDING)
364
(void)Curl_wait_for_resolv(conn, &h);
365
data->set.ip_version = ipver;
368
/* convert the resolved address, sizeof myhost >= INET_ADDRSTRLEN */
369
Curl_printable_address(h->addr, myhost, sizeof(myhost));
370
infof(data, "Name '%s' family %i resolved to '%s' family %i\n",
371
dev, af, myhost, h->addr->ai_family);
372
Curl_resolv_unlock(data, h);
377
* provided dev was no interface (or interfaces are not supported
378
* e.g. solaris) no ip address and no domain we fail here
387
if((af == AF_INET6) &&
388
(Curl_inet_pton(AF_INET6, myhost, &si6->sin6_addr) > 0)) {
389
si6->sin6_family = AF_INET6;
390
si6->sin6_port = htons(port);
391
sizeof_sa = sizeof(struct sockaddr_in6);
396
if((af == AF_INET) &&
397
(Curl_inet_pton(AF_INET, myhost, &si4->sin_addr) > 0)) {
398
si4->sin_family = AF_INET;
399
si4->sin_port = htons(port);
400
sizeof_sa = sizeof(struct sockaddr_in);
405
failf(data, "Couldn't bind to '%s'", dev);
394
406
return CURLE_INTERFACE_FAILED;
395
} /* end of inet_addr */
398
Curl_addrinfo *addr = h->addr;
399
sock = addr->ai_addr;
400
socksize = addr->ai_addrlen;
410
/* no device was given, prepare sa to match af's needs */
412
if ( af == AF_INET6 ) {
413
si6->sin6_family = AF_INET6;
414
si6->sin6_port = htons(port);
415
sizeof_sa = sizeof(struct sockaddr_in6);
403
return CURLE_INTERFACE_FAILED;
407
/* if a local port number is requested but no local IP, extract the
408
address from the socket */
409
memset(&me, 0, sizeof(struct sockaddr));
410
me.sin_family = AF_INET;
411
me.sin_addr.s_addr = INADDR_ANY;
413
sock = (struct sockaddr *)&me;
414
socksize = sizeof(struct sockaddr);
418
/* no local kind of binding was requested */
419
if ( af == AF_INET ) {
420
si4->sin_family = AF_INET;
421
si4->sin_port = htons(port);
422
sizeof_sa = sizeof(struct sockaddr_in);
423
/* Set port number to bind to, 0 makes the system pick one */
424
if(sock->sa_family == AF_INET)
425
((struct sockaddr_in *)sock)->sin_port = htons(port);
428
((struct sockaddr_in6 *)sock)->sin6_port = htons(port);
431
if( bind(sockfd, sock, socksize) >= 0) {
432
/* we succeeded to bind */
427
if( bind(sockfd, sock, sizeof_sa) >= 0) {
428
/* we succeeded to bind */
433
429
struct Curl_sockaddr_storage add;
430
socklen_t size = sizeof(add);
431
memset(&add, 0, sizeof(struct Curl_sockaddr_storage));
437
432
if(getsockname(sockfd, (struct sockaddr *) &add, &size) < 0) {
438
failf(data, "getsockname() failed");
433
data->state.os_errno = error = SOCKERRNO;
434
failf(data, "getsockname() failed with errno %d: %s",
435
error, Curl_strerror(conn, error));
439
436
return CURLE_INTERFACE_FAILED;
441
/* We re-use/clobber the port variable here below */
442
if(((struct sockaddr *)&add)->sa_family == AF_INET)
443
port = ntohs(((struct sockaddr_in *)&add)->sin_port);
446
port = ntohs(((struct sockaddr_in6 *)&add)->sin6_port);
448
438
infof(data, "Local port: %d\n", port);
449
439
conn->bits.bound = TRUE;
452
443
if(--portnum > 0) {
453
444
infof(data, "Bind to local port %d failed, trying next\n", port);
454
445
port++; /* try next port */
446
/* We re-use/clobber the port variable here below */
447
if(sock->sa_family == AF_INET)
448
si4->sin_port = ntohs(port);
451
si6->sin6_port = ntohs(port);
460
data->state.os_errno = SOCKERRNO;
461
failf(data, "bind failure: %s",
462
Curl_strerror(conn, data->state.os_errno));
458
data->state.os_errno = error = SOCKERRNO;
459
failf(data, "bind failed with errno %d: %s",
460
error, Curl_strerror(conn, error));
463
462
return CURLE_INTERFACE_FAILED;
742
720
struct SessionHandle *data = conn->data;
743
721
curl_socket_t sockfd;
746
* Curl_sockaddr_storage, which is basically sockaddr_storage has a space
747
* for a largest possible struct sockaddr only. We should add some space for
748
* the other fields we are using. Hence the addr_storage size math.
750
char addr_storage[sizeof(struct curl_sockaddr)-
751
sizeof(struct sockaddr)+
752
sizeof(struct Curl_sockaddr_storage)];
753
struct curl_sockaddr *addr=(struct curl_sockaddr*)&addr_storage;
754
723
const void *iptoprint;
756
addr->family=ai->ai_family;
757
addr->socktype=conn->socktype;
758
addr->protocol=ai->ai_protocol;
760
(ai->ai_addrlen < (socklen_t)sizeof(struct Curl_sockaddr_storage)) ?
761
(unsigned int)ai->ai_addrlen : sizeof(struct Curl_sockaddr_storage);
762
memcpy(&addr->addr, ai->ai_addr, addr->addrlen);
764
/* If set, use opensocket callback to get the socket */
724
struct sockaddr_in * const sa4 = (void *)&addr.sa_addr;
726
struct sockaddr_in6 * const sa6 = (void *)&addr.sa_addr;
730
* The Curl_sockaddr_ex structure is basically libcurl's external API
731
* curl_sockaddr structure with enough space available to directly hold
732
* any protocol-specific address structures. The variable declared here
733
* will be used to pass / receive data to/from the fopensocket callback
734
* if this has been set, before that, it is initialized from parameters.
737
addr.family = ai->ai_family;
738
addr.socktype = conn->socktype;
739
addr.protocol = ai->ai_protocol;
740
addr.addrlen = ai->ai_addrlen;
742
if(addr.addrlen > sizeof(struct Curl_sockaddr_storage))
743
addr.addrlen = sizeof(struct Curl_sockaddr_storage);
744
memcpy(&addr.sa_addr, ai->ai_addr, addr.addrlen);
746
*connected = FALSE; /* default is not connected */
765
748
if(data->set.fopensocket)
750
* If the opensocket callback is set, all the destination address information
751
* is passed to the callback. Depending on this information the callback may
752
* opt to abort the connection, this is indicated returning CURL_SOCKET_BAD;
753
* otherwise it will return a not-connected socket. When the callback returns
754
* a valid socket the destination address information might have been changed
755
* and this 'new' address will actually be used here to connect.
766
757
sockfd = data->set.fopensocket(data->set.opensocket_client,
767
CURLSOCKTYPE_IPCXN, addr);
759
(struct curl_sockaddr *)&addr);
769
sockfd = socket(addr->family, addr->socktype, addr->protocol);
761
/* opensocket callback not set, so simply create the socket now */
762
sockfd = socket(addr.family, addr.socktype, addr.protocol);
770
764
if(sockfd == CURL_SOCKET_BAD)
765
/* no socket, no connection */
771
766
return CURL_SOCKET_BAD;
773
*connected = FALSE; /* default is not connected */
768
#if defined(ENABLE_IPV6) && defined(HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID)
769
if (conn->scope && (addr.family == AF_INET6))
770
sa6->sin6_scope_id = conn->scope;
775
773
/* FIXME: do we have Curl_printable_address-like with struct sockaddr* as
777
775
#if defined(HAVE_SYS_UN_H) && defined(AF_UNIX)
778
if(addr->family==AF_UNIX)
776
if(addr.family == AF_UNIX) {
779
777
infof(data, " Trying %s... ",
780
((const struct sockaddr_un*)(&addr->addr))->sun_path);
778
((const struct sockaddr_un*)(&addr.sa_addr))->sun_path);
779
snprintf(data->info.ip, MAX_IPADR_LEN, "%s",
780
((const struct sockaddr_un*)(&addr.sa_addr))->sun_path);
781
strcpy(conn->ip_addr_str, data->info.ip);
784
786
#ifdef ENABLE_IPV6
785
if(addr->family==AF_INET6)
786
iptoprint= &((const struct sockaddr_in6*)(&addr->addr))->sin6_addr;
787
if(addr.family == AF_INET6) {
788
iptoprint = &sa6->sin6_addr;
789
conn->bits.ipv6 = TRUE;
789
iptoprint = &((const struct sockaddr_in*)(&addr->addr))->sin_addr;
794
iptoprint = &sa4->sin_addr;
791
if(Curl_inet_ntop(addr->family, iptoprint, addr_buf,
792
sizeof(addr_buf)) != NULL)
797
if(Curl_inet_ntop(addr.family, iptoprint, addr_buf,
798
sizeof(addr_buf)) != NULL) {
793
799
infof(data, " Trying %s... ", addr_buf);
800
snprintf(data->info.ip, MAX_IPADR_LEN, "%s", addr_buf);
801
strcpy(conn->ip_addr_str, data->info.ip);
796
805
if(data->set.tcp_nodelay)