(_nss_dns_getcanonname_r): Initialize status to NSS_STATUS_UNAVAIL.
[kopensolaris-gnu/glibc.git] / resolv / nss_dns / dns-canon.c
1 /* Copyright (C) 2004 Free Software Foundation, Inc.
2    This file is part of the GNU C Library.
3    Contributed by Ulrich Drepper <drepper@redhat.com>, 2004.
4
5    The GNU C Library is free software; you can redistribute it and/or
6    modify it under the terms of the GNU Lesser General Public
7    License as published by the Free Software Foundation; either
8    version 2.1 of the 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    Lesser General Public License for more details.
14
15    You should have received a copy of the GNU Lesser General Public
16    License along with the GNU C Library; if not, write to the Free
17    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
18    02111-1307 USA.  */
19
20 #include <errno.h>
21 #include <netdb.h>
22 #include <resolv.h>
23 #include <stdlib.h>
24 #include <arpa/nameser.h>
25 #include <nsswitch.h>
26
27
28 #if PACKETSZ > 65536
29 # define MAXPACKET      PACKETSZ
30 #else
31 # define MAXPACKET      65536
32 #endif
33
34
35 /* We need this time later.  */
36 typedef union querybuf
37 {
38   HEADER hdr;
39   unsigned char buf[MAXPACKET];
40 } querybuf;
41
42
43 enum nss_status
44 _nss_dns_getcanonname_r (const char *name, char *buffer, size_t buflen,
45                          char **result,int *errnop, int *h_errnop)
46 {
47   /* Just an alibi buffer, res_nquery will allocate a real buffer for
48      us.  */
49   unsigned char buf[20];
50   union
51   {
52     querybuf *buf;
53     unsigned char *ptr;
54   } ansp = { .ptr = buf };
55   enum nss_status status = NSS_STATUS_UNAVAIL;
56   int qtypes[] = { ns_t_a, ns_t_aaaa };
57 #define nqtypes (sizeof (qtypes) / sizeof (qtypes[0]))
58
59   for (int i = 0; i < nqtypes; ++i)
60     {
61       int r = __libc_res_nquery (&_res, name, ns_c_in, qtypes[i],
62                                  buf, sizeof (buf), &ansp.ptr);
63       if (r > 0)
64         {
65           /* We need to decode the response.  Just one question record.
66              And if we got no answers we bail out, too.  */
67           if (ansp.buf->hdr.qdcount != htons (1))
68             continue;
69
70           /* Number of answers.   */
71           unsigned int ancount = ntohs (ansp.buf->hdr.ancount);
72
73           /* Beginning and end of the buffer with query, answer, and the
74              rest.  */
75           unsigned char *ptr = &ansp.buf->buf[sizeof (HEADER)];
76           unsigned char *endptr = ansp.ptr + r;
77
78           /* Skip over the query.  This is the name, type, and class.  */
79           int s = __dn_skipname (ptr, endptr);
80           if (s < 0)
81             {
82             unavail:
83               status = NSS_STATUS_UNAVAIL;
84               break;
85             }
86
87           /* Skip over the name and the two 16-bit values containing type
88              and class.  */
89           ptr += s + 2 * sizeof (uint16_t);
90
91           while (ancount-- > 0)
92             {
93               /* Now the reply.  First again the name from the query,
94                  then type, class, TTL, and the length of the RDATA.
95                  We remember the name start.  */
96               unsigned char *namestart = ptr;
97               s = __dn_skipname (ptr, endptr);
98               if (s < 0)
99                 goto unavail;
100
101               ptr += s;
102
103               /* Check whether type and class match.  */
104               unsigned int type = ntohs (*(uint16_t *) ptr);
105               if (type == qtypes[i])
106                 {
107                   /* We found the record.  */
108                   s = __dn_expand (ansp.buf->buf, endptr, namestart,
109                                    buffer, buflen);
110                   if (s < 0)
111                     {
112                       if (errno != EMSGSIZE)
113                         goto unavail;
114
115                       /* The buffer is too small.  */
116                       *errnop = ERANGE;
117                       status = NSS_STATUS_TRYAGAIN;
118                       h_errno = NETDB_INTERNAL;
119                     }
120                   else
121                     {
122                       /* Success.  */
123                       *result = buffer;
124                       status = NSS_STATUS_SUCCESS;
125                     }
126
127                   goto out;
128                 }
129
130               if (type != ns_t_cname)
131                 goto unavail;
132
133               ptr += sizeof (uint16_t);
134               if (*(uint16_t *) ptr != htons (ns_c_in))
135                 goto unavail;
136
137               /* Also skip over the TTL.  */
138               ptr += sizeof (uint16_t) + sizeof (uint32_t);
139
140               /* Skip over the data length and data.  */
141               ptr += sizeof (uint16_t) + ntohs (*(uint16_t *) ptr);
142             }
143         }
144     }
145
146  out:
147   *h_errnop = h_errno;
148
149   if (ansp.ptr != buf)
150     free (ansp.ptr);
151
152   return status;
153 }