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