nptl: Handle spurious EINTR when thread cancellation is disabled (BZ#29029)
[glibc.git] / support / support_format_dns_packet.c
blobe8b3c125e3d25c995123eb7610b1d6ec73dfe27d
1 /* Convert a DNS packet to a human-readable representation.
2 Copyright (C) 2016-2022 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
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.
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.
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/>. */
19 #include <support/format_nss.h>
21 #include <arpa/inet.h>
22 #include <resolv.h>
23 #include <stdbool.h>
24 #include <support/check.h>
25 #include <support/support.h>
26 #include <support/xmemstream.h>
28 struct in_buffer
30 const unsigned char *data;
31 size_t size;
34 static inline bool
35 extract_16 (struct in_buffer *in, unsigned short *value)
37 if (in->size < 2)
38 return false;
39 *value = (in->data[0] << 8) | in->data[1];
40 in->data += 2;
41 in->size -= 2;
42 return true;
45 static inline bool
46 extract_32 (struct in_buffer *in, unsigned *value)
48 if (in->size < 4)
49 return false;
50 unsigned a = in->data[0];
51 unsigned b = in->data[1];
52 unsigned c = in->data[2];
53 unsigned d = in->data[3];
54 *value = (a << 24) | (b << 16) | (c << 8) | d;
55 in->data += 4;
56 in->size -= 4;
57 return true;
60 static inline bool
61 extract_bytes (struct in_buffer *in, size_t length, struct in_buffer *value)
63 if (in->size < length)
64 return false;
65 *value = (struct in_buffer) {in->data, length};
66 in->data += length;
67 in->size -= length;
68 return true;
71 struct dname
73 char name[MAXDNAME + 1];
76 static bool
77 extract_name (struct in_buffer full, struct in_buffer *in, struct dname *value)
79 const unsigned char *full_end = full.data + full.size;
80 /* Sanity checks; these indicate buffer misuse. */
81 TEST_VERIFY_EXIT
82 (!(in->data < full.data || in->data > full_end
83 || in->size > (size_t) (full_end - in->data)));
84 int ret = dn_expand (full.data, full_end, in->data,
85 value->name, sizeof (value->name));
86 if (ret < 0)
87 return false;
88 in->data += ret;
89 in->size -= ret;
90 return true;
93 char *
94 support_format_dns_packet (const unsigned char *buffer, size_t length)
96 struct in_buffer full = { buffer, length };
97 struct in_buffer in = full;
98 struct xmemstream mem;
99 xopen_memstream (&mem);
101 unsigned short txnid;
102 unsigned short flags;
103 unsigned short qdcount;
104 unsigned short ancount;
105 unsigned short nscount;
106 unsigned short adcount;
107 if (!(extract_16 (&in, &txnid)
108 && extract_16 (&in, &flags)
109 && extract_16 (&in, &qdcount)
110 && extract_16 (&in, &ancount)
111 && extract_16 (&in, &nscount)
112 && extract_16 (&in, &adcount)))
114 fprintf (mem.out, "error: could not parse DNS header\n");
115 goto out;
117 if (qdcount != 1)
119 fprintf (mem.out, "error: question count is %d, not 1\n", qdcount);
120 goto out;
122 struct dname qname;
123 if (!extract_name (full, &in, &qname))
125 fprintf (mem.out, "error: malformed QNAME\n");
126 goto out;
128 unsigned short qtype;
129 unsigned short qclass;
130 if (!(extract_16 (&in, &qtype)
131 && extract_16 (&in, &qclass)))
133 fprintf (mem.out, "error: malformed question\n");
134 goto out;
136 if (qtype != T_A && qtype != T_AAAA && qtype != T_PTR)
138 fprintf (mem.out, "error: unsupported QTYPE %d\n", qtype);
139 goto out;
142 fprintf (mem.out, "name: %s\n", qname.name);
144 for (int i = 0; i < ancount; ++i)
146 struct dname rname;
147 if (!extract_name (full, &in, &rname))
149 fprintf (mem.out, "error: malformed record name\n");
150 goto out;
152 unsigned short rtype;
153 unsigned short rclass;
154 unsigned ttl;
155 unsigned short rdlen;
156 struct in_buffer rdata;
157 if (!(extract_16 (&in, &rtype)
158 && extract_16 (&in, &rclass)
159 && extract_32 (&in, &ttl)
160 && extract_16 (&in, &rdlen)
161 && extract_bytes (&in, rdlen, &rdata)))
163 fprintf (mem.out, "error: malformed record header\n");
164 goto out;
166 /* Skip non-matching record types. */
167 if ((rtype != qtype && rtype != T_CNAME) || rclass != qclass)
168 continue;
169 switch (rtype)
171 case T_A:
172 if (rdlen == 4)
173 fprintf (mem.out, "address: %d.%d.%d.%d\n",
174 rdata.data[0],
175 rdata.data[1],
176 rdata.data[2],
177 rdata.data[3]);
178 else
179 fprintf (mem.out, "error: A record of size %d: %s\n",
180 rdlen, rname.name);
181 break;
182 case T_AAAA:
184 if (rdlen == 16)
186 char buf[100];
187 if (inet_ntop (AF_INET6, rdata.data, buf, sizeof (buf)) == NULL)
188 fprintf (mem.out, "error: AAAA record decoding failed: %m\n");
189 else
190 fprintf (mem.out, "address: %s\n", buf);
192 else
193 fprintf (mem.out, "error: AAAA record of size %d: %s\n",
194 rdlen, rname.name);
196 break;
197 case T_CNAME:
198 case T_PTR:
200 struct dname name;
201 if (extract_name (full, &rdata, &name))
202 fprintf (mem.out, "name: %s\n", name.name);
203 else
204 fprintf (mem.out, "error: malformed CNAME/PTR record\n");
209 out:
210 xfclose_memstream (&mem);
211 return mem.buffer;