Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

local-bind: Support binding to local interface/IPs

Add 3 new functions to set the local binding for the out-going
socket connection, and add ares_set_servers_csv() to set a
list of servers at once as a comma-separated string.

Signed-off-by: Ben Greear <greearb@candelatech.com>
  • Loading branch information...
commit e3b04e5a4796215d2483aba3cb75c72ba337ac14 1 parent 45a09b7
@greearb greearb authored committed
View
19 ares.h
@@ -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);
View
30 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)
View
125 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;
+}
View
7 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 */
View
40 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;
View
39 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
View
34 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
View
34 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
View
4 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)
View
43 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
Please sign in to comment.
Something went wrong with that request. Please try again.