]> asedeno.scripts.mit.edu Git - 1ts-debian.git/blob - libares/ares_gethostbyname.c
need automake as a build-dep, even though we don't use most of it
[1ts-debian.git] / libares / ares_gethostbyname.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_gethostbyname.c,v 1.7 1999/10/23 19:28:13 danw Exp $";
17
18 #include <sys/types.h>
19 #include <sys/socket.h>
20 #include <netinet/in.h>
21 #include <arpa/inet.h>
22 #include <arpa/nameser.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <ctype.h>
27 #include <netdb.h>
28 #include "ares.h"
29 #include "ares_private.h"
30
31 struct host_query {
32   /* Arguments passed to ares_gethostbyname() */
33   ares_channel channel;
34   char *name;
35   ares_host_callback callback;
36   void *arg;
37
38   const char *remaining_lookups;
39 };
40
41 static void next_lookup(struct host_query *hquery);
42 static void host_callback(void *arg, int status, unsigned char *abuf,
43                           int alen);
44 static void end_hquery(struct host_query *hquery, int status,
45                        struct hostent *host);
46 static int fake_hostent(const char *name, ares_host_callback callback,
47                         void *arg);
48 static int file_lookup(const char *name, struct hostent **host);
49 static void sort_addresses(struct hostent *host, struct apattern *sortlist,
50                            int nsort);
51 static int get_address_index(struct in_addr *addr, struct apattern *sortlist,
52                              int nsort);
53
54 void ares_gethostbyname(ares_channel channel, const char *name, int family,
55                         ares_host_callback callback, void *arg)
56 {
57   struct host_query *hquery;
58
59   /* Right now we only know how to look up Internet addresses. */
60   if (family != AF_INET)
61     {
62       callback(arg, ARES_ENOTIMP, NULL);
63       return;
64     }
65
66   if (fake_hostent(name, callback, arg))
67     return;
68
69   /* Allocate and fill in the host query structure. */
70   hquery = malloc(sizeof(struct host_query));
71   if (!hquery)
72     {
73       callback(arg, ARES_ENOMEM, NULL);
74       return;
75     }
76   hquery->channel = channel;
77   hquery->name = strdup(name);
78   if (!hquery->name)
79     {
80       free(hquery);
81       callback(arg, ARES_ENOMEM, NULL);
82       return;
83     }
84   hquery->callback = callback;
85   hquery->arg = arg;
86   hquery->remaining_lookups = channel->lookups;
87
88   /* Start performing lookups according to channel->lookups. */
89   next_lookup(hquery);
90 }
91
92 static void next_lookup(struct host_query *hquery)
93 {
94   int status;
95   const char *p;
96   struct hostent *host;
97
98   for (p = hquery->remaining_lookups; *p; p++)
99     {
100       switch (*p)
101         {
102         case 'b':
103           /* DNS lookup */
104           hquery->remaining_lookups = p + 1;
105           ares_search(hquery->channel, hquery->name, C_IN, T_A, host_callback,
106                       hquery);
107           return;
108
109         case 'f':
110           /* Host file lookup */
111           status = file_lookup(hquery->name, &host);
112           if (status != ARES_ENOTFOUND)
113             {
114               end_hquery(hquery, status, host);
115               return;
116             }
117           break;
118         }
119     }
120   end_hquery(hquery, ARES_ENOTFOUND, NULL);
121 }
122
123 static void host_callback(void *arg, int status, unsigned char *abuf, int alen)
124 {
125   struct host_query *hquery = (struct host_query *) arg;
126   ares_channel channel = hquery->channel;
127   struct hostent *host;
128
129   if (status == ARES_SUCCESS)
130     {
131       status = ares_parse_a_reply(abuf, alen, &host);
132       if (host && channel->nsort)
133         sort_addresses(host, channel->sortlist, channel->nsort);
134       end_hquery(hquery, status, host);
135     }
136   else if (status == ARES_EDESTRUCTION)
137     end_hquery(hquery, status, NULL);
138   else
139     next_lookup(hquery);
140 }
141
142 static void end_hquery(struct host_query *hquery, int status,
143                        struct hostent *host)
144 {
145   hquery->callback(hquery->arg, status, host);
146   if (host)
147     ares_free_hostent(host);
148   free(hquery->name);
149   free(hquery);
150 }
151
152 /* If the name looks like an IP address, fake up a host entry, end the
153  * query immediately, and return true.  Otherwise return false.
154  */
155 static int fake_hostent(const char *name, ares_host_callback callback,
156                         void *arg)
157 {
158   struct in_addr addr;
159   struct hostent hostent;
160   const char *p;
161   char *aliases[1] = { NULL };
162   char *addrs[2];
163
164   /* It only looks like an IP address if it's all numbers and dots. */
165   for (p = name; *p; p++)
166     {
167       if (!isdigit((unsigned char)*p) && *p != '.')
168         return 0;
169     }
170
171   /* It also only looks like an IP address if it's non-zero-length and
172    * doesn't end with a dot.
173    */
174   if (p == name || *(p - 1) == '.')
175     return 0;
176
177   /* It looks like an IP address.  Figure out what IP address it is. */
178   addr.s_addr = inet_addr(name);
179   if (addr.s_addr == INADDR_NONE)
180     {
181       callback(arg, ARES_EBADNAME, NULL);
182       return 1;
183     }
184
185   /* Duplicate the name, to avoid a constness violation. */
186   hostent.h_name = strdup(name);
187   if (!hostent.h_name)
188     {
189       callback(arg, ARES_ENOMEM, NULL);
190       return 1;
191     }
192
193   /* Fill in the rest of the host structure and terminate the query. */
194   addrs[0] = (char *) &addr;
195   addrs[1] = NULL;
196   hostent.h_aliases = aliases;
197   hostent.h_addrtype = AF_INET;
198   hostent.h_length = sizeof(struct in_addr);
199   hostent.h_addr_list = addrs;
200   callback(arg, ARES_SUCCESS, &hostent);
201
202   free(hostent.h_name);
203   return 1;
204 }
205
206 static int file_lookup(const char *name, struct hostent **host)
207 {
208   FILE *fp;
209   char **alias;
210   int status;
211
212   fp = fopen(PATH_HOSTS, "r");
213   if (!fp)
214     return ARES_ENOTFOUND;
215
216   while ((status = ares__get_hostent(fp, host)) == ARES_SUCCESS)
217     {
218       if (strcasecmp((*host)->h_name, name) == 0)
219         break;
220       for (alias = (*host)->h_aliases; *alias; alias++)
221         {
222           if (strcasecmp(*alias, name) == 0)
223             break;
224         }
225       if (*alias)
226         break;
227       ares_free_hostent(*host);
228     }
229   fclose(fp);
230   if (status == ARES_EOF)
231     status = ARES_ENOTFOUND;
232   if (status != ARES_SUCCESS)
233     *host = NULL;
234   return status;
235 }
236
237 static void sort_addresses(struct hostent *host, struct apattern *sortlist,
238                            int nsort)
239 {
240   struct in_addr a1, a2;
241   int i1, i2, ind1, ind2;
242
243   /* This is a simple insertion sort, not optimized at all.  i1 walks
244    * through the address list, with the loop invariant that everything
245    * to the left of i1 is sorted.  In the loop body, the value at i1 is moved
246    * back through the list (via i2) until it is in sorted order.
247    */
248   for (i1 = 0; host->h_addr_list[i1]; i1++)
249     {
250       memcpy(&a1, host->h_addr_list[i1], sizeof(struct in_addr));
251       ind1 = get_address_index(&a1, sortlist, nsort);
252       for (i2 = i1 - 1; i2 >= 0; i2--)
253         {
254           memcpy(&a2, host->h_addr_list[i2], sizeof(struct in_addr));
255           ind2 = get_address_index(&a2, sortlist, nsort);
256           if (ind2 <= ind1)
257             break;
258           memcpy(host->h_addr_list[i2 + 1], &a2, sizeof(struct in_addr));
259         }
260       memcpy(host->h_addr_list[i2 + 1], &a1, sizeof(struct in_addr));
261     }
262 }
263
264 /* Find the first entry in sortlist which matches addr.  Return nsort
265  * if none of them match.
266  */
267 static int get_address_index(struct in_addr *addr, struct apattern *sortlist,
268                              int nsort)
269 {
270   int i;
271
272   for (i = 0; i < nsort; i++)
273     {
274       if ((addr->s_addr & sortlist[i].mask.s_addr) == sortlist[i].addr.s_addr)
275         break;
276     }
277   return i;
278 }