1/* Copyright (C) 2004-2016 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, see
17 <http://www.gnu.org/licenses/>. */
18
19#include <errno.h>
20#include <netdb.h>
21#include <resolv.h>
22#include <stdlib.h>
23#include <stdint.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. */
36typedef union querybuf
37{
38 HEADER hdr;
39 unsigned char buf[MAXPACKET];
40} querybuf;
41
42
43static const short int qtypes[] = { ns_t_a, ns_t_aaaa };
44#define nqtypes (sizeof (qtypes) / sizeof (qtypes[0]))
45
46
47enum 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 NULL, NULL);
66 if (r > 0)
67 {
68 /* We need to decode the response. Just one question record.
69 And if we got no answers we bail out, too. */
70 if (ansp.buf->hdr.qdcount != htons (1))
71 continue;
72
73 /* Number of answers. */
74 unsigned int ancount = ntohs (ansp.buf->hdr.ancount);
75
76 /* Beginning and end of the buffer with query, answer, and the
77 rest. */
78 unsigned char *ptr = &ansp.buf->buf[sizeof (HEADER)];
79 unsigned char *endptr = ansp.ptr + r;
80
81 /* Skip over the query. This is the name, type, and class. */
82 int s = __dn_skipname (ptr, endptr);
83 if (s < 0)
84 {
85 unavail:
86 status = NSS_STATUS_UNAVAIL;
87 break;
88 }
89
90 /* Skip over the name and the two 16-bit values containing type
91 and class. */
92 ptr += s + 2 * sizeof (uint16_t);
93
94 while (ancount-- > 0)
95 {
96 /* Now the reply. First again the name from the query,
97 then type, class, TTL, and the length of the RDATA.
98 We remember the name start. */
99 unsigned char *namestart = ptr;
100 s = __dn_skipname (ptr, endptr);
101 if (s < 0)
102 goto unavail;
103
104 ptr += s;
105
106 /* Check that there are enough bytes for the RR
107 metadata. */
108 if (endptr - ptr < 10)
109 goto unavail;
110
111 /* Check whether type and class match. */
112 uint_fast16_t type;
113 NS_GET16 (type, ptr);
114 if (type == qtypes[i])
115 {
116 /* We found the record. */
117 s = __dn_expand (ansp.buf->buf, endptr, namestart,
118 buffer, buflen);
119 if (s < 0)
120 {
121 if (errno != EMSGSIZE)
122 goto unavail;
123
124 /* The buffer is too small. */
125 *errnop = ERANGE;
126 status = NSS_STATUS_TRYAGAIN;
127 h_errno = NETDB_INTERNAL;
128 }
129 else
130 {
131 /* Success. */
132 *result = buffer;
133 status = NSS_STATUS_SUCCESS;
134 }
135
136 goto out;
137 }
138
139 if (type != ns_t_cname)
140 goto unavail;
141
142 if (__ns_get16 (ptr) != ns_c_in)
143 goto unavail;
144
145 /* Also skip over class and TTL. */
146 ptr += sizeof (uint16_t) + sizeof (uint32_t);
147
148 /* Skip over RDATA length and RDATA itself. */
149 uint16_t rdatalen = __ns_get16 (ptr);
150 ptr += sizeof (uint16_t);
151 /* Not enough room for RDATA. */
152 if (endptr - ptr < rdatalen)
153 goto unavail;
154 ptr += rdatalen;
155 }
156 }
157
158 /* Restore original buffer before retry. */
159 if (ansp.ptr != buf)
160 {
161 free (ansp.ptr);
162 ansp.ptr = buf;
163 }
164 }
165
166 out:
167 *h_errnop = h_errno;
168
169 if (ansp.ptr != buf)
170 free (ansp.ptr);
171
172 return status;
173}
174