diff options
-rw-r--r-- | ares.h | 19 | ||||
-rw-r--r-- | ares_init.c | 30 | ||||
-rw-r--r-- | ares_options.c | 125 | ||||
-rw-r--r-- | ares_private.h | 7 | ||||
-rw-r--r-- | ares_process.c | 40 | ||||
-rw-r--r-- | ares_set_local_dev.3 | 39 | ||||
-rw-r--r-- | ares_set_local_ip4.3 | 34 | ||||
-rw-r--r-- | ares_set_local_ip6.3 | 34 | ||||
-rw-r--r-- | ares_set_servers.3 | 4 | ||||
-rw-r--r-- | ares_set_servers_csv.3 | 43 |
10 files changed, 368 insertions, 7 deletions
@@ -313,6 +313,20 @@ CARES_EXTERN void ares_destroy(ares_channel channel); CARES_EXTERN void ares_cancel(ares_channel channel); +/* These next 3 configure local binding for the out-going socket + * connection. Use these to specify source IP and/or network device + * on multi-homed systems. + */ +CARES_EXTERN void ares_set_local_ip4(ares_channel channel, uint32_t local_ip); + +/* local_ip6 should be 16 bytes in length */ +CARES_EXTERN void ares_set_local_ip6(ares_channel channel, + const unsigned char* local_ip6); + +/* local_dev_name should be null terminated. */ +CARES_EXTERN void ares_set_local_dev(ares_channel channel, + const char* local_dev_name); + CARES_EXTERN void ares_set_socket_callback(ares_channel channel, ares_sock_create_callback callback, void *user_data); @@ -496,6 +510,7 @@ CARES_EXTERN void ares_free_data(void *dataptr); CARES_EXTERN const char *ares_strerror(int code); +/* TODO: Hold port here as well. */ struct ares_addr_node { struct ares_addr_node *next; int family; @@ -508,6 +523,10 @@ struct ares_addr_node { CARES_EXTERN int ares_set_servers(ares_channel channel, struct ares_addr_node *servers); +/* Incomming string format: host[:port][,host[:port]]... */ +CARES_EXTERN int ares_set_servers_csv(ares_channel channel, + const char* servers); + CARES_EXTERN int ares_get_servers(ares_channel channel, struct ares_addr_node **servers); diff --git a/ares_init.c b/ares_init.c index a812b2d..718d552 100644 --- a/ares_init.c +++ b/ares_init.c @@ -172,6 +172,10 @@ int ares_init_options(ares_channel *channelptr, struct ares_options *options, channel->last_server = 0; channel->last_timeout_processed = (time_t)now.tv_sec; + memset(&channel->local_dev_name, 0, sizeof(channel->local_dev_name)); + channel->local_ip4 = 0; + memset(&channel->local_ip6, 0, sizeof(channel->local_ip6)); + /* Initialize our lists of queries */ ares__init_list_head(&(channel->all_queries)); for (i = 0; i < ARES_QID_TABLE_SIZE; i++) @@ -286,6 +290,10 @@ int ares_dup(ares_channel *dest, ares_channel src) (*dest)->sock_create_cb = src->sock_create_cb; (*dest)->sock_create_cb_data = src->sock_create_cb_data; + strncpy((*dest)->local_dev_name, src->local_dev_name, sizeof(src->local_dev_name)); + (*dest)->local_ip4 = src->local_ip4; + memcpy((*dest)->local_ip6, src->local_ip6, sizeof(src->local_ip6)); + /* Full name server cloning required when not all are IPv4 */ for (i = 0; i < src->nservers; i++) { @@ -1604,6 +1612,28 @@ unsigned short ares__generate_new_id(rc4_key* key) return r; } +void ares_set_local_ip4(ares_channel channel, uint32_t local_ip) +{ + channel->local_ip4 = local_ip; +} + +/* local_ip6 should be 16 bytes in length */ +void ares_set_local_ip6(ares_channel channel, + const unsigned char* local_ip6) +{ + memcpy(&channel->local_ip6, local_ip6, sizeof(channel->local_ip6)); +} + +/* local_dev_name should be null terminated. */ +void ares_set_local_dev(ares_channel channel, + const char* local_dev_name) +{ + strncpy(channel->local_dev_name, local_dev_name, + sizeof(channel->local_dev_name)); + channel->local_dev_name[sizeof(channel->local_dev_name) - 1] = 0; +} + + void ares_set_socket_callback(ares_channel channel, ares_sock_create_callback cb, void *data) diff --git a/ares_options.c b/ares_options.c index d00368a..bb1d5d5 100644 --- a/ares_options.c +++ b/ares_options.c @@ -21,6 +21,7 @@ #include "ares.h" #include "ares_data.h" #include "ares_private.h" +#include "inet_net_pton.h" int ares_get_servers(ares_channel channel, @@ -125,3 +126,127 @@ int ares_set_servers(ares_channel channel, return ARES_SUCCESS; } + +/* Incomming string format: host[:port][,host[:port]]... */ +int ares_set_servers_csv(ares_channel channel, + const char* _csv) +{ + struct ares_addr_node *srvr; + int num_srvrs = 0; + int i; + char* csv = NULL; + char* ptr; + char* start_host; + int port; + bool found_port; + int rv = ARES_SUCCESS; + struct ares_addr_node *servers = NULL; + struct ares_addr_node *last = NULL; + + if (ares_library_initialized() != ARES_SUCCESS) + return ARES_ENOTINITIALIZED; + + if (!channel) + return ARES_ENODATA; + + ares__destroy_servers_state(channel); + + i = strlen(_csv); + if (i == 0) + return ARES_SUCCESS; /* blank all servers */ + + csv = malloc(i + 2); + strcpy(csv, _csv); + if (csv[i-1] != ',') { /* make parsing easier by ensuring ending ',' */ + csv[i] = ','; + csv[i+1] = 0; + } + + ptr = csv; + start_host = csv; + found_port = false; + for (ptr; *ptr; ptr++) { + if (*ptr == ',') { + char* pp = ptr - 1; + struct in_addr in4; + struct ares_in6_addr in6; + struct ares_addr_node *s = NULL; + + *ptr = 0; /* null terminate host:port string */ + /* Got an entry..see if port was specified. */ + while (pp > start_host) { + if (*pp == ':') + break; /* yes */ + if (!isdigit(*pp)) { + /* Found end of digits before we found :, so wasn't a port */ + pp = ptr; + break; + } + pp--; + } + if ((pp != start_host) && ((pp + 1) < ptr)) { + /* Found it. */ + found_port = true; + port = atoi(pp + 1); + *pp = 0; /* null terminate host */ + } + /* resolve host, try ipv4 first, rslt is in network byte order */ + rv = ares_inet_pton(AF_INET, start_host, &in4); + if (!rv) { + /* Ok, try IPv6 then */ + rv = ares_inet_pton(AF_INET6, start_host, &in6); + if (!rv) { + rv = ARES_EBADSTR; + goto out; + } + /* was ipv6, add new server */ + s = malloc(sizeof(*s)); + if (!s) { + rv = ARES_ENOMEM; + goto out; + } + s->family = AF_INET6; + memcpy(&s->addr, &in6, sizeof(struct ares_in6_addr)); + } + else { + /* was ipv4, add new server */ + s = malloc(sizeof(*s)); + if (!s) { + rv = ARES_ENOMEM; + goto out; + } + s->family = AF_INET; + memcpy(&s->addr, &in4, sizeof(struct in_addr)); + } + if (s) { + /* TODO: Add port to ares_addr_node and assign it here. */ + + s->next = NULL; + if (last) { + last->next = s; + } + else { + servers = s; + last = s; + } + } + + /* Set up for next one */ + found_port = false; + start_host = ptr + 1; + } + } + + rv = ares_set_servers(channel, servers); + + out: + if (csv) + free(csv); + while (servers) { + struct ares_addr_node *s = servers; + servers = servers->next; + free(s); + } + + return rv; +} diff --git a/ares_private.h b/ares_private.h index 0df5cb7..6f86a3a 100644 --- a/ares_private.h +++ b/ares_private.h @@ -257,6 +257,13 @@ struct ares_channeldata { int nsort; char *lookups; + /* For binding to local devices and/or IP addresses. Leave + * them null/zero for no binding. + */ + char local_dev_name[32]; + uint32_t local_ip4; + unsigned char local_ip6[16]; + int optmask; /* the option bitfield passed in at init time */ /* Server addresses and communications state */ diff --git a/ares_process.c b/ares_process.c index 3ef1ddb..14dce71 100644 --- a/ares_process.c +++ b/ares_process.c @@ -91,7 +91,6 @@ static void skip_server(ares_channel channel, struct query *query, int whichserver); static void next_server(ares_channel channel, struct query *query, struct timeval *now); -static int configure_socket(ares_socket_t s, ares_channel channel); static int open_tcp_socket(ares_channel channel, struct server_state *server); static int open_udp_socket(ares_channel channel, struct server_state *server); static int same_questions(const unsigned char *qbuf, int qlen, @@ -869,7 +868,7 @@ static int setsocknonblock(ares_socket_t sockfd, /* operate on this */ #endif } -static int configure_socket(ares_socket_t s, ares_channel channel) +static int configure_socket(ares_socket_t s, int family, ares_channel channel) { setsocknonblock(s, TRUE); @@ -892,8 +891,39 @@ static int configure_socket(ares_socket_t s, ares_channel channel) sizeof(channel->socket_receive_buffer_size)) == -1) return -1; +#ifdef SO_BINDTODEVICE + if (channel->local_dev_name[0]) { + if (setsockopt(s, SOL_SOCKET, SO_BINDTODEVICE, + channel->local_dev_name, sizeof(channel->local_dev_name))) { + /* Only root can do this, and usually not fatal if it doesn't work, so */ + /* just continue on. */ + } + } +#endif + + if (family == AF_INET) { + if (channel->local_ip4) { + struct sockaddr_in sa; + memset(&sa, 0, sizeof(sa)); + sa.sin_family = AF_INET; + sa.sin_addr.s_addr = htonl(channel->local_ip4); + if (bind(s, (struct sockaddr*)&sa, sizeof(sa)) < 0) + return -1; + } + } + else if (family == AF_INET6) { + if (memcmp(channel->local_ip6, &in6addr_any, sizeof(channel->local_ip6)) != 0) { + struct sockaddr_in6 sa; + memset(&sa, 0, sizeof(sa)); + sa.sin6_family = AF_INET6; + memcpy(&sa.sin6_addr, channel->local_ip6, sizeof(channel->local_ip6)); + if (bind(s, (struct sockaddr*)&sa, sizeof(sa)) < 0) + return -1; + } + } + return 0; - } +} static int open_tcp_socket(ares_channel channel, struct server_state *server) { @@ -936,7 +966,7 @@ static int open_tcp_socket(ares_channel channel, struct server_state *server) return -1; /* Configure it. */ - if (configure_socket(s, channel) < 0) + if (configure_socket(s, server->addr.family, channel) < 0) { sclose(s); return -1; @@ -1028,7 +1058,7 @@ static int open_udp_socket(ares_channel channel, struct server_state *server) return -1; /* Set the socket non-blocking. */ - if (configure_socket(s, channel) < 0) + if (configure_socket(s, server->addr.family, channel) < 0) { sclose(s); return -1; diff --git a/ares_set_local_dev.3 b/ares_set_local_dev.3 new file mode 100644 index 0000000..b967c89 --- /dev/null +++ b/ares_set_local_dev.3 @@ -0,0 +1,39 @@ +.\" +.\" Copyright 2010 by Ben Greear <greearb@candelatech.com> +.\" +.\" Permission to use, copy, modify, and distribute this +.\" software and its documentation for any purpose and without +.\" fee is hereby granted, provided that the above copyright +.\" notice appear in all copies and that both that copyright +.\" notice and this permission notice appear in supporting +.\" documentation, and that the name of M.I.T. not be used in +.\" advertising or publicity pertaining to distribution of the +.\" software without specific, written prior permission. +.\" M.I.T. makes no representations about the suitability of +.\" this software for any purpose. It is provided "as is" +.\" without express or implied warranty. +.\" +.TH ARES_SET_LOCAL_DEV 3 "30 June 2010" +.SH NAME +ares_set_local_dev \- Bind to a specific network device when creating sockets. +.SH SYNOPSIS +.nf +.B #include <ares.h> +.PP +.B void ares_set_local_dev(ares_channel \fIchannel\fP, const char* \fIlocal_dev_name\fP) +.fi +.SH DESCRIPTION +The \fBares_set_local_dev\fP function causes all future sockets +to be bound to this device with SO_BINDTODEVICE. This forces communications +to go over a certain interface, which can be useful on multi-homed machines. +This option is only supported on Linux, and root priviledges are required +for the option to work. If SO_BINDTODEVICE is not supported or the +setsocktop call fails (probably because of permissions), the error is +silently ignored. +.SH SEE ALSO +.BR ares_set_local_ipv4 (3) +.BR ares_set_local_ipv6 (3) +.SH NOTES +This function was added in c-ares 1.7.2 +.SH AUTHOR +Ben Greear diff --git a/ares_set_local_ip4.3 b/ares_set_local_ip4.3 new file mode 100644 index 0000000..b7955b7 --- /dev/null +++ b/ares_set_local_ip4.3 @@ -0,0 +1,34 @@ +.\" +.\" Copyright 2010 by Ben Greear <greearb@candelatech.com> +.\" +.\" Permission to use, copy, modify, and distribute this +.\" software and its documentation for any purpose and without +.\" fee is hereby granted, provided that the above copyright +.\" notice appear in all copies and that both that copyright +.\" notice and this permission notice appear in supporting +.\" documentation, and that the name of M.I.T. not be used in +.\" advertising or publicity pertaining to distribution of the +.\" software without specific, written prior permission. +.\" M.I.T. makes no representations about the suitability of +.\" this software for any purpose. It is provided "as is" +.\" without express or implied warranty. +.\" +.TH ARES_SET_LOCAL_IP4 3 "30 June 2010" +.SH NAME +ares_set_local_ip4 \- Set local IPv4 address outgoing requests. +.SH SYNOPSIS +.nf +.B #include <ares.h> +.PP +.B void ares_set_local_ip4(ares_channel \fIchannel\fP, uint32_t \fIlocal_ip\fP) +.fi +.SH DESCRIPTION +The \fBares_set_local_ip4\fP function sets the IP address for outbound +requests. This allows users to specify outbound interfaces when used +on multi-homed systems. +.SH SEE ALSO +.BR ares_set_local_ip6 (3) +.SH NOTES +This function was added in c-ares 1.7.2 +.SH AUTHOR +Ben Greear diff --git a/ares_set_local_ip6.3 b/ares_set_local_ip6.3 new file mode 100644 index 0000000..7ca0afc --- /dev/null +++ b/ares_set_local_ip6.3 @@ -0,0 +1,34 @@ +.\" +.\" Copyright 2010 by Ben Greear <greearb@candelatech.com> +.\" +.\" Permission to use, copy, modify, and distribute this +.\" software and its documentation for any purpose and without +.\" fee is hereby granted, provided that the above copyright +.\" notice appear in all copies and that both that copyright +.\" notice and this permission notice appear in supporting +.\" documentation, and that the name of M.I.T. not be used in +.\" advertising or publicity pertaining to distribution of the +.\" software without specific, written prior permission. +.\" M.I.T. makes no representations about the suitability of +.\" this software for any purpose. It is provided "as is" +.\" without express or implied warranty. +.\" +.TH ARES_SET_LOCAL_IP6 3 "30 June 2010" +.SH NAME +ares_set_local_ip6 \- Set local IPv6 address outgoing requests. +.SH SYNOPSIS +.nf +.B #include <ares.h> +.PP +.B void ares_set_local_ip6(ares_channel \fIchannel\fP, const unsigned char* \fIlocal_ip6\fP) +.fi +.SH DESCRIPTION +The \fBares_set_local_ip6\fP function sets the IPv6 address for outbound +IPv6 requests. This allows users to specify outbound interfaces when used +on multi-homed systems. The local_ip6 argument must be 16 bytes in length. +.SH SEE ALSO +.BR ares_set_local_ip4 (3) +.SH NOTES +This function was added in c-ares 1.7.2 +.SH AUTHOR +Ben Greear diff --git a/ares_set_servers.3 b/ares_set_servers.3 index 63fb461..6f25c29 100644 --- a/ares_set_servers.3 +++ b/ares_set_servers.3 @@ -1,6 +1,5 @@ .\" -.\" Copyright 1998 by the Massachusetts Institute of Technology. -.\" Copyright (C) 2008-2010 by Daniel Stenberg +.\" Copyright 2010 by Ben Greear <greearb@candelatech.com> .\" .\" Permission to use, copy, modify, and distribute this .\" software and its documentation for any purpose and without @@ -67,6 +66,7 @@ was invalid. .B ARES_ENOTINITIALIZED c-ares library initialization not yet performed. .SH SEE ALSO +.BR ares_set_servers_csv (3), .BR ares_get_servers (3), .BR ares_init_options (3), .BR ares_dup(3) diff --git a/ares_set_servers_csv.3 b/ares_set_servers_csv.3 new file mode 100644 index 0000000..a37c437 --- /dev/null +++ b/ares_set_servers_csv.3 @@ -0,0 +1,43 @@ +.\" +.\" Copyright 2010 by Ben Greear <greearb@candelatech.com> +.\" +.\" Permission to use, copy, modify, and distribute this +.\" software and its documentation for any purpose and without +.\" fee is hereby granted, provided that the above copyright +.\" notice appear in all copies and that both that copyright +.\" notice and this permission notice appear in supporting +.\" documentation, and that the name of M.I.T. not be used in +.\" advertising or publicity pertaining to distribution of the +.\" software without specific, written prior permission. +.\" M.I.T. makes no representations about the suitability of +.\" this software for any purpose. It is provided "as is" +.\" without express or implied warranty. +.\" +.TH ARES_SET_SERVERS_CSV 3 "30 June 2010" +.SH NAME +ares_set_servers_csv \- Set list of DNS servers to be used. +.SH SYNOPSIS +.nf +.B #include <ares.h> +.PP +.B void ares_set_servers_csv(ares_channel \fIchannel\fP, const char* \fIservers\fP) +.fi +.SH DESCRIPTION +The \fBares_set_servers_csv\fP function sets the list of DNS servers +that ARES will query. The format of the servers option is: + +host[:port][,host[:port]]... + +For example: + +192.168.1.100,192.168.1.101,3.4.5.6 + +.SH SEE ALSO +.BR ares_set_servers (3) +.SH NOTES +The port option is currently ignored by c-ares internals +and the standard port is always used. + +This function was added in c-ares 1.7.2 +.SH AUTHOR +Ben Greear |