Add ares_init_options() configurability for path to resolv.conf file

Add resolvconf_path to end of struct ares_options with ARES_OPT_RESOLVCONF option
so on Unix-like systems a custom path can be specified.  If no path is specified, 
/etc/resolv.conf is used like normal.

Fix By: Sarat Addepalli @SirR4T 
Fixes Bug: #220 
Review By: Brad House @bradh352
pull/223/head
Sarat Addepalli 7 years ago committed by Brad House
parent ad5852734c
commit 7e6af8efbc
  1. 2
      ares.h
  2. 5
      ares_destroy.c
  3. 41
      ares_init.c
  4. 13
      ares_init_options.3
  5. 3
      ares_private.h
  6. 34
      test/ares-test-init.cc

@ -164,6 +164,7 @@ extern "C" {
#define ARES_OPT_ROTATE (1 << 14) #define ARES_OPT_ROTATE (1 << 14)
#define ARES_OPT_EDNSPSZ (1 << 15) #define ARES_OPT_EDNSPSZ (1 << 15)
#define ARES_OPT_NOROTATE (1 << 16) #define ARES_OPT_NOROTATE (1 << 16)
#define ARES_OPT_RESOLVCONF (1 << 17)
/* Nameinfo flag values */ /* Nameinfo flag values */
#define ARES_NI_NOFQDN (1 << 0) #define ARES_NI_NOFQDN (1 << 0)
@ -270,6 +271,7 @@ struct ares_options {
struct apattern *sortlist; struct apattern *sortlist;
int nsort; int nsort;
int ednspsz; int ednspsz;
char *resolvconf_path;
}; };
struct hostent; struct hostent;

@ -36,6 +36,8 @@ void ares_destroy_options(struct ares_options *options)
ares_free(options->sortlist); ares_free(options->sortlist);
if(options->lookups) if(options->lookups)
ares_free(options->lookups); ares_free(options->lookups);
if(options->resolvconf_path)
ares_free(options->resolvconf_path);
} }
void ares_destroy(ares_channel channel) void ares_destroy(ares_channel channel)
@ -85,6 +87,9 @@ void ares_destroy(ares_channel channel)
if (channel->lookups) if (channel->lookups)
ares_free(channel->lookups); ares_free(channel->lookups);
if (channel->resolvconf_path)
ares_free(channel->resolvconf_path);
ares_free(channel); ares_free(channel);
} }

@ -169,6 +169,7 @@ int ares_init_options(ares_channel *channelptr, struct ares_options *options,
channel->sock_config_cb_data = NULL; channel->sock_config_cb_data = NULL;
channel->sock_funcs = NULL; channel->sock_funcs = NULL;
channel->sock_func_cb_data = NULL; channel->sock_func_cb_data = NULL;
channel->resolvconf_path = NULL;
channel->last_server = 0; channel->last_server = 0;
channel->last_timeout_processed = (time_t)now.tv_sec; channel->last_timeout_processed = (time_t)now.tv_sec;
@ -242,6 +243,8 @@ done:
ares_free(channel->sortlist); ares_free(channel->sortlist);
if(channel->lookups) if(channel->lookups)
ares_free(channel->lookups); ares_free(channel->lookups);
if(channel->resolvconf_path)
ares_free(channel->resolvconf_path);
ares_free(channel); ares_free(channel);
return status; return status;
} }
@ -347,7 +350,7 @@ int ares_save_options(ares_channel channel, struct ares_options *options,
(*optmask) = (ARES_OPT_FLAGS|ARES_OPT_TRIES|ARES_OPT_NDOTS| (*optmask) = (ARES_OPT_FLAGS|ARES_OPT_TRIES|ARES_OPT_NDOTS|
ARES_OPT_UDP_PORT|ARES_OPT_TCP_PORT|ARES_OPT_SOCK_STATE_CB| ARES_OPT_UDP_PORT|ARES_OPT_TCP_PORT|ARES_OPT_SOCK_STATE_CB|
ARES_OPT_SERVERS|ARES_OPT_DOMAINS|ARES_OPT_LOOKUPS| ARES_OPT_SERVERS|ARES_OPT_DOMAINS|ARES_OPT_LOOKUPS|
ARES_OPT_SORTLIST|ARES_OPT_TIMEOUTMS); ARES_OPT_SORTLIST|ARES_OPT_TIMEOUTMS|ARES_OPT_RESOLVCONF);
(*optmask) |= (channel->rotate ? ARES_OPT_ROTATE : ARES_OPT_NOROTATE); (*optmask) |= (channel->rotate ? ARES_OPT_ROTATE : ARES_OPT_NOROTATE);
/* Copy easy stuff */ /* Copy easy stuff */
@ -422,6 +425,13 @@ int ares_save_options(ares_channel channel, struct ares_options *options,
} }
options->nsort = channel->nsort; options->nsort = channel->nsort;
/* copy path for resolv.conf file */
if (channel->resolvconf_path) {
options->resolvconf_path = ares_strdup(channel->resolvconf_path);
if (!options->resolvconf_path)
return ARES_ENOMEM;
}
return ARES_SUCCESS; return ARES_SUCCESS;
} }
@ -530,6 +540,14 @@ static int init_by_options(ares_channel channel,
channel->nsort = options->nsort; channel->nsort = options->nsort;
} }
/* Set path for resolv.conf file, if given. */
if ((optmask & ARES_OPT_RESOLVCONF) && !channel->resolvconf_path)
{
channel->resolvconf_path = ares_strdup(options->resolvconf_path);
if (!channel->resolvconf_path && options->resolvconf_path)
return ARES_ENOMEM;
}
channel->optmask = optmask; channel->optmask = optmask;
return ARES_SUCCESS; return ARES_SUCCESS;
@ -1644,6 +1662,7 @@ static int init_by_resolv_conf(ares_channel channel)
size_t linesize; size_t linesize;
int error; int error;
int update_domains; int update_domains;
const char *resolvconf_path;
/* Don't read resolv.conf and friends if we don't have to */ /* Don't read resolv.conf and friends if we don't have to */
if (ARES_CONFIG_CHECK(channel)) if (ARES_CONFIG_CHECK(channel))
@ -1652,7 +1671,14 @@ static int init_by_resolv_conf(ares_channel channel)
/* Only update search domains if they're not already specified */ /* Only update search domains if they're not already specified */
update_domains = (channel->ndomains == -1); update_domains = (channel->ndomains == -1);
fp = fopen(PATH_RESOLV_CONF, "r"); /* Support path for resolvconf filename set by ares_init_options */
if(channel->resolvconf_path) {
resolvconf_path = channel->resolvconf_path;
} else {
resolvconf_path = PATH_RESOLV_CONF;
}
fp = fopen(resolvconf_path, "r");
if (fp) { if (fp) {
while ((status = ares__read_line(fp, &line, &linesize)) == ARES_SUCCESS) while ((status = ares__read_line(fp, &line, &linesize)) == ARES_SUCCESS)
{ {
@ -1663,10 +1689,10 @@ static int init_by_resolv_conf(ares_channel channel)
else if ((p = try_config(line, "search", ';')) && update_domains) else if ((p = try_config(line, "search", ';')) && update_domains)
status = set_search(channel, p); status = set_search(channel, p);
else if ((p = try_config(line, "nameserver", ';')) && else if ((p = try_config(line, "nameserver", ';')) &&
channel->nservers == -1) channel->nservers == -1)
status = config_nameserver(&servers, &nservers, p); status = config_nameserver(&servers, &nservers, p);
else if ((p = try_config(line, "sortlist", ';')) && else if ((p = try_config(line, "sortlist", ';')) &&
channel->nsort == -1) channel->nsort == -1)
status = config_sortlist(&sortlist, &nsort, p); status = config_sortlist(&sortlist, &nsort, p);
else if ((p = try_config(line, "options", ';'))) else if ((p = try_config(line, "options", ';')))
status = set_options(channel, p); status = set_options(channel, p);
@ -1686,7 +1712,7 @@ static int init_by_resolv_conf(ares_channel channel)
break; break;
default: default:
DEBUGF(fprintf(stderr, "fopen() failed with error: %d %s\n", DEBUGF(fprintf(stderr, "fopen() failed with error: %d %s\n",
error, strerror(error))); error, strerror(error)));
DEBUGF(fprintf(stderr, "Error opening file: %s\n", PATH_RESOLV_CONF)); DEBUGF(fprintf(stderr, "Error opening file: %s\n", PATH_RESOLV_CONF));
status = ARES_EFILE; status = ARES_EFILE;
} }
@ -1954,6 +1980,11 @@ static int init_by_defaults(ares_channel channel)
ares_free(channel->lookups); ares_free(channel->lookups);
channel->lookups = NULL; channel->lookups = NULL;
} }
if(channel->resolvconf_path) {
ares_free(channel->resolvconf_path);
channel->resolvconf_path = NULL;
}
} }
if(hostname) if(hostname)

@ -40,6 +40,7 @@ struct ares_options {
struct apattern *sortlist; struct apattern *sortlist;
int nsort; int nsort;
int ednspsz; int ednspsz;
char *resolvconf_path;
}; };
int ares_init_options(ares_channel *\fIchannelptr\fP, int ares_init_options(ares_channel *\fIchannelptr\fP,
@ -181,6 +182,15 @@ The receive buffer size to set for the socket.
.B ARES_OPT_EDNSPSZ .B ARES_OPT_EDNSPSZ
.B int \fIednspsz\fP; .B int \fIednspsz\fP;
.br .br
.TP 18
.B ARES_OPT_RESOLVCONF
.B char *\fIresolvconf_path\fP;
The path to use for reading the resolv.conf file. The
.I resolvconf_path
should be set to a path string, and will be honoured on *nix like systems. The
default is
.B /etc/resolv.conf
.br
The message size to be advertized in EDNS; only takes effect if the The message size to be advertized in EDNS; only takes effect if the
.B ARES_FLAG_EDNS .B ARES_FLAG_EDNS
flag is set. flag is set.
@ -259,6 +269,9 @@ c-ares library initialization not yet performed.
.SH NOTES .SH NOTES
When initializing from When initializing from
.B /etc/resolv.conf, .B /etc/resolv.conf,
(or, alternatively when specified by the
.I resolvconf_path
path location)
\fBares_init_options(3)\fP reads the \fIdomain\fP and \fIsearch\fP directives \fBares_init_options(3)\fP reads the \fIdomain\fP and \fIsearch\fP directives
to allow lookups of short names relative to the domains specified. The to allow lookups of short names relative to the domains specified. The
\fIdomain\fP and \fIsearch\fP directives override one another. If more that \fIdomain\fP and \fIsearch\fP directives override one another. If more that

@ -325,6 +325,9 @@ struct ares_channeldata {
const struct ares_socket_functions * sock_funcs; const struct ares_socket_functions * sock_funcs;
void *sock_func_cb_data; void *sock_func_cb_data;
/* Path for resolv.conf file, configurable via ares_options */
char *resolvconf_path;
}; };
/* Memory management functions */ /* Memory management functions */

@ -86,6 +86,8 @@ TEST_F(LibraryTest, OptionsChannelInit) {
opts.lookups = strdup("b"); opts.lookups = strdup("b");
optmask |= ARES_OPT_LOOKUPS; optmask |= ARES_OPT_LOOKUPS;
optmask |= ARES_OPT_ROTATE; optmask |= ARES_OPT_ROTATE;
opts.resolvconf_path = strdup("/etc/resolv.conf");
optmask |= ARES_OPT_RESOLVCONF;
ares_channel channel = nullptr; ares_channel channel = nullptr;
EXPECT_EQ(ARES_SUCCESS, ares_init_options(&channel, &opts, optmask)); EXPECT_EQ(ARES_SUCCESS, ares_init_options(&channel, &opts, optmask));
@ -112,6 +114,7 @@ TEST_F(LibraryTest, OptionsChannelInit) {
EXPECT_EQ(std::string(opts.domains[0]), std::string(opts2.domains[0])); EXPECT_EQ(std::string(opts.domains[0]), std::string(opts2.domains[0]));
EXPECT_EQ(std::string(opts.domains[1]), std::string(opts2.domains[1])); EXPECT_EQ(std::string(opts.domains[1]), std::string(opts2.domains[1]));
EXPECT_EQ(std::string(opts.lookups), std::string(opts2.lookups)); EXPECT_EQ(std::string(opts.lookups), std::string(opts2.lookups));
EXPECT_EQ(std::string(opts.resolvconf_path), std::string(opts2.resolvconf_path));
ares_destroy_options(&opts); ares_destroy_options(&opts);
ares_destroy_options(&opts2); ares_destroy_options(&opts2);
@ -169,6 +172,8 @@ TEST_F(LibraryTest, OptionsChannelAllocFail) {
opts.lookups = strdup("b"); opts.lookups = strdup("b");
optmask |= ARES_OPT_LOOKUPS; optmask |= ARES_OPT_LOOKUPS;
optmask |= ARES_OPT_ROTATE; optmask |= ARES_OPT_ROTATE;
opts.resolvconf_path = strdup("/etc/resolv.conf");
optmask |= ARES_OPT_RESOLVCONF;
ares_channel channel = nullptr; ares_channel channel = nullptr;
for (int ii = 1; ii <= 8; ii++) { for (int ii = 1; ii <= 8; ii++) {
@ -396,6 +401,35 @@ CONTAINED_TEST_F(LibraryTest, ContainerFullResolvInit,
return HasFailure(); return HasFailure();
} }
// Allow path for resolv.conf to be configurable
NameContentList myresolvconf = {
{"/tmp/myresolv.cnf", " nameserver 1.2.3.4 \n"
"search first.com second.com\n"
"lookup bind\n"
"options debug ndots:5\n"
"sortlist 1.2.3.4/16 2.3.4.5\n"}};
CONTAINED_TEST_F(LibraryTest, ContainerMyResolvConfInit,
"myhostname", "mydomain.org", myresolvconf) {
char filename[] = "/tmp/myresolv.cnf";
ares_channel channel = nullptr;
struct ares_options options = {0};
options.resolvconf_path = strdup(filename);
int optmask = ARES_OPT_RESOLVCONF;
EXPECT_EQ(ARES_SUCCESS, ares_init_options(&channel, &options, optmask));
optmask = 0;
free(options.resolvconf_path);
options.resolvconf_path = NULL;
EXPECT_EQ(ARES_SUCCESS, ares_save_options(channel, &options, &optmask));
EXPECT_EQ(ARES_OPT_RESOLVCONF, (optmask & ARES_OPT_RESOLVCONF));
EXPECT_EQ(std::string(filename), std::string(options.resolvconf_path));
ares_destroy_options(&options);
ares_destroy(channel);
return HasFailure();
}
NameContentList hostconf = { NameContentList hostconf = {
{"/etc/resolv.conf", "nameserver 1.2.3.4\n" {"/etc/resolv.conf", "nameserver 1.2.3.4\n"
"sortlist1.2.3.4\n" // malformed line "sortlist1.2.3.4\n" // malformed line

Loading…
Cancel
Save