Merge from vendor branch LESS:
[dragonfly.git] / contrib / bind-9.3 / lib / bind / irs / nis_ho.c
1 /*
2  * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
3  * Copyright (c) 1996,1999 by Internet Software Consortium.
4  *
5  * Permission to use, copy, modify, and distribute this software for any
6  * purpose with or without fee is hereby granted, provided that the above
7  * copyright notice and this permission notice appear in all copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
10  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11  * MERCHANTABILITY AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR
12  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
15  * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16  */
17
18 #if defined(LIBC_SCCS) && !defined(lint)
19 static const char rcsid[] = "$Id: nis_ho.c,v 1.2.2.1.4.1 2004/03/09 08:33:38 marka Exp $";
20 #endif /* LIBC_SCCS and not lint */
21
22 /* Imports */
23
24 #include "port_before.h"
25
26 #ifndef WANT_IRS_NIS
27 static int __bind_irs_nis_unneeded;
28 #else
29
30 #include <sys/types.h>
31 #include <sys/param.h>
32 #include <sys/socket.h>
33 #include <netinet/in.h>
34 #include <arpa/inet.h>
35 #include <arpa/nameser.h>
36 #ifdef T_NULL
37 #undef T_NULL                   /* Silence re-definition warning of T_NULL. */
38 #endif
39 #include <rpc/rpc.h>
40 #include <rpc/xdr.h>
41 #include <rpcsvc/yp_prot.h>
42 #include <rpcsvc/ypclnt.h>
43
44 #include <ctype.h>
45 #include <errno.h>
46 #include <stdlib.h>
47 #include <netdb.h>
48 #include <resolv.h>
49 #include <stdio.h>
50 #include <string.h>
51
52 #include <isc/memcluster.h>
53 #include <irs.h>
54
55 #include "port_after.h"
56
57 #include "irs_p.h"
58 #include "nis_p.h"
59
60 /* Definitions */
61
62 #define MAXALIASES      35
63 #define MAXADDRS        35
64
65 #if PACKETSZ > 1024
66 #define MAXPACKET       PACKETSZ
67 #else
68 #define MAXPACKET       1024
69 #endif
70
71 struct pvt {
72         int             needrewind;
73         char *          nis_domain;
74         char *          curkey_data;
75         int             curkey_len;
76         char *          curval_data;
77         int             curval_len;
78         struct hostent  host;
79         char *          h_addr_ptrs[MAXADDRS + 1];
80         char *          host_aliases[MAXALIASES + 1];
81         char            hostbuf[8*1024];
82         u_char          host_addr[16];  /* IPv4 or IPv6 */
83         struct __res_state  *res;
84         void            (*free_res)(void *);
85 };
86
87 enum do_what { do_none = 0x0, do_key = 0x1, do_val = 0x2, do_all = 0x3 };
88
89 static const u_char mapped[] = { 0,0, 0,0, 0,0, 0,0, 0,0, 0xff,0xff };
90 static const u_char tunnelled[] = { 0,0, 0,0, 0,0, 0,0, 0,0, 0,0 };
91 static /*const*/ char hosts_byname[] = "hosts.byname";
92 static /*const*/ char hosts_byaddr[] = "hosts.byaddr";
93 static /*const*/ char ipnode_byname[] = "ipnode.byname";
94 static /*const*/ char ipnode_byaddr[] = "ipnode.byaddr";
95 static /*const*/ char yp_multi[] = "YP_MULTI_";
96
97 /* Forwards */
98
99 static void             ho_close(struct irs_ho *this);
100 static struct hostent * ho_byname(struct irs_ho *this, const char *name);
101 static struct hostent * ho_byname2(struct irs_ho *this, const char *name,
102                                         int af);
103 static struct hostent * ho_byaddr(struct irs_ho *this, const void *addr,
104                                        int len, int af);
105 static struct hostent * ho_next(struct irs_ho *this);
106 static void             ho_rewind(struct irs_ho *this);
107 static void             ho_minimize(struct irs_ho *this);
108 static struct __res_state * ho_res_get(struct irs_ho *this);
109 static void             ho_res_set(struct irs_ho *this,
110                                    struct __res_state *res,
111                                    void (*free_res)(void *));
112 static struct addrinfo * ho_addrinfo(struct irs_ho *this, const char *name,
113                                      const struct addrinfo *pai);
114
115 static struct hostent * makehostent(struct irs_ho *this);
116 static void             nisfree(struct pvt *, enum do_what);
117 static int              init(struct irs_ho *this);
118
119 /* Public */
120
121 struct irs_ho *
122 irs_nis_ho(struct irs_acc *this) {
123         struct irs_ho *ho;
124         struct pvt *pvt;
125
126         if (!(pvt = memget(sizeof *pvt))) {
127                 errno = ENOMEM;
128                 return (NULL);
129         }
130         memset(pvt, 0, sizeof *pvt);
131         if (!(ho = memget(sizeof *ho))) {
132                 memput(pvt, sizeof *pvt);
133                 errno = ENOMEM;
134                 return (NULL);
135         }
136         memset(ho, 0x5e, sizeof *ho);
137         pvt->needrewind = 1;
138         pvt->nis_domain = ((struct nis_p *)this->private)->domain;
139         ho->private = pvt;
140         ho->close = ho_close;
141         ho->byname = ho_byname;
142         ho->byname2 = ho_byname2;
143         ho->byaddr = ho_byaddr;
144         ho->next = ho_next;
145         ho->rewind = ho_rewind;
146         ho->minimize = ho_minimize;
147         ho->res_set = ho_res_set;
148         ho->res_get = ho_res_get;
149         ho->addrinfo = ho_addrinfo;
150         return (ho);
151 }
152
153 /* Methods */
154
155 static void
156 ho_close(struct irs_ho *this) {
157         struct pvt *pvt = (struct pvt *)this->private;
158
159         ho_minimize(this);
160         nisfree(pvt, do_all);
161         if (pvt->res && pvt->free_res)
162                 (*pvt->free_res)(pvt->res);
163         memput(pvt, sizeof *pvt);
164         memput(this, sizeof *this);
165 }
166
167 static struct hostent *
168 ho_byname(struct irs_ho *this, const char *name) {
169         struct pvt *pvt = (struct pvt *)this->private;
170         struct hostent *hp;
171
172         if (init(this) == -1)
173                 return (NULL);
174
175         if (pvt->res->options & RES_USE_INET6) {
176                 hp = ho_byname2(this, name, AF_INET6);
177                 if (hp)
178                         return (hp);
179         }
180         return (ho_byname2(this, name, AF_INET));
181 }
182
183 static struct hostent *
184 ho_byname2(struct irs_ho *this, const char *name, int af) {
185         struct pvt *pvt = (struct pvt *)this->private;
186         int r;
187         char *tmp;
188
189         UNUSED(af);
190         
191         if (init(this) == -1)
192                 return (NULL);
193
194         nisfree(pvt, do_val);
195
196         strcpy(pvt->hostbuf, yp_multi);
197         strncat(pvt->hostbuf, name, sizeof(pvt->hostbuf) - sizeof(yp_multi));
198         pvt->hostbuf[sizeof(pvt->hostbuf) - 1] = '\0';
199         for (r = sizeof(yp_multi) - 1; pvt->hostbuf[r] != '\0'; r++)
200                 if (isupper((unsigned char)pvt->hostbuf[r]))
201                         tolower(pvt->hostbuf[r]);
202
203         tmp = pvt->hostbuf;
204         r = yp_match(pvt->nis_domain, ipnode_byname, tmp,
205                      strlen(tmp), &pvt->curval_data, &pvt->curval_len);
206         if (r != 0) {
207                 tmp = pvt->hostbuf + sizeof(yp_multi) - 1;
208                 r = yp_match(pvt->nis_domain, ipnode_byname, tmp,
209                              strlen(tmp), &pvt->curval_data, &pvt->curval_len);
210         }
211         if (r != 0) {
212                 tmp = pvt->hostbuf;
213                 r = yp_match(pvt->nis_domain, hosts_byname, tmp,
214                              strlen(tmp), &pvt->curval_data, &pvt->curval_len);
215         }
216         if (r != 0) {
217                 tmp = pvt->hostbuf + sizeof(yp_multi) - 1;
218                 r = yp_match(pvt->nis_domain, hosts_byname, tmp,
219                              strlen(tmp), &pvt->curval_data, &pvt->curval_len);
220         }
221         if (r != 0) {
222                 RES_SET_H_ERRNO(pvt->res, HOST_NOT_FOUND);
223                 return (NULL);
224         }
225         return (makehostent(this));
226 }
227
228 static struct hostent *
229 ho_byaddr(struct irs_ho *this, const void *addr, int len, int af) {
230         struct pvt *pvt = (struct pvt *)this->private;
231         char tmp[sizeof "ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255"];
232         const u_char *uaddr = addr;
233         int r;
234         
235         if (init(this) == -1)
236                 return (NULL);
237
238         if (af == AF_INET6 && len == IN6ADDRSZ &&
239             (!memcmp(uaddr, mapped, sizeof mapped) ||
240              !memcmp(uaddr, tunnelled, sizeof tunnelled))) {
241                 /* Unmap. */
242                 addr = (const u_char *)addr + sizeof mapped;
243                 uaddr += sizeof mapped;
244                 af = AF_INET;
245                 len = INADDRSZ;
246         }
247         if (inet_ntop(af, uaddr, tmp, sizeof tmp) == NULL) {
248                 RES_SET_H_ERRNO(pvt->res, NETDB_INTERNAL);
249                 return (NULL);
250         }
251         nisfree(pvt, do_val);
252         r = yp_match(pvt->nis_domain, ipnode_byaddr, tmp, strlen(tmp),
253                      &pvt->curval_data, &pvt->curval_len);
254         if (r != 0)
255                 r = yp_match(pvt->nis_domain, hosts_byaddr, tmp, strlen(tmp),
256                              &pvt->curval_data, &pvt->curval_len);
257         if (r != 0) {
258                 RES_SET_H_ERRNO(pvt->res, HOST_NOT_FOUND);
259                 return (NULL);
260         }
261         return (makehostent(this));
262 }
263
264 static struct hostent *
265 ho_next(struct irs_ho *this) {
266         struct pvt *pvt = (struct pvt *)this->private;
267         struct hostent *rval;
268         int r;
269
270         if (init(this) == -1)
271                 return (NULL);
272
273         do {
274                 if (pvt->needrewind) {
275                         nisfree(pvt, do_all);
276                         r = yp_first(pvt->nis_domain, hosts_byaddr,
277                                      &pvt->curkey_data, &pvt->curkey_len,
278                                      &pvt->curval_data, &pvt->curval_len);
279                         pvt->needrewind = 0;
280                 } else {
281                         char *newkey_data;
282                         int newkey_len;
283
284                         nisfree(pvt, do_val);
285                         r = yp_next(pvt->nis_domain, hosts_byaddr,
286                                     pvt->curkey_data, pvt->curkey_len,
287                                     &newkey_data, &newkey_len,
288                                     &pvt->curval_data, &pvt->curval_len);
289                         nisfree(pvt, do_key);
290                         pvt->curkey_data = newkey_data;
291                         pvt->curkey_len = newkey_len;
292                 }
293                 if (r != 0) {
294                         RES_SET_H_ERRNO(pvt->res, HOST_NOT_FOUND);
295                         return (NULL);
296                 }
297                 rval = makehostent(this);
298         } while (rval == NULL);
299         return (rval);
300 }
301
302 static void
303 ho_rewind(struct irs_ho *this) {
304         struct pvt *pvt = (struct pvt *)this->private;
305
306         pvt->needrewind = 1;
307 }
308
309 static void
310 ho_minimize(struct irs_ho *this) {
311         struct pvt *pvt = (struct pvt *)this->private;
312
313         if (pvt->res)
314                 res_nclose(pvt->res);
315 }
316
317 static struct __res_state *
318 ho_res_get(struct irs_ho *this) {
319         struct pvt *pvt = (struct pvt *)this->private;
320
321         if (!pvt->res) {
322                 struct __res_state *res;
323                 res = (struct __res_state *)malloc(sizeof *res);
324                 if (!res) {
325                         errno = ENOMEM;
326                         return (NULL);
327                 }
328                 memset(res, 0, sizeof *res);
329                 ho_res_set(this, res, free);
330         }
331
332         return (pvt->res);
333 }
334
335 static void
336 ho_res_set(struct irs_ho *this, struct __res_state *res,
337                 void (*free_res)(void *)) {
338         struct pvt *pvt = (struct pvt *)this->private;
339
340         if (pvt->res && pvt->free_res) {
341                 res_nclose(pvt->res);
342                 (*pvt->free_res)(pvt->res);
343         }
344
345         pvt->res = res;
346         pvt->free_res = free_res;
347 }
348
349 struct nis_res_target {
350         struct nis_res_target *next;
351         int family;
352 };
353
354 /* XXX */
355 extern struct addrinfo *hostent2addrinfo __P((struct hostent *,
356                                               const struct addrinfo *pai));
357
358 static struct addrinfo *
359 ho_addrinfo(struct irs_ho *this, const char *name, const struct addrinfo *pai)
360 {
361         struct pvt *pvt = (struct pvt *)this->private;
362         struct hostent *hp;
363         struct nis_res_target q, q2, *p;
364         struct addrinfo sentinel, *cur;
365
366         memset(&q, 0, sizeof(q2));
367         memset(&q2, 0, sizeof(q2));
368         memset(&sentinel, 0, sizeof(sentinel));
369         cur = &sentinel;
370
371         switch(pai->ai_family) {
372         case AF_UNSPEC:         /* INET6 then INET4 */
373                 q.family = AF_INET6;
374                 q.next = &q2;
375                 q2.family = AF_INET;
376                 break;
377         case AF_INET6:
378                 q.family = AF_INET6;
379                 break;
380         case AF_INET:
381                 q.family = AF_INET;
382                 break;
383         default:
384                 RES_SET_H_ERRNO(pvt->res, NO_RECOVERY); /* ??? */
385                 return(NULL);
386         }
387
388         for (p = &q; p; p = p->next) {
389                 struct addrinfo *ai;
390
391                 hp = (*this->byname2)(this, name, p->family);
392                 if (hp == NULL) {
393                         /* byname2 should've set an appropriate error */
394                         continue;
395                 }
396                 if ((hp->h_name == NULL) || (hp->h_name[0] == 0) ||
397                     (hp->h_addr_list[0] == NULL)) {
398                         RES_SET_H_ERRNO(pvt->res, NO_RECOVERY);
399                         continue;
400                 }
401                 ai = hostent2addrinfo(hp, pai);
402                 if (ai) {
403                         cur->ai_next = ai;
404                         while (cur && cur->ai_next)
405                                 cur = cur->ai_next;
406                 }
407         }
408
409         if (sentinel.ai_next == NULL)
410                 RES_SET_H_ERRNO(pvt->res, HOST_NOT_FOUND);
411
412         return(sentinel.ai_next);
413 }
414
415 /* Private */
416
417 /*
418 ipnodes:
419 ::1             localhost
420 127.0.0.1       localhost
421 1.2.3.4         FOO bar
422 1.2.6.4         FOO bar
423 1.2.6.5         host
424
425 ipnodes.byname:
426 YP_MULTI_localhost ::1,127.0.0.1        localhost
427 YP_MULTI_foo 1.2.3.4,1.2.6.4    FOO bar
428 YP_MULTI_bar 1.2.3.4,1.2.6.4    FOO bar
429 host 1.2.6.5    host
430
431 hosts.byname:
432 localhost 127.0.0.1     localhost
433 host 1.2.6.5    host
434 YP_MULTI_foo 1.2.3.4,1.2.6.4    FOO bar
435 YP_MULTI_bar 1.2.3.4,1.2.6.4    FOO bar
436 */
437
438 static struct hostent *
439 makehostent(struct irs_ho *this) {
440         struct pvt *pvt = (struct pvt *)this->private;
441         static const char spaces[] = " \t";
442         char *cp, **q, *p, *comma, *ap;
443         int af = 0, len = 0;
444         int multi = 0;
445         int addr = 0;
446
447         p = pvt->curval_data;
448         if ((cp = strpbrk(p, "#\n")) != NULL)
449                 *cp = '\0';
450         if (!(cp = strpbrk(p, spaces)))
451                 return (NULL);
452         *cp++ = '\0';
453         ap = pvt->hostbuf;
454         do {
455                 if ((comma = strchr(p, ',')) != NULL) {
456                         *comma++ = '\0';
457                         multi = 1;
458                 }
459                 if ((ap + IN6ADDRSZ) > (pvt->hostbuf + sizeof(pvt->hostbuf)))
460                         break;
461                 if ((pvt->res->options & RES_USE_INET6) &&
462                     inet_pton(AF_INET6, p, ap) > 0) {
463                         af = AF_INET6;
464                         len = IN6ADDRSZ;
465                 } else if (inet_pton(AF_INET, p, pvt->host_addr) > 0) {
466                         if (pvt->res->options & RES_USE_INET6) {
467                                 map_v4v6_address((char*)pvt->host_addr, ap);
468                                 af = AF_INET6;
469                                 len = IN6ADDRSZ;
470                         } else {
471                                 af = AF_INET;
472                                 len = INADDRSZ;
473                         }
474                 } else {
475                         if (!multi)
476                                 return (NULL);
477                         continue;
478                 }
479                 if (addr < MAXADDRS) {
480                         pvt->h_addr_ptrs[addr++] = ap;
481                         pvt->h_addr_ptrs[addr] = NULL;
482                         ap += len;
483                 }
484         } while ((p = comma) != NULL);
485         if (ap == pvt->hostbuf)
486                 return (NULL);
487         pvt->host.h_addr_list = pvt->h_addr_ptrs;
488         pvt->host.h_length = len;
489         pvt->host.h_addrtype = af;
490         cp += strspn(cp, spaces);
491         pvt->host.h_name = cp;
492         q = pvt->host.h_aliases = pvt->host_aliases;
493         if ((cp = strpbrk(cp, spaces)) != NULL)
494                 *cp++ = '\0';
495         while (cp && *cp) {
496                 if (*cp == ' ' || *cp == '\t') {
497                         cp++;
498                         continue;
499                 }
500                 if (q < &pvt->host_aliases[MAXALIASES])
501                         *q++ = cp;
502                 if ((cp = strpbrk(cp, spaces)) != NULL)
503                         *cp++ = '\0';
504         }
505         *q = NULL;
506         RES_SET_H_ERRNO(pvt->res, NETDB_SUCCESS);
507         return (&pvt->host);
508 }
509
510 static void
511 nisfree(struct pvt *pvt, enum do_what do_what) {
512         if ((do_what & do_key) && pvt->curkey_data) {
513                 free(pvt->curkey_data);
514                 pvt->curkey_data = NULL;
515         }
516         if ((do_what & do_val) && pvt->curval_data) {
517                 free(pvt->curval_data);
518                 pvt->curval_data = NULL;
519         }
520 }
521
522 static int
523 init(struct irs_ho *this) {
524         struct pvt *pvt = (struct pvt *)this->private;
525         
526         if (!pvt->res && !ho_res_get(this))
527                 return (-1);
528         if (((pvt->res->options & RES_INIT) == 0) &&
529             res_ninit(pvt->res) == -1)
530                 return (-1);
531         return (0);
532 }
533 #endif /*WANT_IRS_NIS*/