Sun Jun 23 19:42:05 1996 Ulrich Drepper <drepper@cygnus.com>
[kopensolaris-gnu/glibc.git] / nss / nss_dns / dns-host.c
1 /* Copyright (C) 1996 Free Software Foundation, Inc.
2 This file is part of the GNU C Library.
3 Extended from original form by Ulrich Drepper <drepper@cygnus.com>, 1996.
4
5 The GNU C Library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Library General Public License as
7 published by the Free Software Foundation; either version 2 of the
8 License, or (at your option) any later version.
9
10 The GNU C Library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13 Library General Public License for more details.
14
15 You should have received a copy of the GNU Library General Public
16 License along with the GNU C Library; see the file COPYING.LIB.  If
17 not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.  */
19
20 /* Parts of this file are plain copies of the file `getnetnamadr.c' from
21    the bind package and it has the following copyright.  */
22
23 /* Copyright (c) 1993 Carlos Leandro and Rui Salgueiro
24  *      Dep. Matematica Universidade de Coimbra, Portugal, Europe
25  *
26  * Permission to use, copy, modify, and distribute this software for any
27  * purpose with or without fee is hereby granted, provided that the above
28  * copyright notice and this permission notice appear in all copies.
29  */
30 /*
31  * Copyright (c) 1983, 1993
32  *      The Regents of the University of California.  All rights reserved.
33  *
34  * Redistribution and use in source and binary forms, with or without
35  * modification, are permitted provided that the following conditions
36  * are met:
37  * 1. Redistributions of source code must retain the above copyright
38  *    notice, this list of conditions and the following disclaimer.
39  * 2. Redistributions in binary form must reproduce the above copyright
40  *    notice, this list of conditions and the following disclaimer in the
41  *    documentation and/or other materials provided with the distribution.
42  * 3. All advertising materials mentioning features or use of this software
43  *    must display the following acknowledgement:
44  *      This product includes software developed by the University of
45  *      California, Berkeley and its contributors.
46  * 4. Neither the name of the University nor the names of its contributors
47  *    may be used to endorse or promote products derived from this software
48  *    without specific prior written permission.
49  *
50  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
51  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
52  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
53  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
54  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
55  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
56  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
57  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
58  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
59  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
60  * SUCH DAMAGE.
61  */
62
63 #include <ctype.h>
64 #include <errno.h>
65 #include <netdb.h>
66 #include <stdio.h>
67 #include <stdlib.h>
68 #include <stddef.h>
69 #include <string.h>
70 #include <sys/syslog.h>
71
72 #include <netinet/in.h>
73 #include <arpa/inet.h>
74 #include <arpa/nameser.h>
75 #include <resolv.h>
76
77 #include "nsswitch.h"
78
79 /* Get implementation for some internal functions.  */
80 #include "../resolv/mapv4v6addr.h"
81 #include "../resolv/mapv4v6hostent.h"
82
83 /* Maximum number of aliases we allow.  */
84 #define MAX_NR_ALIASES  48
85 #define MAX_NR_ADDRS    48
86
87 #if PACKETSZ > 1024
88 # define MAXPACKET      PACKETSZ
89 #else
90 # define MAXPACKET      1024
91 #endif
92
93 static const char AskedForGot[] = "\
94 gethostby*.getanswer: asked for \"%s\", got \"%s\"";
95
96
97 /* We need this time later.  */
98 typedef union querybuf
99 {
100   HEADER hdr;
101   u_char buf[MAXPACKET];
102 } querybuf;
103
104
105 static enum nss_status getanswer_r (const querybuf *answer, int anslen,
106                                     const char *qname, int qtype,
107                                     struct hostent *result,
108                                     char *buffer, int buflen, int *h_errnop);
109
110 enum nss_status
111 _nss_dns_gethostbyname2_r (const char *name, int af, struct hostent *result,
112                            char *buffer, int buflen, int *h_errnop)
113 {
114   struct host_data
115   {
116     char *aliases[MAX_NR_ALIASES];
117     unsigned char host_addr[16];        /* IPv4 or IPv6 */
118     char *h_addr_ptrs[MAX_NR_ADDRS + 1];
119     char linebuffer[0];
120   } *host_data = (struct host_data *) buffer;
121   int linebuflen = buflen - offsetof (struct host_data, linebuffer);
122   querybuf host_buffer;
123   int size, type, n;
124   const char *cp;
125
126   switch (af) {
127   case AF_INET:
128     size = INADDRSZ;
129     type = T_A;
130     break;
131   case AF_INET6:
132     size = IN6ADDRSZ;
133     type = T_AAAA;
134     break;
135   default:
136     *h_errnop = NETDB_INTERNAL;
137     errno = EAFNOSUPPORT;
138     return NSS_STATUS_UNAVAIL;
139   }
140
141   result->h_addrtype = af;
142   result->h_length = size;
143
144   /*
145    * if there aren't any dots, it could be a user-level alias.
146    * this is also done in res_query() since we are not the only
147    * function that looks up host names.
148    */
149   if (strchr (name, '.') == NULL && (cp = __hostalias (name)) != NULL)
150     name = cp;
151
152   /*
153    * disallow names consisting only of digits/dots, unless
154    * they end in a dot.
155    */
156   if (isdigit (name[0]))
157     for (cp = name;; ++cp)
158       {
159         if (*cp == '\0')
160           {
161             char *bp;
162
163             if (*--cp == '.')
164               break;
165             /*
166              * All-numeric, no dot at the end.  Fake up a hostent
167              * as if we'd actually done a lookup.
168              */
169             if (inet_pton (af, name, host_data->host_addr) <= 0)
170               {
171                 *h_errnop = HOST_NOT_FOUND;
172                 return NSS_STATUS_NOTFOUND;
173               }
174
175             bp = __stpncpy (host_data->linebuffer, name, linebuflen);
176             host_data->linebuffer[linebuflen - 1] = '\0';
177             linebuflen -= bp - host_data->linebuffer;
178
179             result->h_name = host_data->linebuffer;
180             result->h_aliases = host_data->aliases;
181             host_data->aliases[0] = NULL;
182             host_data->h_addr_ptrs[0] = (char *) host_data->host_addr;
183             host_data->h_addr_ptrs[1] = NULL;
184             result->h_addr_list = host_data->h_addr_ptrs;
185
186             if (_res.options & RES_USE_INET6)
187               map_v4v6_hostent (result, &bp, &linebuflen);
188             *h_errnop = NETDB_SUCCESS;
189             return NSS_STATUS_SUCCESS;
190           }
191         if (!isdigit (*cp) && *cp != '.')
192           break;
193       }
194
195   n = res_search (name, C_IN, type, host_buffer.buf, sizeof (host_buffer));
196   if (n < 0)
197     return errno == ECONNREFUSED ? NSS_STATUS_UNAVAIL : NSS_STATUS_NOTFOUND;
198
199   return getanswer_r (&host_buffer, n, name, type, result, buffer, buflen,
200                       h_errnop);
201 }
202
203
204 enum nss_status
205 _nss_dns_gethostbyname_r (const char *name, struct hostent *result,
206                           char *buffer, int buflen, int *h_errnop)
207 {
208   enum nss_status status = NSS_STATUS_NOTFOUND;
209
210   if (_res.options & RES_USE_INET6)
211     status = _nss_dns_gethostbyname2_r (name, AF_INET6, result, buffer,
212                                         buflen, h_errnop);
213   if (status == NSS_STATUS_NOTFOUND)
214     status = _nss_dns_gethostbyname2_r (name, AF_INET, result, buffer,
215                                         buflen, h_errnop);
216
217   return status;
218 }
219
220
221 enum nss_status
222 _nss_dns_gethostbyaddr_r (const char *addr, int len, int af,
223                           struct hostent *result, char *buffer, int buflen,
224                           int *h_errnop)
225 {
226   static const u_char mapped[] = { 0,0, 0,0, 0,0, 0,0, 0,0, 0xff,0xff };
227   static const u_char tunnelled[] = { 0,0, 0,0, 0,0, 0,0, 0,0, 0,0 };
228   const u_char *uaddr = (const u_char *)addr;
229   struct host_data
230   {
231     char *aliases[MAX_NR_ALIASES];
232     unsigned char host_addr[16];        /* IPv4 or IPv6 */
233     char *h_addr_ptrs[MAX_NR_ADDRS + 1];
234     char linebuffer[0];
235   } *host_data = (struct host_data *) buffer;
236   querybuf host_buffer;
237   char qbuf[MAXDNAME+1], *qp;
238   int size, n, status;
239
240   if (af == AF_INET6 && len == IN6ADDRSZ &&
241       (bcmp (uaddr, mapped, sizeof mapped) == 0
242        || bcmp (uaddr, tunnelled, sizeof tunnelled) == 0))
243     {
244       /* Unmap. */
245       addr += sizeof mapped;
246       uaddr += sizeof mapped;
247       af = AF_INET;
248       len = INADDRSZ;
249     }
250
251   switch (af)
252     {
253     case AF_INET:
254       size = INADDRSZ;
255       break;
256     case AF_INET6:
257       size = IN6ADDRSZ;
258       break;
259     default:
260       errno = EAFNOSUPPORT;
261       *h_errnop = NETDB_INTERNAL;
262       return NSS_STATUS_UNAVAIL;
263     }
264   if (size != len)
265     {
266       errno = EAFNOSUPPORT;
267       *h_errnop = NETDB_INTERNAL;
268       return NSS_STATUS_UNAVAIL;
269     }
270
271   switch (af)
272     {
273     case AF_INET:
274       sprintf (qbuf, "%u.%u.%u.%u.in-addr.arpa", (uaddr[3] & 0xff),
275                (uaddr[2] & 0xff), (uaddr[1] & 0xff), (uaddr[0] & 0xff));
276       break;
277     case AF_INET6:
278       qp = qbuf;
279       for (n = IN6ADDRSZ - 1; n >= 0; n--)
280         qp += sprintf (qp, "%x.%x.", uaddr[n] & 0xf, (uaddr[n] >> 4) & 0xf);
281       strcpy(qp, "ip6.int");
282       break;
283     default:
284       /* Cannot happen.  */
285     }
286
287   n = res_query (qbuf, C_IN, T_PTR, (u_char *)host_buffer.buf,
288                  sizeof host_buffer);
289   if (n < 0)
290     return errno == ECONNREFUSED ? NSS_STATUS_UNAVAIL : NSS_STATUS_NOTFOUND;
291
292   status = getanswer_r (&host_buffer, n, qbuf, T_PTR, result, buffer, buflen,
293                         h_errnop);
294   if (status != NSS_STATUS_SUCCESS)
295     return status;
296
297 #ifdef SUNSECURITY
298   This is not implemented because it is not possible to use the current
299   source from bind in a multi-threaded program.
300 #endif
301
302   result->h_addrtype = af;
303   result->h_length = len;
304   bcopy (addr, host_data->host_addr, len);
305   host_data->h_addr_ptrs[0] = (char *) host_data->host_addr;
306   host_data->h_addr_ptrs[1] = NULL;
307   if (af == AF_INET && (_res.options & RES_USE_INET6))
308     {
309       map_v4v6_address ((char *) host_data->host_addr,
310                         (char *) host_data->host_addr);
311       result->h_addrtype = AF_INET6;
312       result->h_length = IN6ADDRSZ;
313     }
314   *h_errnop = NETDB_SUCCESS;
315   return NSS_STATUS_SUCCESS;
316 }
317
318
319 static enum nss_status
320 getanswer_r (const querybuf *answer, int anslen, const char *qname, int qtype,
321              struct hostent *result, char *buffer, int buflen, int *h_errnop)
322 {
323   struct host_data
324   {
325     char *aliases[MAX_NR_ALIASES];
326     unsigned char host_addr[16];        /* IPv4 or IPv6 */
327     char *h_addr_ptrs[MAX_NR_ADDRS + 1];
328     char linebuffer[0];
329   } *host_data = (struct host_data *) buffer;
330   int linebuflen = buflen - offsetof (struct host_data, linebuffer);
331   register const HEADER *hp;
332   const u_char *end_of_message, *cp;
333   int n, ancount, qdcount;
334   int haveanswer, had_error;
335   char *bp, **ap, **hap;
336   char tbuf[MAXDNAME+1];
337   const char *tname;
338   int (*name_ok) __P ((const char *));
339
340   tname = qname;
341   result->h_name = NULL;
342   end_of_message = answer->buf + anslen;
343   switch (qtype)
344     {
345     case T_A:
346     case T_AAAA:
347       name_ok = res_hnok;
348       break;
349     case T_PTR:
350       name_ok = res_dnok;
351       break;
352     default:
353       return NSS_STATUS_UNAVAIL;  /* XXX should be abort(); */
354     }
355
356   /*
357    * find first satisfactory answer
358    */
359   hp = &answer->hdr;
360   bp = host_data->linebuffer;
361   ancount = ntohs (hp->ancount);
362   qdcount = ntohs (hp->qdcount);
363   cp = answer->buf + HFIXEDSZ;
364   if (qdcount != 1)
365     {
366       *h_errnop = NO_RECOVERY;
367       return NSS_STATUS_UNAVAIL;
368     }
369
370   n = dn_expand (answer->buf, end_of_message, cp, bp, linebuflen);
371   if (n < 0 || (*name_ok) (bp) == 0)
372     {
373       *h_errnop = NO_RECOVERY;
374       return NSS_STATUS_UNAVAIL;
375     }
376   cp += n + QFIXEDSZ;
377
378   if (qtype == T_A || qtype == T_AAAA)
379     {
380       /* res_send() has already verified that the query name is the
381        * same as the one we sent; this just gets the expanded name
382        * (i.e., with the succeeding search-domain tacked on).
383        */
384       n = strlen (bp) + 1;             /* for the \0 */
385       result->h_name = bp;
386       bp += n;
387       linebuflen -= n;
388       /* The qname can be abbreviated, but h_name is now absolute. */
389       qname = result->h_name;
390     }
391
392   ap = host_data->aliases;
393   *ap = NULL;
394   result->h_aliases = host_data->aliases;
395   hap = host_data->h_addr_ptrs;
396   *hap = NULL;
397   result->h_addr_list = host_data->h_addr_ptrs;
398   haveanswer = 0;
399   had_error = 0;
400
401   while (ancount-- > 0 && cp < end_of_message && had_error == 0)
402     {
403       int type, class;
404
405       n = dn_expand (answer->buf, end_of_message, cp, bp, linebuflen);
406       if (n < 0 || (*name_ok) (bp) == 0)
407         {
408           ++had_error;
409           continue;
410         }
411       cp += n;                          /* name */
412       type = _getshort (cp);
413       cp += INT16SZ;                    /* type */
414       class = _getshort(cp);
415       cp += INT16SZ + INT32SZ;          /* class, TTL */
416       n = _getshort(cp);
417       cp += INT16SZ;                    /* len */
418       if (class != C_IN)
419         {
420           /* XXX - debug? syslog? */
421           cp += n;
422           continue;                     /* XXX - had_error++ ? */
423         }
424
425       if ((qtype ==T_A || qtype == T_AAAA) && type == T_CNAME)
426         {
427           if (ap >= &host_data->aliases[MAX_NR_ALIASES - 1])
428             continue;
429           n = dn_expand (answer->buf, end_of_message, cp, tbuf, sizeof tbuf);
430           if (n < 0 || (*name_ok) (tbuf) == 0)
431             {
432               ++had_error;
433               continue;
434             }
435           cp += n;
436           /* Store alias.  */
437           *ap++ = bp;
438           n = strlen (bp) + 1;          /* For the \0.  */
439           bp += n;
440           linebuflen -= n;
441           /* Get canonical name.  */
442           n = strlen (tbuf) + 1;        /* For the \0.  */
443           if (n > buflen)
444             {
445               ++had_error;
446               continue;
447             }
448           strcpy (bp, tbuf);
449           result->h_name = bp;
450           bp += n;
451           linebuflen -= n;
452           continue;
453         }
454
455       if (qtype == T_PTR && type == T_CNAME)
456         {
457           n = dn_expand (answer->buf, end_of_message, cp, tbuf, sizeof tbuf);
458           if (n < 0 || res_hnok (tbuf) == 0)
459             {
460               ++had_error;
461               continue;
462             }
463           cp += n;
464           /* Get canonical name. */
465           n = strlen (tbuf) + 1;   /* For the \0.  */
466           if (n > buflen)
467             {
468               ++had_error;
469               continue;
470             }
471           strcpy (bp, tbuf);
472           tname = bp;
473           bp += n;
474           linebuflen -= n;
475           continue;
476         }
477       if (type != qtype)
478         {
479           syslog (LOG_NOTICE | LOG_AUTH,
480                "gethostby*.getanswer: asked for \"%s %s %s\", got type \"%s\"",
481                   qname, p_class (C_IN), p_type (qtype), p_type (type));
482           cp += n;
483           continue;                     /* XXX - had_error++ ? */
484         }
485
486       switch (type)
487         {
488         case T_PTR:
489           if (strcasecmp (tname, bp) != 0)
490             {
491               syslog (LOG_NOTICE | LOG_AUTH, AskedForGot, qname, bp);
492               cp += n;
493               continue;                 /* XXX - had_error++ ? */
494             }
495           n = dn_expand (answer->buf, end_of_message, cp, bp, linebuflen);
496           if (n < 0 || res_hnok (bp) == 0)
497             {
498               ++had_error;
499               break;
500             }
501 #if MULTI_PTRS_ARE_ALIASES
502           cp += n;
503           if (haveanswer == 0)
504             result->h_name = bp;
505           else if (ap < &host_data->aliases[MAXALIASES-1])
506             *ap++ = bp;
507           else
508             n = -1;
509           if (n != -1)
510             {
511               n = strlen (bp) + 1;      /* for the \0 */
512               bp += n;
513               linebuflen -= n;
514             }
515           break;
516 #else
517           result->h_name = bp;
518           if (_res.options & RES_USE_INET6)
519             {
520               n = strlen (bp) + 1;      /* for the \0 */
521               bp += n;
522               linebuflen -= n;
523               map_v4v6_hostent (result, &bp, &linebuflen);
524             }
525           *h_errnop = NETDB_SUCCESS;
526           return NSS_STATUS_SUCCESS;
527 #endif
528         case T_A:
529         case T_AAAA:
530           if (strcasecmp (result->h_name, bp) != 0)
531             {
532               syslog (LOG_NOTICE | LOG_AUTH, AskedForGot, result->h_name, bp);
533               cp += n;
534               continue;                 /* XXX - had_error++ ? */
535             }
536           if (haveanswer)
537             {
538               if (n != result->h_length)
539                 {
540                   cp += n;
541                   continue;
542                 }
543             }
544           else
545             {
546               register int nn;
547
548               result->h_name = bp;
549               nn = strlen (bp) + 1;     /* for the \0 */
550               bp += nn;
551               linebuflen -= nn;
552             }
553
554           bp += sizeof (align) - ((u_long) bp % sizeof (align));
555
556           if (n >= linebuflen)
557             {
558               ++had_error;
559               continue;
560             }
561           if (hap >= &host_data->h_addr_ptrs[MAX_NR_ADDRS-1])
562             {
563               cp += n;
564               continue;
565             }
566           bcopy (cp, *hap++ = bp, n);
567           bp += n;
568           cp += n;
569           linebuflen -= n;
570           break;
571         default:
572           abort ();
573         }
574       if (had_error == 0)
575         ++haveanswer;
576     }
577
578   if (haveanswer > 0)
579     {
580       *ap = NULL;
581       *hap = NULL;
582 #if defined(RESOLVSORT)
583       /*
584        * Note: we sort even if host can take only one address
585        * in its return structures - should give it the "best"
586        * address in that case, not some random one
587        */
588       if (_res.nsort && haveanswer > 1 && qtype == T_A)
589         addrsort (host_data->h_addr_ptrs, haveanswer);
590 #endif /*RESOLVSORT*/
591
592       if (result->h_name == NULL)
593         {
594           n = strlen (qname) + 1;       /* For the \0.  */
595           if (n > linebuflen)
596             goto try_again;
597           strcpy (bp, qname);
598           result->h_name = bp;
599           bp += n;
600           linebuflen -= n;
601         }
602
603       if (_res.options & RES_USE_INET6)
604         map_v4v6_hostent (result, &bp, &linebuflen);
605       *h_errnop = NETDB_SUCCESS;
606       return NSS_STATUS_SUCCESS;
607     }
608 try_again:
609   *h_errnop = TRY_AGAIN;
610   return NSS_STATUS_TRYAGAIN;
611 }