mirror of
https://frontier.innolan.net/rainlance/c-ares.git
synced 2025-11-20 17:40:52 +00:00
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>
This commit is contained in:
committed by
Daniel Stenberg
parent
45a09b7efb
commit
e3b04e5a47
19
ares.h
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);
|
||||
|
||||
|
||||
30
ares_init.c
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)
|
||||
|
||||
125
ares_options.c
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;
|
||||
}
|
||||
|
||||
@ -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 */
|
||||
|
||||
@ -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;
|
||||
|
||||
39
ares_set_local_dev.3
Normal file
39
ares_set_local_dev.3
Normal file
@ -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
|
||||
34
ares_set_local_ip4.3
Normal file
34
ares_set_local_ip4.3
Normal file
@ -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
|
||||
34
ares_set_local_ip6.3
Normal file
34
ares_set_local_ip6.3
Normal file
@ -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
|
||||
@ -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)
|
||||
|
||||
43
ares_set_servers_csv.3
Normal file
43
ares_set_servers_csv.3
Normal file
@ -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
|
||||
Reference in New Issue
Block a user