ares_search.c
Go to the documentation of this file.
1 
2 /* Copyright 1998 by the Massachusetts Institute of Technology.
3  *
4  * Permission to use, copy, modify, and distribute this
5  * software and its documentation for any purpose and without
6  * fee is hereby granted, provided that the above copyright
7  * notice appear in all copies and that both that copyright
8  * notice and this permission notice appear in supporting
9  * documentation, and that the name of M.I.T. not be used in
10  * advertising or publicity pertaining to distribution of the
11  * software without specific, written prior permission.
12  * M.I.T. makes no representations about the suitability of
13  * this software for any purpose. It is provided "as is"
14  * without express or implied warranty.
15  */
16 
17 #include "ares_setup.h"
18 
19 #ifdef HAVE_STRINGS_H
20 # include <strings.h>
21 #endif
22 
23 #include "ares.h"
24 #include "ares_private.h"
25 
26 struct search_query {
27  /* Arguments passed to ares_search */
29  char *name; /* copied into an allocated buffer */
30  int dnsclass;
31  int type;
33  void *arg;
34 
35  int status_as_is; /* error status from trying as-is */
36  int next_domain; /* next search domain to try */
37  int trying_as_is; /* current query is for name as-is */
38  int timeouts; /* number of timeouts we saw for this request */
39  int ever_got_nodata; /* did we ever get ARES_ENODATA along the way? */
40 };
41 
42 static void search_callback(void *arg, int status, int timeouts,
43  unsigned char *abuf, int alen);
44 static void end_squery(struct search_query *squery, int status,
45  unsigned char *abuf, int alen);
46 
47 void ares_search(ares_channel channel, const char *name, int dnsclass,
48  int type, ares_callback callback, void *arg)
49 {
50  struct search_query *squery;
51  char *s;
52  const char *p;
53  int status, ndots;
54 
55  /* Per RFC 7686, reject queries for ".onion" domain names with NXDOMAIN. */
57  {
58  callback(arg, ARES_ENOTFOUND, 0, NULL, 0);
59  return;
60  }
61 
62  /* If name only yields one domain to search, then we don't have
63  * to keep extra state, so just do an ares_query().
64  */
66  if (status != ARES_SUCCESS)
67  {
68  callback(arg, status, 0, NULL, 0);
69  return;
70  }
71  if (s)
72  {
74  ares_free(s);
75  return;
76  }
77 
78  /* Allocate a search_query structure to hold the state necessary for
79  * doing multiple lookups.
80  */
81  squery = ares_malloc(sizeof(struct search_query));
82  if (!squery)
83  {
84  callback(arg, ARES_ENOMEM, 0, NULL, 0);
85  return;
86  }
87  squery->channel = channel;
88  squery->name = ares_strdup(name);
89  if (!squery->name)
90  {
91  ares_free(squery);
92  callback(arg, ARES_ENOMEM, 0, NULL, 0);
93  return;
94  }
95  squery->dnsclass = dnsclass;
96  squery->type = type;
97  squery->status_as_is = -1;
98  squery->callback = callback;
99  squery->arg = arg;
100  squery->timeouts = 0;
101  squery->ever_got_nodata = 0;
102 
103  /* Count the number of dots in name. */
104  ndots = 0;
105  for (p = name; *p; p++)
106  {
107  if (*p == '.')
108  ndots++;
109  }
110 
111  /* If ndots is at least the channel ndots threshold (usually 1),
112  * then we try the name as-is first. Otherwise, we try the name
113  * as-is last.
114  */
115  if (ndots >= channel->ndots)
116  {
117  /* Try the name as-is first. */
118  squery->next_domain = 0;
119  squery->trying_as_is = 1;
121  }
122  else
123  {
124  /* Try the name as-is last; start with the first search domain. */
125  squery->next_domain = 1;
126  squery->trying_as_is = 0;
127  status = ares__cat_domain(name, channel->domains[0], &s);
128  if (status == ARES_SUCCESS)
129  {
131  ares_free(s);
132  }
133  else
134  {
135  /* failed, free the malloc()ed memory */
136  ares_free(squery->name);
137  ares_free(squery);
138  callback(arg, status, 0, NULL, 0);
139  }
140  }
141 }
142 
143 static void search_callback(void *arg, int status, int timeouts,
144  unsigned char *abuf, int alen)
145 {
146  struct search_query *squery = (struct search_query *) arg;
147  ares_channel channel = squery->channel;
148  char *s;
149 
150  squery->timeouts += timeouts;
151 
152  /* Stop searching unless we got a non-fatal error. */
154  && status != ARES_ENOTFOUND)
155  end_squery(squery, status, abuf, alen);
156  else
157  {
158  /* Save the status if we were trying as-is. */
159  if (squery->trying_as_is)
160  squery->status_as_is = status;
161 
162  /*
163  * If we ever get ARES_ENODATA along the way, record that; if the search
164  * should run to the very end and we got at least one ARES_ENODATA,
165  * then callers like ares_gethostbyname() may want to try a T_A search
166  * even if the last domain we queried for T_AAAA resource records
167  * returned ARES_ENOTFOUND.
168  */
169  if (status == ARES_ENODATA)
170  squery->ever_got_nodata = 1;
171 
172  if (squery->next_domain < channel->ndomains)
173  {
174  /* Try the next domain. */
175  status = ares__cat_domain(squery->name,
176  channel->domains[squery->next_domain], &s);
177  if (status != ARES_SUCCESS)
178  end_squery(squery, status, NULL, 0);
179  else
180  {
181  squery->trying_as_is = 0;
182  squery->next_domain++;
183  ares_query(channel, s, squery->dnsclass, squery->type,
184  search_callback, squery);
185  ares_free(s);
186  }
187  }
188  else if (squery->status_as_is == -1)
189  {
190  /* Try the name as-is at the end. */
191  squery->trying_as_is = 1;
192  ares_query(channel, squery->name, squery->dnsclass, squery->type,
193  search_callback, squery);
194  }
195  else {
196  if (squery->status_as_is == ARES_ENOTFOUND && squery->ever_got_nodata) {
197  end_squery(squery, ARES_ENODATA, NULL, 0);
198  }
199  else
200  end_squery(squery, squery->status_as_is, NULL, 0);
201  }
202  }
203 }
204 
205 static void end_squery(struct search_query *squery, int status,
206  unsigned char *abuf, int alen)
207 {
208  squery->callback(squery->arg, status, squery->timeouts, abuf, alen);
209  ares_free(squery->name);
210  ares_free(squery);
211 }
212 
213 /* Concatenate two domains. */
214 int ares__cat_domain(const char *name, const char *domain, char **s)
215 {
216  size_t nlen = strlen(name);
217  size_t dlen = strlen(domain);
218 
219  *s = ares_malloc(nlen + 1 + dlen + 1);
220  if (!*s)
221  return ARES_ENOMEM;
222  memcpy(*s, name, nlen);
223  (*s)[nlen] = '.';
224  memcpy(*s + nlen + 1, domain, dlen);
225  (*s)[nlen + 1 + dlen] = 0;
226  return ARES_SUCCESS;
227 }
228 
229 /* Determine if this name only yields one query. If it does, set *s to
230  * the string we should query, in an allocated buffer. If not, set *s
231  * to NULL.
232  */
233 int ares__single_domain(ares_channel channel, const char *name, char **s)
234 {
235  size_t len = strlen(name);
236  const char *hostaliases;
237  FILE *fp;
238  char *line = NULL;
239  int status;
240  size_t linesize;
241  const char *p, *q;
242  int error;
243 
244  /* If the name contains a trailing dot, then the single query is the name
245  * sans the trailing dot.
246  */
247  if ((len > 0) && (name[len - 1] == '.'))
248  {
249  *s = ares_strdup(name);
250  return (*s) ? ARES_SUCCESS : ARES_ENOMEM;
251  }
252 
253  if (!(channel->flags & ARES_FLAG_NOALIASES) && !strchr(name, '.'))
254  {
255  /* The name might be a host alias. */
256  hostaliases = getenv("HOSTALIASES");
257  if (hostaliases)
258  {
259  fp = fopen(hostaliases, "r");
260  if (fp)
261  {
262  while ((status = ares__read_line(fp, &line, &linesize))
263  == ARES_SUCCESS)
264  {
265  if (strncasecmp(line, name, len) != 0 ||
266  !ISSPACE(line[len]))
267  continue;
268  p = line + len;
269  while (ISSPACE(*p))
270  p++;
271  if (*p)
272  {
273  q = p + 1;
274  while (*q && !ISSPACE(*q))
275  q++;
276  *s = ares_malloc(q - p + 1);
277  if (*s)
278  {
279  memcpy(*s, p, q - p);
280  (*s)[q - p] = 0;
281  }
282  ares_free(line);
283  fclose(fp);
284  return (*s) ? ARES_SUCCESS : ARES_ENOMEM;
285  }
286  }
287  ares_free(line);
288  fclose(fp);
289  if (status != ARES_SUCCESS && status != ARES_EOF)
290  return status;
291  }
292  else
293  {
294  error = ERRNO;
295  switch(error)
296  {
297  case ENOENT:
298  case ESRCH:
299  break;
300  default:
301  DEBUGF(fprintf(stderr, "fopen() failed with error: %d %s\n",
302  error, strerror(error)));
303  DEBUGF(fprintf(stderr, "Error opening file: %s\n",
304  hostaliases));
305  *s = NULL;
306  return ARES_EFILE;
307  }
308  }
309  }
310  }
311 
312  if (channel->flags & ARES_FLAG_NOSEARCH || channel->ndomains == 0)
313  {
314  /* No domain search to do; just try the name as-is. */
315  *s = ares_strdup(name);
316  return (*s) ? ARES_SUCCESS : ARES_ENOMEM;
317  }
318 
319  *s = NULL;
320  return ARES_SUCCESS;
321 }
search_query::ever_got_nodata
int ever_got_nodata
Definition: ares_search.c:39
ARES_ENOMEM
#define ARES_ENOMEM
Definition: ares.h:117
strncasecmp
#define strncasecmp(p1, p2, n)
Definition: ares_private.h:119
ares_query
CARES_EXTERN void ares_query(ares_channel channel, const char *name, int dnsclass, int type, ares_callback callback, void *arg)
Definition: ares_query.c:105
ares.h
search_callback
static void search_callback(void *arg, int status, int timeouts, unsigned char *abuf, int alen)
Definition: ares_search.c:143
ares_strdup
char * ares_strdup(const char *s1)
Definition: ares_strdup.c:23
search_query::next_domain
int next_domain
Definition: ares_search.c:36
search_query::timeouts
int timeouts
Definition: ares_search.c:38
ares_callback
void(* ares_callback)(void *arg, int status, int timeouts, unsigned char *abuf, int alen)
Definition: ares.h:290
ares__single_domain
int ares__single_domain(ares_channel channel, const char *name, char **s)
Definition: ares_search.c:233
search_query::arg
void * arg
Definition: ares_search.c:33
error
grpc_error_handle error
Definition: retry_filter.cc:499
search_query::status_as_is
int status_as_is
Definition: ares_search.c:35
status
absl::Status status
Definition: rls.cc:251
setup.name
name
Definition: setup.py:542
ares__is_onion_domain
int ares__is_onion_domain(const char *name)
Definition: ares_getnameinfo.c:438
xds_manager.p
p
Definition: xds_manager.py:60
ares__read_line
int ares__read_line(FILE *fp, char **buf, size_t *bufsize)
Definition: ares__read_line.c:31
DEBUGF
#define DEBUGF(x)
Definition: setup_once.h:402
python_utils.port_server.stderr
stderr
Definition: port_server.py:51
search_query::dnsclass
int dnsclass
Definition: ares_search.c:30
memcpy
memcpy(mem, inblock.get(), min(CONTAINING_RECORD(inblock.get(), MEMBLOCK, data) ->size, size))
ARES_ENOTFOUND
#define ARES_ENOTFOUND
Definition: ares.h:104
channel
wrapped_grpc_channel * channel
Definition: src/php/ext/grpc/call.h:33
ares_malloc
void *(* ares_malloc)(size_t size)=default_malloc
Definition: ares_library_init.c:58
ARES_ESERVFAIL
#define ARES_ESERVFAIL
Definition: ares.h:103
end_squery
static void end_squery(struct search_query *squery, int status, unsigned char *abuf, int alen)
Definition: ares_search.c:205
ARES_ENODATA
#define ARES_ENODATA
Definition: ares.h:101
search_query::channel
ares_channel channel
Definition: ares_search.c:28
arg
Definition: cmdline.cc:40
ERRNO
#define ERRNO
Definition: fake_udp_and_tcp_server.cc:40
callback
static void callback(void *arg, int status, int timeouts, struct hostent *host)
Definition: acountry.c:224
ARES_SUCCESS
#define ARES_SUCCESS
Definition: ares.h:98
ares_setup.h
search_query::trying_as_is
int trying_as_is
Definition: ares_search.c:37
ares_channeldata
Definition: ares_private.h:266
ARES_EFILE
#define ARES_EFILE
Definition: ares.h:116
benchmark.FILE
FILE
Definition: benchmark.py:21
grpc::fclose
fclose(creds_file)
ares_free
void(* ares_free)(void *ptr)=default_free
Definition: ares_library_init.c:60
regen-readme.line
line
Definition: regen-readme.py:30
arg
struct arg arg
search_query::name
char * name
Definition: ares_search.c:29
ares_search
void ares_search(ares_channel channel, const char *name, int dnsclass, int type, ares_callback callback, void *arg)
Definition: ares_search.c:47
ares_private.h
ARES_FLAG_NOALIASES
#define ARES_FLAG_NOALIASES
Definition: ares.h:148
search_query::callback
ares_callback callback
Definition: ares_search.c:32
asyncio_get_stats.type
type
Definition: asyncio_get_stats.py:37
len
int len
Definition: abseil-cpp/absl/base/internal/low_level_alloc_test.cc:46
search_query::type
int type
Definition: ares_search.c:31
ARES_EOF
#define ARES_EOF
Definition: ares.h:115
ARES_FLAG_NOSEARCH
#define ARES_FLAG_NOSEARCH
Definition: ares.h:147
getenv
#define getenv(ptr)
Definition: ares_private.h:106
ares__cat_domain
int ares__cat_domain(const char *name, const char *domain, char **s)
Definition: ares_search.c:214
ISSPACE
#define ISSPACE(x)
Definition: setup_once.h:275
search_query
Definition: ares_search.c:26


grpc
Author(s):
autogenerated on Fri May 16 2025 02:57:43