47949b862fd9f518eb0dfa076cc30d35d88fc9c7
[kopensolaris-gnu/glibc.git] / resolv / nss_dns / dns-canon.c
1 /* Copyright (C) 2004, 2006, 2008 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 static const short int qtypes[] = { ns_t_a, ns_t_aaaa };
44 #define nqtypes (sizeof (qtypes) / sizeof (qtypes[0]))
45
46
47 enum nss_status
48 _nss_dns_getcanonname_r (const char *name, char *buffer, size_t buflen,
49                          char **result,int *errnop, int *h_errnop)
50 {
51   /* Just an alibi buffer, res_nquery will allocate a real buffer for
52      us.  */
53   unsigned char buf[20];
54   union
55   {
56     querybuf *buf;
57     unsigned char *ptr;
58   } ansp = { .ptr = buf };
59   enum nss_status status = NSS_STATUS_UNAVAIL;
60
61   for (int i = 0; i < nqtypes; ++i)
62     {
63       int r = __libc_res_nquery (&_res, name, ns_c_in, qtypes[i],
64                                  buf, sizeof (buf), &ansp.ptr, NULL, NULL);
65       if (r > 0)
66         {
67           /* We need to decode the response.  Just one question record.
68              And if we got no answers we bail out, too.  */
69           if (ansp.buf->hdr.qdcount != htons (1))
70             continue;
71
72           /* Number of answers.   */
73           unsigned int ancount = ntohs (ansp.buf->hdr.ancount);
74
75           /* Beginning and end of the buffer with query, answer, and the
76              rest.  */
77           unsigned char *ptr = &ansp.buf->buf[sizeof (HEADER)];
78           unsigned char *endptr = ansp.ptr + r;
79
80           /* Skip over the query.  This is the name, type, and class.  */
81           int s = __dn_skipname (ptr, endptr);
82           if (s < 0)
83             {
84             unavail:
85               status = NSS_STATUS_UNAVAIL;
86               break;
87             }
88
89           /* Skip over the name and the two 16-bit values containing type
90              and class.  */
91           ptr += s + 2 * sizeof (uint16_t);
92
93           while (ancount-- > 0)
94             {
95               /* Now the reply.  First again the name from the query,
96                  then type, class, TTL, and the length of the RDATA.
97                  We remember the name start.  */
98               unsigned char *namestart = ptr;
99               s = __dn_skipname (ptr, endptr);
100               if (s < 0)
101                 goto unavail;
102
103               ptr += s;
104
105               /* Check whether type and class match.  */
106               uint_fast16_t type;
107               NS_GET16 (type, ptr);
108               if (type == qtypes[i])
109                 {
110                   /* We found the record.  */
111                   s = __dn_expand (ansp.buf->buf, endptr, namestart,
112                                    buffer, buflen);
113                   if (s < 0)
114                     {
115                       if (errno != EMSGSIZE)
116                         goto unavail;
117
118                       /* The buffer is too small.  */
119                       *errnop = ERANGE;
120                       status = NSS_STATUS_TRYAGAIN;
121                       h_errno = NETDB_INTERNAL;
122                     }
123                   else
124                     {
125                       /* Success.  */
126                       *result = buffer;
127                       status = NSS_STATUS_SUCCESS;
128                     }
129
130                   goto out;
131                 }
132
133               if (type != ns_t_cname)
134                 goto unavail;
135
136               if (ns_get16 (ptr) != ns_c_in)
137                 goto unavail;
138
139               /* Also skip over the TTL.  */
140               ptr += sizeof (uint16_t) + sizeof (uint32_t);
141
142               /* Skip over the data length and data.  */
143               ptr += sizeof (uint16_t) + ns_get16 (ptr);
144             }
145         }
146     }
147
148  out:
149   *h_errnop = h_errno;
150
151   if (ansp.ptr != buf)
152     free (ansp.ptr);
153
154   return status;
155 }