1/* Copyright (C) 2004-2021 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 <https://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#include <resolv/resolv_context.h>
27#include <resolv/resolv-internal.h>
28#include <nss_dns.h>
29
30#if PACKETSZ > 65536
31# define MAXPACKET PACKETSZ
32#else
33# define MAXPACKET 65536
34#endif
35
36
37/* We need this time later. */
38typedef union querybuf
39{
40 HEADER hdr;
41 unsigned char buf[MAXPACKET];
42} querybuf;
43
44
45static const short int qtypes[] = { ns_t_a, ns_t_aaaa };
46#define nqtypes (sizeof (qtypes) / sizeof (qtypes[0]))
47
48
49enum nss_status
50_nss_dns_getcanonname_r (const char *name, char *buffer, size_t buflen,
51 char **result,int *errnop, int *h_errnop)
52{
53 /* Just an alibi buffer, res_nquery will allocate a real buffer for
54 us. */
55 unsigned char buf[20];
56 union
57 {
58 querybuf *buf;
59 unsigned char *ptr;
60 } ansp = { .ptr = buf };
61 enum nss_status status = NSS_STATUS_UNAVAIL;
62
63 struct resolv_context *ctx = __resolv_context_get ();
64 if (ctx == NULL)
65 {
66 *errnop = errno;
67 *h_errnop = NETDB_INTERNAL;
68 return NSS_STATUS_UNAVAIL;
69 }
70
71 for (int i = 0; i < nqtypes; ++i)
72 {
73 int r = __res_context_query (ctx, name, ns_c_in, qtypes[i],
74 buf, sizeof (buf), &ansp.ptr, NULL, NULL,
75 NULL, NULL);
76 if (r > 0)
77 {
78 /* We need to decode the response. Just one question record.
79 And if we got no answers we bail out, too. */
80 if (ansp.buf->hdr.qdcount != htons (1))
81 continue;
82
83 /* Number of answers. */
84 unsigned int ancount = ntohs (ansp.buf->hdr.ancount);
85
86 /* Beginning and end of the buffer with query, answer, and the
87 rest. */
88 unsigned char *ptr = &ansp.buf->buf[sizeof (HEADER)];
89 unsigned char *endptr = ansp.ptr + r;
90
91 /* Skip over the query. This is the name, type, and class. */
92 int s = __libc_dn_skipname (ptr, endptr);
93 if (s < 0)
94 {
95 unavail:
96 status = NSS_STATUS_UNAVAIL;
97 break;
98 }
99
100 /* Skip over the name and the two 16-bit values containing type
101 and class. */
102 ptr += s + 2 * sizeof (uint16_t);
103
104 while (ancount-- > 0)
105 {
106 /* Now the reply. First again the name from the query,
107 then type, class, TTL, and the length of the RDATA.
108 We remember the name start. */
109 unsigned char *namestart = ptr;
110 s = __libc_dn_skipname (ptr, endptr);
111 if (s < 0)
112 goto unavail;
113
114 ptr += s;
115
116 /* Check that there are enough bytes for the RR
117 metadata. */
118 if (endptr - ptr < 10)
119 goto unavail;
120
121 /* Check whether type and class match. */
122 uint_fast16_t type;
123 NS_GET16 (type, ptr);
124 if (type == qtypes[i])
125 {
126 /* We found the record. */
127 s = __libc_dn_expand (ansp.buf->buf, endptr, namestart,
128 buffer, buflen);
129 if (s < 0)
130 {
131 if (errno != EMSGSIZE)
132 goto unavail;
133
134 /* The buffer is too small. */
135 *errnop = ERANGE;
136 status = NSS_STATUS_TRYAGAIN;
137 h_errno = NETDB_INTERNAL;
138 }
139 else
140 {
141 /* Success. */
142 *result = buffer;
143 status = NSS_STATUS_SUCCESS;
144 }
145
146 goto out;
147 }
148
149 if (type != ns_t_cname)
150 goto unavail;
151
152 uint16_t rrclass;
153 NS_GET16 (rrclass, ptr);
154 if (rrclass != ns_c_in)
155 goto unavail;
156
157 /* Skip over TTL. */
158 ptr += sizeof (uint32_t);
159
160 /* Skip over RDATA length and RDATA itself. */
161 uint16_t rdatalen;
162 NS_GET16 (rdatalen, ptr);
163
164 /* Not enough room for RDATA. */
165 if (endptr - ptr < rdatalen)
166 goto unavail;
167 ptr += rdatalen;
168 }
169 }
170
171 /* Restore original buffer before retry. */
172 if (ansp.ptr != buf)
173 {
174 free (ansp.ptr);
175 ansp.ptr = buf;
176 }
177 }
178
179 out:
180 *h_errnop = h_errno;
181
182 if (ansp.ptr != buf)
183 free (ansp.ptr);
184 __resolv_context_put (ctx);
185 return status;
186}
187libc_hidden_def (_nss_dns_getcanonname_r)
188