]> asedeno.scripts.mit.edu Git - 1ts-debian.git/blob - libares/ares_search.c
need automake as a build-dep, even though we don't use most of it
[1ts-debian.git] / libares / ares_search.c
1 /* Copyright 1998 by the Massachusetts Institute of Technology.
2  *
3  * Permission to use, copy, modify, and distribute this
4  * software and its documentation for any purpose and without
5  * fee is hereby granted, provided that the above copyright
6  * notice appear in all copies and that both that copyright
7  * notice and this permission notice appear in supporting
8  * documentation, and that the name of M.I.T. not be used in
9  * advertising or publicity pertaining to distribution of the
10  * software without specific, written prior permission.
11  * M.I.T. makes no representations about the suitability of
12  * this software for any purpose.  It is provided "as is"
13  * without express or implied warranty.
14  */
15
16 static const char rcsid[] = "$Id: ares_search.c,v 1.3 2000/09/21 19:16:02 ghudson Exp $";
17
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include <string.h>
21 #include <ctype.h>
22 #include "ares.h"
23 #include "ares_private.h"
24
25 struct search_query {
26   /* Arguments passed to ares_search */
27   ares_channel channel;
28   char *name;                   /* copied into an allocated buffer */
29   int dnsclass;
30   int type;
31   ares_callback callback;
32   void *arg;
33
34   int status_as_is;             /* error status from trying as-is */
35   int next_domain;              /* next search domain to try */
36   int trying_as_is;             /* current query is for name as-is */
37 };
38
39 static void search_callback(void *arg, int status, unsigned char *abuf,
40                             int alen);
41 static void end_squery(struct search_query *squery, int status,
42                        unsigned char *abuf, int alen);
43 static int cat_domain(const char *name, const char *domain, char **s);
44 static int single_domain(ares_channel channel, const char *name, char **s);
45
46 void ares_search(ares_channel channel, const char *name, int dnsclass,
47                  int type, ares_callback callback, void *arg)
48 {
49   struct search_query *squery;
50   char *s;
51   const char *p;
52   int status, ndots;
53
54   /* If name only yields one domain to search, then we don't have
55    * to keep extra state, so just do an ares_query().
56    */
57   status = single_domain(channel, name, &s);
58   if (status != ARES_SUCCESS)
59     {
60       callback(arg, status, NULL, 0);
61       return;
62     }
63   if (s)
64     {
65       ares_query(channel, s, dnsclass, type, callback, arg);
66       free(s);
67       return;
68     }
69
70   /* Allocate a search_query structure to hold the state necessary for
71    * doing multiple lookups.
72    */
73   squery = malloc(sizeof(struct search_query));
74   if (!squery)
75     {
76       callback(arg, ARES_ENOMEM, NULL, 0);
77       return;
78     }
79   squery->channel = channel;
80   squery->name = strdup(name);
81   if (!squery->name)
82     {
83       free(squery);
84       callback(arg, ARES_ENOMEM, NULL, 0);
85       return;
86     }
87   squery->dnsclass = dnsclass;
88   squery->type = type;
89   squery->status_as_is = -1;
90   squery->callback = callback;
91   squery->arg = arg;
92
93   /* Count the number of dots in name. */
94   ndots = 0;
95   for (p = name; *p; p++)
96     {
97       if (*p == '.')
98         ndots++;
99     }
100
101   /* If ndots is at least the channel ndots threshold (usually 1),
102    * then we try the name as-is first.  Otherwise, we try the name
103    * as-is last.
104    */
105   if (ndots >= channel->ndots)
106     {
107       /* Try the name as-is first. */
108       squery->next_domain = 0;
109       squery->trying_as_is = 1;
110       ares_query(channel, name, dnsclass, type, search_callback, squery);
111     }
112   else
113     {
114       /* Try the name as-is last; start with the first search domain. */
115       squery->next_domain = 1;
116       squery->trying_as_is = 0;
117       status = cat_domain(name, channel->domains[0], &s);
118       if (status == ARES_SUCCESS)
119         {
120           ares_query(channel, s, dnsclass, type, search_callback, squery);
121           free(s);
122         }
123       else
124         callback(arg, status, NULL, 0);
125     }
126 }
127
128 static void search_callback(void *arg, int status, unsigned char *abuf,
129                             int alen)
130 {
131   struct search_query *squery = (struct search_query *) arg;
132   ares_channel channel = squery->channel;
133   char *s;
134
135   /* Stop searching unless we got a non-fatal error. */
136   if (status != ARES_ENODATA && status != ARES_ESERVFAIL
137       && status != ARES_ENOTFOUND)
138     end_squery(squery, status, abuf, alen);
139   else
140     {
141       /* Save the status if we were trying as-is. */
142       if (squery->trying_as_is)
143         squery->status_as_is = status;
144       if (squery->next_domain < channel->ndomains)
145         {
146           /* Try the next domain. */
147           status = cat_domain(squery->name,
148                               channel->domains[squery->next_domain], &s);
149           if (status != ARES_SUCCESS)
150             end_squery(squery, status, NULL, 0);
151           else
152             {
153               squery->trying_as_is = 0;
154               squery->next_domain++;
155               ares_query(channel, s, squery->dnsclass, squery->type,
156                          search_callback, squery);
157               free(s);
158             }
159         }
160       else if (squery->status_as_is == -1)
161         {
162           /* Try the name as-is at the end. */
163           squery->trying_as_is = 1;
164           ares_query(channel, squery->name, squery->dnsclass, squery->type,
165                      search_callback, squery);
166         }
167       else
168         end_squery(squery, squery->status_as_is, NULL, 0);
169     }
170 }
171
172 static void end_squery(struct search_query *squery, int status,
173                        unsigned char *abuf, int alen)
174 {
175   squery->callback(squery->arg, status, abuf, alen);
176   free(squery->name);
177   free(squery);
178 }
179
180 /* Concatenate two domains. */
181 static int cat_domain(const char *name, const char *domain, char **s)
182 {
183   int nlen = strlen(name), dlen = strlen(domain);
184
185   *s = malloc(nlen + 1 + dlen + 1);
186   if (!*s)
187     return ARES_ENOMEM;
188   memcpy(*s, name, nlen);
189   (*s)[nlen] = '.';
190   memcpy(*s + nlen + 1, domain, dlen);
191   (*s)[nlen + 1 + dlen] = 0;
192   return ARES_SUCCESS;
193 }
194
195 /* Determine if this name only yields one query.  If it does, set *s to
196  * the string we should query, in an allocated buffer.  If not, set *s
197  * to NULL.
198  */
199 static int single_domain(ares_channel channel, const char *name, char **s)
200 {
201   int len = strlen(name);
202   const char *hostaliases;
203   FILE *fp;
204   char *line = NULL;
205   int linesize, status;
206   const char *p, *q;
207
208   /* If the name contains a trailing dot, then the single query is the name
209    * sans the trailing dot.
210    */
211   if (name[len - 1] == '.')
212     {
213       *s = strdup(name);
214       return (*s) ? ARES_SUCCESS : ARES_ENOMEM;
215     }
216
217   if (!(channel->flags & ARES_FLAG_NOALIASES) && !strchr(name, '.'))
218     {
219       /* The name might be a host alias. */
220       hostaliases = getenv("HOSTALIASES");
221       if (hostaliases)
222         {
223           fp = fopen(hostaliases, "r");
224           if (fp)
225             {
226               while ((status = ares__read_line(fp, &line, &linesize))
227                      == ARES_SUCCESS)
228                 {
229                   if (strncasecmp(line, name, len) != 0 ||
230                       !isspace((unsigned char)line[len]))
231                     continue;
232                   p = line + len;
233                   while (isspace((unsigned char)*p))
234                     p++;
235                   if (*p)
236                     {
237                       q = p + 1;
238                       while (*q && !isspace((unsigned char)*q))
239                         q++;
240                       *s = malloc(q - p + 1);
241                       if (*s)
242                         {
243                           memcpy(*s, p, q - p);
244                           (*s)[q - p] = 0;
245                         }
246                       free(line);
247                       fclose(fp);
248                       return (*s) ? ARES_SUCCESS : ARES_ENOMEM;
249                     }
250                 }
251               free(line);
252               fclose(fp);
253               if (status != ARES_SUCCESS)
254                 return status;
255             }
256         }
257     }
258
259   if (channel->flags & ARES_FLAG_NOSEARCH || channel->ndomains == 0)
260     {
261       /* No domain search to do; just try the name as-is. */
262       *s = strdup(name);
263       return (*s) ? ARES_SUCCESS : ARES_ENOMEM;
264     }
265
266   *s = NULL;
267   return ARES_SUCCESS;
268 }