Add nsswitch support.
[dragonfly.git] / lib / libc / net / gethostnamadr.c
1 /*-
2  * Copyright (c) 1994, Garrett Wollman
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS ``AS IS'' AND
14  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
17  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23  * SUCH DAMAGE.
24  *
25  * $FreeBSD: src/lib/libc/net/gethostnamadr.c,v 1.15.2.2 2001/03/05 10:40:42 obrien Exp $
26  * $DragonFly: src/lib/libc/net/gethostnamadr.c,v 1.6 2007/12/29 22:55:29 matthias Exp $
27  */
28
29 #include <sys/param.h>
30 #include <sys/socket.h>
31 #include <netinet/in.h>
32 #include <arpa/inet.h>
33 #include <netdb.h>
34 #include <stdio.h>
35 #include <ctype.h>
36 #include <string.h>
37 #include <stdarg.h>
38 #include <nsswitch.h>
39 #include <arpa/nameser.h>               /* XXX hack for _res */
40 #include <resolv.h>                     /* XXX hack for _res */
41 #ifdef NS_CACHING
42 #include "nscache.h"
43 #endif
44
45 extern int _ht_gethostbyname(void *, void *, va_list);
46 extern int _dns_gethostbyname(void *, void *, va_list);
47 extern int _nis_gethostbyname(void *, void *, va_list);
48 extern int _ht_gethostbyaddr(void *, void *, va_list);
49 extern int _dns_gethostbyaddr(void *, void *, va_list);
50 extern int _nis_gethostbyaddr(void *, void *, va_list);
51
52 /* Host lookup order if nsswitch.conf is broken or nonexistant */
53 static const ns_src default_src[] = {
54         { NSSRC_FILES, NS_SUCCESS },
55         { NSSRC_DNS, NS_SUCCESS },
56         { 0 }
57 };
58 #ifdef NS_CACHING
59 static int host_id_func(char *, size_t *, va_list, void *);
60 static int host_marshal_func(char *, size_t *, void *, va_list, void *);
61 static int host_unmarshal_func(char *, size_t, void *, va_list, void *);
62 #endif
63
64 struct hostent *
65 gethostbyname(const char *name)
66 {
67         struct hostent *hp;
68
69         if ((_res.options & RES_INIT) == 0 && res_init() == -1) {
70                 h_errno = NETDB_INTERNAL;
71                 return (NULL);
72         }
73         if (_res.options & RES_USE_INET6) {             /* XXX */
74                 hp = gethostbyname2(name, AF_INET6);    /* XXX */
75                 if (hp)                                 /* XXX */
76                         return (hp);                    /* XXX */
77         }                                               /* XXX */
78         return (gethostbyname2(name, AF_INET));
79 }
80
81 struct hostent *
82 gethostbyname2(const char *name, int type)
83 {
84         struct hostent *hp = 0;
85         int rval;
86
87 #ifdef NS_CACHING
88         static const nss_cache_info cache_info =
89                 NS_COMMON_CACHE_INFO_INITIALIZER(
90                 hosts, (void *)nss_lt_name,
91                 host_id_func, host_marshal_func, host_unmarshal_func);
92 #endif
93         static const ns_dtab dtab[] = {
94                 NS_FILES_CB(_ht_gethostbyname, NULL)
95                 { NSSRC_DNS, _dns_gethostbyname, NULL },
96                 NS_NIS_CB(_nis_gethostbyname, NULL) /* force -DHESIOD */
97 #ifdef NS_CACHING
98                 NS_CACHE_CB(&cache_info)
99 #endif
100                 { 0 }
101         };
102
103         rval = nsdispatch((void *)&hp, dtab, NSDB_HOSTS, "gethostbyname",
104                           default_src, name, type);
105
106         if (rval != NS_SUCCESS)
107                 return NULL;
108         else
109                 return hp;
110 }
111
112 struct hostent *
113 gethostbyaddr(const void *addr, socklen_t len, int type)
114 {
115         struct hostent *hp = 0;
116         int rval;
117
118 #ifdef NS_CACHING
119         static const nss_cache_info cache_info =
120                 NS_COMMON_CACHE_INFO_INITIALIZER(
121                 hosts, (void *)nss_lt_id,
122                 host_id_func, host_marshal_func, host_unmarshal_func);
123 #endif
124         static const ns_dtab dtab[] = {
125                 NS_FILES_CB(_ht_gethostbyaddr, NULL)
126                 { NSSRC_DNS, _dns_gethostbyaddr, NULL },
127                 NS_NIS_CB(_nis_gethostbyaddr, NULL) /* force -DHESIOD */
128 #ifdef NS_CACHING
129                 NS_CACHE_CB(&cache_info)
130 #endif
131                 { 0 }
132         };
133
134         rval = nsdispatch((void *)&hp, dtab, NSDB_HOSTS, "gethostbyaddr",
135                           default_src, addr, len, type);
136
137         if (rval != NS_SUCCESS)
138                 return NULL;
139         else
140                 return hp;
141 }
142
143 #ifdef NS_CACHING
144 static int
145 host_id_func(char *buffer, size_t *buffer_size, va_list ap, void *cache_mdata)
146 {
147         res_state statp;
148         u_long res_options;
149
150         const int op_id = 1;
151         char *str;
152         int len, type;
153
154         size_t desired_size, size;
155         enum nss_lookup_type lookup_type;
156         char *p;
157         int res = NS_UNAVAIL;
158
159         statp = __res_state();
160         res_options = statp->options & (RES_RECURSE | RES_DEFNAMES |
161             RES_DNSRCH | RES_NOALIASES | RES_USE_INET6);
162
163         lookup_type = (enum nss_lookup_type)cache_mdata;
164         switch (lookup_type) {
165         case nss_lt_name:
166                 str = va_arg(ap, char *);
167                 type = va_arg(ap, int);
168
169                 size = strlen(str);
170                 desired_size = sizeof(res_options) + sizeof(int) +
171                     sizeof(enum nss_lookup_type) + sizeof(int) + size + 1;
172
173                 if (desired_size > *buffer_size) {
174                         res = NS_RETURN;
175                         goto fin;
176                 }
177
178                 p = buffer;
179
180                 memcpy(p, &res_options, sizeof(res_options));
181                 p += sizeof(res_options);
182
183                 memcpy(p, &op_id, sizeof(int));
184                 p += sizeof(int);
185
186                 memcpy(p, &lookup_type, sizeof(enum nss_lookup_type));
187                 p += sizeof(int);
188
189                 memcpy(p, &type, sizeof(int));
190                 p += sizeof(int);
191
192                 memcpy(p, str, size + 1);
193
194                 res = NS_SUCCESS;
195                 break;
196         case nss_lt_id:
197                 str = va_arg(ap, char *);
198                 len = va_arg(ap, int);
199                 type = va_arg(ap, int);
200
201                 desired_size = sizeof(res_options) + sizeof(int) +
202                     sizeof(enum nss_lookup_type) + sizeof(int) * 2 + len;
203
204                 if (desired_size > *buffer_size) {
205                         res = NS_RETURN;
206                         goto fin;
207                 }
208
209                 p = buffer;
210                 memcpy(p, &res_options, sizeof(res_options));
211                 p += sizeof(res_options);
212
213                 memcpy(p, &op_id, sizeof(int));
214                 p += sizeof(int);
215
216                 memcpy(p, &lookup_type, sizeof(enum nss_lookup_type));
217                 p += sizeof(int);
218
219                 memcpy(p, &type, sizeof(int));
220                 p += sizeof(int);
221
222                 memcpy(p, &len, sizeof(int));
223                 p += sizeof(int);
224
225                 memcpy(p, str, len);
226
227                 res = NS_SUCCESS;
228                 break;
229         default:
230                 /* should be unreachable */
231                 return (NS_UNAVAIL);
232         }
233
234 fin:
235         *buffer_size = desired_size;
236         return (res);
237 }
238
239 static int
240 host_marshal_func(char *buffer, size_t *buffer_size, void *retval, va_list ap,
241     void *cache_mdata)
242 {
243         char *str;
244         int len, type;
245         struct hostent *ht;
246
247         struct hostent new_ht;
248         size_t desired_size, aliases_size, addr_size, size;
249         char *p, **iter;
250
251         switch ((enum nss_lookup_type)cache_mdata) {
252         case nss_lt_name:
253                 str = va_arg(ap, char *);
254                 type = va_arg(ap, int);
255                 break;
256         case nss_lt_id:
257                 str = va_arg(ap, char *);
258                 len = va_arg(ap, int);
259                 type = va_arg(ap, int);
260                 break;
261         default:
262                 /* should be unreachable */
263                 return (NS_UNAVAIL);
264         }
265         ht = va_arg(ap, struct hostent *);
266
267         desired_size = _ALIGNBYTES + sizeof(struct hostent) + sizeof(char *);
268         if (ht->h_name != NULL)
269                 desired_size += strlen(ht->h_name) + 1;
270
271         if (ht->h_aliases != NULL) {
272                 aliases_size = 0;
273                 for (iter = ht->h_aliases; *iter; ++iter) {
274                         desired_size += strlen(*iter) + 1;
275                         ++aliases_size;
276                 }
277
278                 desired_size += _ALIGNBYTES +
279                     (aliases_size + 1) * sizeof(char *);
280         }
281
282         if (ht->h_addr_list != NULL) {
283                 addr_size = 0;
284                 for (iter = ht->h_addr_list; *iter; ++iter)
285                         ++addr_size;
286
287                 desired_size += addr_size * _ALIGN(ht->h_length);
288                 desired_size += _ALIGNBYTES + (addr_size + 1) * sizeof(char *);
289         }
290
291         if (desired_size > *buffer_size) {
292                 /* this assignment is here for future use */
293                 *buffer_size = desired_size;
294                 return (NS_RETURN);
295         }
296
297         memcpy(&new_ht, ht, sizeof(struct hostent));
298         memset(buffer, 0, desired_size);
299
300         *buffer_size = desired_size;
301         p = buffer + sizeof(struct hostent) + sizeof(char *);
302         memcpy(buffer + sizeof(struct hostent), &p, sizeof(char *));
303         p = (char *)_ALIGN(p);
304
305         if (new_ht.h_name != NULL) {
306                 size = strlen(new_ht.h_name);
307                 memcpy(p, new_ht.h_name, size);
308                 new_ht.h_name = p;
309                 p += size + 1;
310         }
311
312         if (new_ht.h_aliases != NULL) {
313                 p = (char *)_ALIGN(p);
314                 memcpy(p, new_ht.h_aliases, sizeof(char *) * aliases_size);
315                 new_ht.h_aliases = (char **)p;
316                 p += sizeof(char *) * (aliases_size + 1);
317
318                 for (iter = new_ht.h_aliases; *iter; ++iter) {
319                         size = strlen(*iter);
320                         memcpy(p, *iter, size);
321                         *iter = p;
322                         p += size + 1;
323                 }
324         }
325
326         if (new_ht.h_addr_list != NULL) {
327                 p = (char *)_ALIGN(p);
328                 memcpy(p, new_ht.h_addr_list, sizeof(char *) * addr_size);
329                 new_ht.h_addr_list = (char **)p;
330                 p += sizeof(char *) * (addr_size + 1);
331
332                 size = _ALIGN(new_ht.h_length);
333                 for (iter = new_ht.h_addr_list; *iter; ++iter) {
334                         memcpy(p, *iter, size);
335                         *iter = p;
336                         p += size + 1;
337                 }
338         }
339         memcpy(buffer, &new_ht, sizeof(struct hostent));
340         return (NS_SUCCESS);
341 }
342
343 static int
344 host_unmarshal_func(char *buffer, size_t buffer_size, void *retval, va_list ap,
345     void *cache_mdata)
346 {
347         char *str;
348         int len, type;
349         struct hostent *ht;
350
351         char *p;
352         char **iter;
353         char *orig_buf;
354         size_t orig_buf_size;
355
356         switch ((enum nss_lookup_type)cache_mdata) {
357         case nss_lt_name:
358                 str = va_arg(ap, char *);
359                 type = va_arg(ap, int);
360                 break;
361         case nss_lt_id:
362                 str = va_arg(ap, char *);
363                 len = va_arg(ap, int);
364                 type = va_arg(ap, int);
365                 break;
366         default:
367                 /* should be unreachable */
368                 return (NS_UNAVAIL);
369         }
370
371         ht = va_arg(ap, struct hostent *);
372         orig_buf = va_arg(ap, char *);
373         orig_buf_size = va_arg(ap, size_t);
374
375         if (orig_buf_size <
376             buffer_size - sizeof(struct hostent) - sizeof(char *)) {
377                 errno = ERANGE;
378                 return (NS_RETURN);
379         }
380
381         memcpy(ht, buffer, sizeof(struct hostent));
382         memcpy(&p, buffer + sizeof(struct hostent), sizeof(char *));
383
384         orig_buf = (char *)_ALIGN(orig_buf);
385         memcpy(orig_buf, buffer + sizeof(struct hostent) + sizeof(char *) +
386             _ALIGN(p) - (size_t)p,
387             buffer_size - sizeof(struct hostent) - sizeof(char *) -
388             _ALIGN(p) + (size_t)p);
389         p = (char *)_ALIGN(p);
390
391         NS_APPLY_OFFSET(ht->h_name, orig_buf, p, char *);
392         if (ht->h_aliases != NULL) {
393                 NS_APPLY_OFFSET(ht->h_aliases, orig_buf, p, char **);
394
395                 for (iter = ht->h_aliases; *iter; ++iter)
396                         NS_APPLY_OFFSET(*iter, orig_buf, p, char *);
397         }
398
399         if (ht->h_addr_list != NULL) {
400                 NS_APPLY_OFFSET(ht->h_addr_list, orig_buf, p, char **);
401
402                 for (iter = ht->h_addr_list; *iter; ++iter)
403                         NS_APPLY_OFFSET(*iter, orig_buf, p, char *);
404         }
405
406         *((struct hostent **)retval) = ht;
407         return (NS_SUCCESS);
408 }
409 #endif /* NS_CACHING */
410
411 void
412 sethostent(int stayopen)
413 {
414         _sethosthtent(stayopen);
415         _sethostdnsent(stayopen);
416 }
417
418 void
419 endhostent(void)
420 {
421         _endhosthtent();
422         _endhostdnsent();
423 }