Branch data Line data Source code
1 : : /*
2 : : * Copyright (c) 2021 Igalia S.L.
3 : : *
4 : : * SPDX-License-Identifier: LGPL-2.1-or-later
5 : : *
6 : : * This library is free software; you can redistribute it and/or
7 : : * modify it under the terms of the GNU Lesser General Public
8 : : * License as published by the Free Software Foundation; either
9 : : * version 2.1 of the License, or (at your option) any later version.
10 : : *
11 : : * This library is distributed in the hope that it will be useful,
12 : : * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 : : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 : : * Lesser General Public License for more details.
15 : : *
16 : : * You should have received a copy of the GNU Lesser General
17 : : * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
18 : : *
19 : : * Authors: Patrick Griffis <pgriffis@igalia.com>
20 : : */
21 : :
22 : : #include "config.h"
23 : :
24 : : #include <glib.h>
25 : : #include <gio/gnetworking.h>
26 : :
27 : : #define GIO_COMPILATION
28 : : #include "gthreadedresolver.h"
29 : : #include "gthreadedresolver-private.h"
30 : : #undef GIO_COMPILATION
31 : :
32 : : #ifdef HAVE_DN_COMP
33 : : static void
34 : 541 : dns_builder_add_uint8 (GByteArray *builder,
35 : : guint8 value)
36 : : {
37 : 541 : g_byte_array_append (builder, &value, 1);
38 : 541 : }
39 : :
40 : : static void
41 : 185 : dns_builder_add_uint16 (GByteArray *builder,
42 : : guint16 value)
43 : : {
44 : 185 : dns_builder_add_uint8 (builder, (value >> 8) & 0xFF);
45 : 185 : dns_builder_add_uint8 (builder, (value) & 0xFF);
46 : 185 : }
47 : :
48 : : static void
49 : 38 : dns_builder_add_uint32 (GByteArray *builder,
50 : : guint32 value)
51 : : {
52 : 38 : dns_builder_add_uint8 (builder, (value >> 24) & 0xFF);
53 : 38 : dns_builder_add_uint8 (builder, (value >> 16) & 0xFF);
54 : 38 : dns_builder_add_uint8 (builder, (value >> 8) & 0xFF);
55 : 38 : dns_builder_add_uint8 (builder, (value) & 0xFF);
56 : 38 : }
57 : :
58 : : static void
59 : 3 : dns_builder_add_length_prefixed_string (GByteArray *builder,
60 : : const char *string)
61 : : {
62 : : guint8 length;
63 : :
64 : 3 : g_assert (strlen (string) <= G_MAXUINT8);
65 : :
66 : 3 : length = (guint8) strlen (string);
67 : 3 : dns_builder_add_uint8 (builder, length);
68 : :
69 : : /* Don't include trailing NUL */
70 : 3 : g_byte_array_append (builder, (const guchar *)string, length);
71 : 3 : }
72 : :
73 : : static void
74 : 28 : dns_builder_add_domain (GByteArray *builder,
75 : : const char *string)
76 : : {
77 : : int ret;
78 : : guchar buffer[256];
79 : :
80 : 28 : ret = dn_comp (string, buffer, sizeof (buffer), NULL, NULL);
81 : 28 : g_assert (ret != -1);
82 : :
83 : 28 : g_byte_array_append (builder, buffer, ret);
84 : 28 : }
85 : :
86 : : /* Append an invalid domain name to the DNS response. This is implemented by
87 : : * appending a single label followed by a pointer back to that label. This is
88 : : * invalid regardless of any other context in the response as its expansion is
89 : : * infinite.
90 : : *
91 : : * See https://datatracker.ietf.org/doc/html/rfc1035#section-4.1.4
92 : : *
93 : : * In order to create a pointer to the label, the label’s final offset in the
94 : : * DNS response must be known. The current length of @builder, plus @offset, is
95 : : * used for this. Hence, @offset is the additional offset (in bytes) to add, and
96 : : * typically corresponds to the length of the parent #GByteArray that @builder
97 : : * will eventually be added to. Potentially plus 2 bytes for the rdlength, as
98 : : * per dns_builder_add_answer_data(). */
99 : : static void
100 : 5 : dns_builder_add_invalid_domain (GByteArray *builder,
101 : : gsize offset)
102 : : {
103 : 5 : offset += builder->len;
104 : 5 : g_assert ((offset & 0xc0) == 0);
105 : :
106 : 5 : dns_builder_add_uint8 (builder, 1);
107 : 5 : dns_builder_add_uint8 (builder, 'f');
108 : 5 : dns_builder_add_uint8 (builder, 0xc0 | offset);
109 : 5 : }
110 : :
111 : : static void
112 : 18 : dns_builder_add_answer_data (GByteArray *builder,
113 : : GByteArray *answer)
114 : : {
115 : 18 : dns_builder_add_uint16 (builder, answer->len); /* rdlength */
116 : 18 : g_byte_array_append (builder, answer->data, answer->len);
117 : 18 : }
118 : :
119 : : static GByteArray *
120 : 19 : dns_header (void)
121 : : {
122 : 19 : GByteArray *answer = g_byte_array_sized_new (2046);
123 : :
124 : : /* Start with a header, we ignore everything except ancount.
125 : : https://datatracker.ietf.org/doc/html/rfc1035#section-4.1.1 */
126 : 19 : dns_builder_add_uint16 (answer, 0); /* ID */
127 : 19 : dns_builder_add_uint16 (answer, 0); /* |QR| Opcode |AA|TC|RD|RA| Z | RCODE | */
128 : 19 : dns_builder_add_uint16 (answer, 0); /* QDCOUNT */
129 : 19 : dns_builder_add_uint16 (answer, 1); /* ANCOUNT (1 answer) */
130 : 19 : dns_builder_add_uint16 (answer, 0); /* NSCOUNT */
131 : 19 : dns_builder_add_uint16 (answer, 0); /* ARCOUNT */
132 : :
133 : 19 : return g_steal_pointer (&answer);
134 : : }
135 : :
136 : : static void
137 : 12 : assert_query_fails (const gchar *rrname,
138 : : GResolverRecordType record_type,
139 : : GByteArray *answer)
140 : : {
141 : 12 : GList *records = NULL;
142 : 12 : GError *local_error = NULL;
143 : :
144 : 12 : records = g_resolver_records_from_res_query (rrname,
145 : : g_resolver_record_type_to_rrtype (record_type),
146 : 12 : answer->data,
147 : 12 : answer->len,
148 : : 0,
149 : : &local_error);
150 : :
151 : 12 : g_assert_error (local_error, G_RESOLVER_ERROR, G_RESOLVER_ERROR_INTERNAL);
152 : 12 : g_assert_null (records);
153 : 12 : g_clear_error (&local_error);
154 : 12 : }
155 : :
156 : : static void
157 : 6 : assert_query_succeeds (const gchar *rrname,
158 : : GResolverRecordType record_type,
159 : : GByteArray *answer,
160 : : const gchar *expected_answer_variant_str)
161 : : {
162 : 6 : GList *records = NULL;
163 : 6 : GVariant *answer_variant, *expected_answer_variant = NULL;
164 : 6 : GError *local_error = NULL;
165 : :
166 : 6 : records = g_resolver_records_from_res_query (rrname,
167 : : g_resolver_record_type_to_rrtype (record_type),
168 : 6 : answer->data,
169 : 6 : answer->len,
170 : : 0,
171 : : &local_error);
172 : :
173 : 6 : g_assert_no_error (local_error);
174 : 6 : g_assert_nonnull (records);
175 : :
176 : : /* Test the results. */
177 : 6 : answer_variant = records->data;
178 : 6 : expected_answer_variant = g_variant_new_parsed (expected_answer_variant_str);
179 : 6 : g_assert_cmpvariant (answer_variant, expected_answer_variant);
180 : :
181 : 6 : g_variant_unref (expected_answer_variant);
182 : 6 : g_list_free_full (records, (GDestroyNotify) g_variant_unref);
183 : 6 : }
184 : : #endif /* HAVE_DN_COMP */
185 : :
186 : : static void
187 : 1 : test_invalid_header (void)
188 : : {
189 : : const struct
190 : : {
191 : : const guint8 *answer;
192 : : gsize answer_len;
193 : : GResolverError expected_error_code;
194 : : }
195 : 1 : vectors[] =
196 : : {
197 : : /* No answer: */
198 : : { (const guint8 *) "", 0, G_RESOLVER_ERROR_NOT_FOUND },
199 : : /* Definitely too short to be a valid header: */
200 : : { (const guint8 *) "\x20", 1, G_RESOLVER_ERROR_INTERNAL },
201 : : /* One byte too short to be a valid header: */
202 : : { (const guint8 *) "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 11, G_RESOLVER_ERROR_INTERNAL },
203 : : /* Valid header indicating no answers: */
204 : : { (const guint8 *) "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 12, G_RESOLVER_ERROR_NOT_FOUND },
205 : : };
206 : : gsize i;
207 : :
208 [ + + ]: 5 : for (i = 0; i < G_N_ELEMENTS (vectors); i++)
209 : : {
210 : 4 : GList *records = NULL;
211 : 4 : GError *local_error = NULL;
212 : :
213 : 4 : records = g_resolver_records_from_res_query ("example.org",
214 : : g_resolver_record_type_to_rrtype (G_RESOLVER_RECORD_NS),
215 : 4 : vectors[i].answer,
216 : 4 : vectors[i].answer_len,
217 : : 0,
218 : : &local_error);
219 : :
220 : 4 : g_assert_error (local_error, G_RESOLVER_ERROR, (gint) vectors[i].expected_error_code);
221 : 4 : g_assert_null (records);
222 : 4 : g_clear_error (&local_error);
223 : : }
224 : 1 : }
225 : :
226 : : static void
227 : 1 : test_unknown_record_type (void)
228 : : {
229 : : #ifndef HAVE_DN_COMP
230 : : g_test_skip ("The dn_comp() function was not available.");
231 : : return;
232 : : #else
233 : 1 : GByteArray *answer = NULL;
234 : 1 : GList *records = NULL;
235 : 1 : GError *local_error = NULL;
236 : 1 : const guint type_id = 20; /* ISDN, not supported anywhere */
237 : :
238 : : /* An answer with an unsupported type chosen from
239 : : * https://en.wikipedia.org/wiki/List_of_DNS_record_types#[1]_Obsolete_record_types */
240 : 1 : answer = dns_header ();
241 : 1 : dns_builder_add_domain (answer, "example.org");
242 : 1 : dns_builder_add_uint16 (answer, type_id);
243 : 1 : dns_builder_add_uint16 (answer, 1); /* qclass=C_IN */
244 : 1 : dns_builder_add_uint32 (answer, 0); /* ttl (ignored) */
245 : 1 : dns_builder_add_uint16 (answer, 0); /* rdlength */
246 : :
247 : 1 : records = g_resolver_records_from_res_query ("example.org",
248 : : type_id,
249 : 1 : answer->data,
250 : 1 : answer->len,
251 : : 0,
252 : : &local_error);
253 : :
254 : 1 : g_assert_error (local_error, G_RESOLVER_ERROR, G_RESOLVER_ERROR_NOT_FOUND);
255 : 1 : g_assert_null (records);
256 : 1 : g_clear_error (&local_error);
257 : :
258 : 1 : g_byte_array_free (answer, TRUE);
259 : : #endif
260 : 1 : }
261 : :
262 : : static void
263 : 1 : test_mx_valid (void)
264 : : {
265 : : #ifndef HAVE_DN_COMP
266 : : g_test_skip ("The dn_comp() function was not available.");
267 : : return;
268 : : #else
269 : 1 : GByteArray *answer = NULL, *mx_rdata = NULL;
270 : :
271 : 1 : answer = dns_header ();
272 : :
273 : : /* Resource record */
274 : 1 : dns_builder_add_domain (answer, "example.org");
275 : 1 : dns_builder_add_uint16 (answer, g_resolver_record_type_to_rrtype (G_RESOLVER_RECORD_MX));
276 : 1 : dns_builder_add_uint16 (answer, 1); /* qclass=C_IN */
277 : 1 : dns_builder_add_uint32 (answer, 0); /* ttl (ignored) */
278 : :
279 : : /* MX rdata, https://datatracker.ietf.org/doc/html/rfc1035#section-3.3.9 */
280 : 1 : mx_rdata = g_byte_array_new ();
281 : 1 : dns_builder_add_uint16 (mx_rdata, 0); /* preference */
282 : 1 : dns_builder_add_domain (mx_rdata, "mail.example.org");
283 : 1 : dns_builder_add_answer_data (answer, mx_rdata);
284 : 1 : g_byte_array_unref (mx_rdata);
285 : :
286 : 1 : assert_query_succeeds ("example.org", G_RESOLVER_RECORD_MX, answer,
287 : : "(@q 0, 'mail.example.org')");
288 : :
289 : 1 : g_byte_array_free (answer, TRUE);
290 : : #endif
291 : 1 : }
292 : :
293 : : static void
294 : 1 : test_mx_invalid (void)
295 : : {
296 : : #ifndef HAVE_DN_COMP
297 : : g_test_skip ("The dn_comp() function was not available.");
298 : : return;
299 : : #else
300 : 1 : GByteArray *answer = NULL, *mx_rdata = NULL;
301 : :
302 : 1 : answer = dns_header ();
303 : :
304 : : /* Resource record */
305 : 1 : dns_builder_add_domain (answer, "example.org");
306 : 1 : dns_builder_add_uint16 (answer, g_resolver_record_type_to_rrtype (G_RESOLVER_RECORD_MX));
307 : 1 : dns_builder_add_uint16 (answer, 1); /* qclass=C_IN */
308 : 1 : dns_builder_add_uint32 (answer, 0); /* ttl (ignored) */
309 : :
310 : : /* MX rdata, https://datatracker.ietf.org/doc/html/rfc1035#section-3.3.9
311 : : *
312 : : * Use an invalid domain to trigger parsing failure. */
313 : 1 : mx_rdata = g_byte_array_new ();
314 : 1 : dns_builder_add_uint16 (mx_rdata, 0); /* preference */
315 : 1 : dns_builder_add_invalid_domain (mx_rdata, answer->len + 2);
316 : 1 : dns_builder_add_answer_data (answer, mx_rdata);
317 : 1 : g_byte_array_unref (mx_rdata);
318 : :
319 : 1 : assert_query_fails ("example.org", G_RESOLVER_RECORD_MX, answer);
320 : :
321 : 1 : g_byte_array_free (answer, TRUE);
322 : : #endif
323 : 1 : }
324 : :
325 : : static void
326 : 1 : test_mx_invalid_too_short (void)
327 : : {
328 : : #ifndef HAVE_DN_COMP
329 : : g_test_skip ("The dn_comp() function was not available.");
330 : : return;
331 : : #else
332 : 1 : GByteArray *answer = NULL, *mx_rdata = NULL;
333 : :
334 : 1 : answer = dns_header ();
335 : :
336 : : /* Resource record */
337 : 1 : dns_builder_add_domain (answer, "example.org");
338 : 1 : dns_builder_add_uint16 (answer, g_resolver_record_type_to_rrtype (G_RESOLVER_RECORD_MX));
339 : 1 : dns_builder_add_uint16 (answer, 1); /* qclass=C_IN */
340 : 1 : dns_builder_add_uint32 (answer, 0); /* ttl (ignored) */
341 : :
342 : : /* MX rdata, https://datatracker.ietf.org/doc/html/rfc1035#section-3.3.9
343 : : *
344 : : * Miss out the domain field to trigger failure */
345 : 1 : mx_rdata = g_byte_array_new ();
346 : 1 : dns_builder_add_uint16 (mx_rdata, 0); /* preference */
347 : : /* missing domain field */
348 : 1 : dns_builder_add_answer_data (answer, mx_rdata);
349 : 1 : g_byte_array_unref (mx_rdata);
350 : :
351 : 1 : assert_query_fails ("example.org", G_RESOLVER_RECORD_MX, answer);
352 : :
353 : 1 : g_byte_array_free (answer, TRUE);
354 : : #endif
355 : 1 : }
356 : :
357 : : static void
358 : 1 : test_mx_invalid_too_short2 (void)
359 : : {
360 : : #ifndef HAVE_DN_COMP
361 : : g_test_skip ("The dn_comp() function was not available.");
362 : : return;
363 : : #else
364 : 1 : GByteArray *answer = NULL, *mx_rdata = NULL;
365 : :
366 : 1 : answer = dns_header ();
367 : :
368 : : /* Resource record */
369 : 1 : dns_builder_add_domain (answer, "example.org");
370 : 1 : dns_builder_add_uint16 (answer, g_resolver_record_type_to_rrtype (G_RESOLVER_RECORD_MX));
371 : 1 : dns_builder_add_uint16 (answer, 1); /* qclass=C_IN */
372 : 1 : dns_builder_add_uint32 (answer, 0); /* ttl (ignored) */
373 : :
374 : : /* MX rdata, https://datatracker.ietf.org/doc/html/rfc1035#section-3.3.9
375 : : *
376 : : * Miss out all fields to trigger failure */
377 : 1 : mx_rdata = g_byte_array_new ();
378 : : /* missing preference and domain fields */
379 : 1 : dns_builder_add_answer_data (answer, mx_rdata);
380 : 1 : g_byte_array_unref (mx_rdata);
381 : :
382 : 1 : assert_query_fails ("example.org", G_RESOLVER_RECORD_MX, answer);
383 : :
384 : 1 : g_byte_array_free (answer, TRUE);
385 : : #endif
386 : 1 : }
387 : :
388 : : static void
389 : 1 : test_ns_valid (void)
390 : : {
391 : : #ifndef HAVE_DN_COMP
392 : : g_test_skip ("The dn_comp() function was not available.");
393 : : return;
394 : : #else
395 : 1 : GByteArray *answer = NULL, *ns_rdata = NULL;
396 : :
397 : 1 : answer = dns_header ();
398 : :
399 : : /* Resource record */
400 : 1 : dns_builder_add_domain (answer, "example.org");
401 : 1 : dns_builder_add_uint16 (answer, g_resolver_record_type_to_rrtype (G_RESOLVER_RECORD_NS));
402 : 1 : dns_builder_add_uint16 (answer, 1); /* qclass=C_IN */
403 : 1 : dns_builder_add_uint32 (answer, 0); /* ttl (ignored) */
404 : :
405 : : /* NS rdata, https://datatracker.ietf.org/doc/html/rfc1035#section-3.3.11 */
406 : 1 : ns_rdata = g_byte_array_new ();
407 : 1 : dns_builder_add_domain (ns_rdata, "ns.example.org");
408 : 1 : dns_builder_add_answer_data (answer, ns_rdata);
409 : 1 : g_byte_array_unref (ns_rdata);
410 : :
411 : 1 : assert_query_succeeds ("example.org", G_RESOLVER_RECORD_NS, answer,
412 : : "('ns.example.org',)");
413 : :
414 : 1 : g_byte_array_free (answer, TRUE);
415 : : #endif
416 : 1 : }
417 : :
418 : : static void
419 : 1 : test_ns_invalid (void)
420 : : {
421 : : #ifndef HAVE_DN_COMP
422 : : g_test_skip ("The dn_comp() function was not available.");
423 : : return;
424 : : #else
425 : 1 : GByteArray *answer = NULL, *ns_rdata = NULL;
426 : :
427 : 1 : answer = dns_header ();
428 : :
429 : : /* Resource record */
430 : 1 : dns_builder_add_domain (answer, "example.org");
431 : 1 : dns_builder_add_uint16 (answer, g_resolver_record_type_to_rrtype (G_RESOLVER_RECORD_NS));
432 : 1 : dns_builder_add_uint16 (answer, 1); /* qclass=C_IN */
433 : 1 : dns_builder_add_uint32 (answer, 0); /* ttl (ignored) */
434 : :
435 : : /* NS rdata, https://datatracker.ietf.org/doc/html/rfc1035#section-3.3.11
436 : : *
437 : : * Use an invalid domain to trigger parsing failure. */
438 : 1 : ns_rdata = g_byte_array_new ();
439 : 1 : dns_builder_add_invalid_domain (ns_rdata, answer->len + 2);
440 : 1 : dns_builder_add_answer_data (answer, ns_rdata);
441 : 1 : g_byte_array_unref (ns_rdata);
442 : :
443 : 1 : assert_query_fails ("example.org", G_RESOLVER_RECORD_NS, answer);
444 : :
445 : 1 : g_byte_array_free (answer, TRUE);
446 : : #endif
447 : 1 : }
448 : :
449 : : static void
450 : 1 : test_soa_valid (void)
451 : : {
452 : : #ifndef HAVE_DN_COMP
453 : : g_test_skip ("The dn_comp() function was not available.");
454 : : return;
455 : : #else
456 : 1 : GByteArray *answer = NULL, *soa_rdata = NULL;
457 : :
458 : 1 : answer = dns_header ();
459 : :
460 : : /* Resource record */
461 : 1 : dns_builder_add_domain (answer, "example.org");
462 : 1 : dns_builder_add_uint16 (answer, g_resolver_record_type_to_rrtype (G_RESOLVER_RECORD_SOA));
463 : 1 : dns_builder_add_uint16 (answer, 1); /* qclass=C_IN */
464 : 1 : dns_builder_add_uint32 (answer, 0); /* ttl (ignored) */
465 : :
466 : : /* SOA rdata, https://datatracker.ietf.org/doc/html/rfc1035#section-3.3.13 */
467 : 1 : soa_rdata = g_byte_array_new ();
468 : 1 : dns_builder_add_domain (soa_rdata, "mname.example.org");
469 : 1 : dns_builder_add_domain (soa_rdata, "rname.example.org");
470 : 1 : dns_builder_add_uint32 (soa_rdata, 0); /* serial */
471 : 1 : dns_builder_add_uint32 (soa_rdata, 0); /* refresh */
472 : 1 : dns_builder_add_uint32 (soa_rdata, 0); /* retry */
473 : 1 : dns_builder_add_uint32 (soa_rdata, 0); /* expire */
474 : 1 : dns_builder_add_uint32 (soa_rdata, 0); /* minimum */
475 : 1 : dns_builder_add_answer_data (answer, soa_rdata);
476 : 1 : g_byte_array_unref (soa_rdata);
477 : :
478 : 1 : assert_query_succeeds ("example.org", G_RESOLVER_RECORD_SOA, answer,
479 : : "('mname.example.org', 'rname.example.org', @u 0, @u 0, @u 0, @u 0, @u 0)");
480 : :
481 : 1 : g_byte_array_free (answer, TRUE);
482 : : #endif
483 : 1 : }
484 : :
485 : : static void
486 : 1 : test_soa_invalid_mname (void)
487 : : {
488 : : #ifndef HAVE_DN_COMP
489 : : g_test_skip ("The dn_comp() function was not available.");
490 : : return;
491 : : #else
492 : 1 : GByteArray *answer = NULL, *soa_rdata = NULL;
493 : :
494 : 1 : answer = dns_header ();
495 : :
496 : : /* Resource record */
497 : 1 : dns_builder_add_domain (answer, "example.org");
498 : 1 : dns_builder_add_uint16 (answer, g_resolver_record_type_to_rrtype (G_RESOLVER_RECORD_SOA));
499 : 1 : dns_builder_add_uint16 (answer, 1); /* qclass=C_IN */
500 : 1 : dns_builder_add_uint32 (answer, 0); /* ttl (ignored) */
501 : :
502 : : /* SOA rdata, https://datatracker.ietf.org/doc/html/rfc1035#section-3.3.13
503 : : *
504 : : * Use an invalid domain to trigger parsing failure. */
505 : 1 : soa_rdata = g_byte_array_new ();
506 : 1 : dns_builder_add_invalid_domain (soa_rdata, answer->len + 2); /* mname */
507 : 1 : dns_builder_add_domain (soa_rdata, "rname.example.org");
508 : 1 : dns_builder_add_uint32 (soa_rdata, 0); /* serial */
509 : 1 : dns_builder_add_uint32 (soa_rdata, 0); /* refresh */
510 : 1 : dns_builder_add_uint32 (soa_rdata, 0); /* retry */
511 : 1 : dns_builder_add_uint32 (soa_rdata, 0); /* expire */
512 : 1 : dns_builder_add_uint32 (soa_rdata, 0); /* minimum */
513 : 1 : dns_builder_add_answer_data (answer, soa_rdata);
514 : 1 : g_byte_array_unref (soa_rdata);
515 : :
516 : 1 : assert_query_fails ("example.org", G_RESOLVER_RECORD_SOA, answer);
517 : :
518 : 1 : g_byte_array_free (answer, TRUE);
519 : : #endif
520 : 1 : }
521 : :
522 : : static void
523 : 1 : test_soa_invalid_rname (void)
524 : : {
525 : : #ifndef HAVE_DN_COMP
526 : : g_test_skip ("The dn_comp() function was not available.");
527 : : return;
528 : : #else
529 : 1 : GByteArray *answer = NULL, *soa_rdata = NULL;
530 : :
531 : 1 : answer = dns_header ();
532 : :
533 : : /* Resource record */
534 : 1 : dns_builder_add_domain (answer, "example.org");
535 : 1 : dns_builder_add_uint16 (answer, g_resolver_record_type_to_rrtype (G_RESOLVER_RECORD_SOA));
536 : 1 : dns_builder_add_uint16 (answer, 1); /* qclass=C_IN */
537 : 1 : dns_builder_add_uint32 (answer, 0); /* ttl (ignored) */
538 : :
539 : : /* SOA rdata, https://datatracker.ietf.org/doc/html/rfc1035#section-3.3.13
540 : : *
541 : : * Use an invalid domain to trigger parsing failure. */
542 : 1 : soa_rdata = g_byte_array_new ();
543 : 1 : dns_builder_add_domain (soa_rdata, "mname.example.org");
544 : 1 : dns_builder_add_invalid_domain (soa_rdata, answer->len + 2); /* rname */
545 : 1 : dns_builder_add_uint32 (soa_rdata, 0); /* serial */
546 : 1 : dns_builder_add_uint32 (soa_rdata, 0); /* refresh */
547 : 1 : dns_builder_add_uint32 (soa_rdata, 0); /* retry */
548 : 1 : dns_builder_add_uint32 (soa_rdata, 0); /* expire */
549 : 1 : dns_builder_add_uint32 (soa_rdata, 0); /* minimum */
550 : 1 : dns_builder_add_answer_data (answer, soa_rdata);
551 : 1 : g_byte_array_unref (soa_rdata);
552 : :
553 : 1 : assert_query_fails ("example.org", G_RESOLVER_RECORD_SOA, answer);
554 : :
555 : 1 : g_byte_array_free (answer, TRUE);
556 : : #endif
557 : 1 : }
558 : :
559 : : static void
560 : 1 : test_soa_invalid_too_short (void)
561 : : {
562 : : #ifndef HAVE_DN_COMP
563 : : g_test_skip ("The dn_comp() function was not available.");
564 : : return;
565 : : #else
566 : 1 : GByteArray *answer = NULL, *soa_rdata = NULL;
567 : :
568 : 1 : answer = dns_header ();
569 : :
570 : : /* Resource record */
571 : 1 : dns_builder_add_domain (answer, "example.org");
572 : 1 : dns_builder_add_uint16 (answer, g_resolver_record_type_to_rrtype (G_RESOLVER_RECORD_SOA));
573 : 1 : dns_builder_add_uint16 (answer, 1); /* qclass=C_IN */
574 : 1 : dns_builder_add_uint32 (answer, 0); /* ttl (ignored) */
575 : :
576 : : /* SOA rdata, https://datatracker.ietf.org/doc/html/rfc1035#section-3.3.13
577 : : *
578 : : * Miss out one of the fields to trigger a failure. */
579 : 1 : soa_rdata = g_byte_array_new ();
580 : 1 : dns_builder_add_domain (soa_rdata, "mname.example.org");
581 : 1 : dns_builder_add_domain (soa_rdata, "rname.example.org");
582 : 1 : dns_builder_add_uint32 (soa_rdata, 0); /* serial */
583 : 1 : dns_builder_add_uint32 (soa_rdata, 0); /* refresh */
584 : 1 : dns_builder_add_uint32 (soa_rdata, 0); /* retry */
585 : 1 : dns_builder_add_uint32 (soa_rdata, 0); /* expire */
586 : : /* missing minimum field */
587 : 1 : dns_builder_add_answer_data (answer, soa_rdata);
588 : 1 : g_byte_array_unref (soa_rdata);
589 : :
590 : 1 : assert_query_fails ("example.org", G_RESOLVER_RECORD_SOA, answer);
591 : :
592 : 1 : g_byte_array_free (answer, TRUE);
593 : : #endif
594 : 1 : }
595 : :
596 : : static void
597 : 1 : test_txt_valid (void)
598 : : {
599 : : #ifndef HAVE_DN_COMP
600 : : g_test_skip ("The dn_comp() function was not available.");
601 : : return;
602 : : #else
603 : 1 : GByteArray *answer = NULL, *txt_rdata = NULL;
604 : :
605 : 1 : answer = dns_header ();
606 : :
607 : : /* Resource record */
608 : 1 : dns_builder_add_domain (answer, "example.org");
609 : 1 : dns_builder_add_uint16 (answer, g_resolver_record_type_to_rrtype (G_RESOLVER_RECORD_TXT));
610 : 1 : dns_builder_add_uint16 (answer, 1); /* qclass=C_IN */
611 : 1 : dns_builder_add_uint32 (answer, 0); /* ttl (ignored) */
612 : :
613 : : /* TXT rdata, https://datatracker.ietf.org/doc/html/rfc1035#section-3.3.14 */
614 : 1 : txt_rdata = g_byte_array_new ();
615 : 1 : dns_builder_add_length_prefixed_string (txt_rdata, "some test content");
616 : 1 : dns_builder_add_answer_data (answer, txt_rdata);
617 : 1 : g_byte_array_unref (txt_rdata);
618 : :
619 : 1 : assert_query_succeeds ("example.org", G_RESOLVER_RECORD_TXT, answer,
620 : : "(['some test content'],)");
621 : :
622 : 1 : g_byte_array_free (answer, TRUE);
623 : : #endif
624 : 1 : }
625 : :
626 : : static void
627 : 1 : test_txt_valid_multiple_strings (void)
628 : : {
629 : : #ifndef HAVE_DN_COMP
630 : : g_test_skip ("The dn_comp() function was not available.");
631 : : return;
632 : : #else
633 : 1 : GByteArray *answer = NULL, *txt_rdata = NULL;
634 : :
635 : 1 : answer = dns_header ();
636 : :
637 : : /* Resource record */
638 : 1 : dns_builder_add_domain (answer, "example.org");
639 : 1 : dns_builder_add_uint16 (answer, g_resolver_record_type_to_rrtype (G_RESOLVER_RECORD_TXT));
640 : 1 : dns_builder_add_uint16 (answer, 1); /* qclass=C_IN */
641 : 1 : dns_builder_add_uint32 (answer, 0); /* ttl (ignored) */
642 : :
643 : : /* TXT rdata, https://datatracker.ietf.org/doc/html/rfc1035#section-3.3.14 */
644 : 1 : txt_rdata = g_byte_array_new ();
645 : 1 : dns_builder_add_length_prefixed_string (txt_rdata, "some test content");
646 : 1 : dns_builder_add_length_prefixed_string (txt_rdata, "more test content");
647 : 1 : dns_builder_add_answer_data (answer, txt_rdata);
648 : 1 : g_byte_array_unref (txt_rdata);
649 : :
650 : 1 : assert_query_succeeds ("example.org", G_RESOLVER_RECORD_TXT, answer,
651 : : "(['some test content', 'more test content'],)");
652 : :
653 : 1 : g_byte_array_free (answer, TRUE);
654 : : #endif
655 : 1 : }
656 : :
657 : : static void
658 : 1 : test_txt_invalid_empty (void)
659 : : {
660 : : #ifndef HAVE_DN_COMP
661 : : g_test_skip ("The dn_comp() function was not available.");
662 : : return;
663 : : #else
664 : 1 : GByteArray *answer = NULL, *txt_rdata = NULL;
665 : :
666 : 1 : answer = dns_header ();
667 : :
668 : : /* Resource record */
669 : 1 : dns_builder_add_domain (answer, "example.org");
670 : 1 : dns_builder_add_uint16 (answer, g_resolver_record_type_to_rrtype (G_RESOLVER_RECORD_TXT));
671 : 1 : dns_builder_add_uint16 (answer, 1); /* qclass=C_IN */
672 : 1 : dns_builder_add_uint32 (answer, 0); /* ttl (ignored) */
673 : :
674 : : /* TXT rdata, https://datatracker.ietf.org/doc/html/rfc1035#section-3.3.14
675 : : *
676 : : * Provide zero character strings (i.e. an empty rdata section) to trigger
677 : : * failure. */
678 : 1 : txt_rdata = g_byte_array_new ();
679 : 1 : dns_builder_add_answer_data (answer, txt_rdata);
680 : 1 : g_byte_array_unref (txt_rdata);
681 : :
682 : 1 : assert_query_fails ("example.org", G_RESOLVER_RECORD_TXT, answer);
683 : :
684 : 1 : g_byte_array_free (answer, TRUE);
685 : : #endif
686 : 1 : }
687 : :
688 : : static void
689 : 1 : test_txt_invalid_overflow (void)
690 : : {
691 : : #ifndef HAVE_DN_COMP
692 : : g_test_skip ("The dn_comp() function was not available.");
693 : : return;
694 : : #else
695 : 1 : GByteArray *answer = NULL, *txt_rdata = NULL;
696 : :
697 : 1 : answer = dns_header ();
698 : :
699 : : /* Resource record */
700 : 1 : dns_builder_add_domain (answer, "example.org");
701 : 1 : dns_builder_add_uint16 (answer, g_resolver_record_type_to_rrtype (G_RESOLVER_RECORD_TXT));
702 : 1 : dns_builder_add_uint16 (answer, 1); /* qclass=C_IN */
703 : 1 : dns_builder_add_uint32 (answer, 0); /* ttl (ignored) */
704 : :
705 : : /* TXT rdata, https://datatracker.ietf.org/doc/html/rfc1035#section-3.3.14
706 : : *
707 : : * Use a character string whose length exceeds the remaining length in the
708 : : * answer record, to trigger failure. */
709 : 1 : txt_rdata = g_byte_array_new ();
710 : 1 : dns_builder_add_uint8 (txt_rdata, 10); /* length, but no content */
711 : 1 : dns_builder_add_answer_data (answer, txt_rdata);
712 : 1 : g_byte_array_unref (txt_rdata);
713 : :
714 : 1 : assert_query_fails ("example.org", G_RESOLVER_RECORD_TXT, answer);
715 : :
716 : 1 : g_byte_array_free (answer, TRUE);
717 : : #endif
718 : 1 : }
719 : :
720 : : static void
721 : 1 : test_srv_valid (void)
722 : : {
723 : : #ifndef HAVE_DN_COMP
724 : : g_test_skip ("The dn_comp() function was not available.");
725 : : return;
726 : : #else
727 : 1 : GByteArray *answer = NULL, *srv_rdata = NULL;
728 : :
729 : 1 : answer = dns_header ();
730 : :
731 : : /* Resource record */
732 : 1 : dns_builder_add_domain (answer, "example.org");
733 : 1 : dns_builder_add_uint16 (answer, g_resolver_record_type_to_rrtype (G_RESOLVER_RECORD_SRV));
734 : 1 : dns_builder_add_uint16 (answer, 1); /* qclass=C_IN */
735 : 1 : dns_builder_add_uint32 (answer, 0); /* ttl (ignored) */
736 : :
737 : : /* SRV rdata, https://datatracker.ietf.org/doc/html/rfc2782 */
738 : 1 : srv_rdata = g_byte_array_new ();
739 : 1 : dns_builder_add_uint16 (srv_rdata, 0); /* priority */
740 : 1 : dns_builder_add_uint16 (srv_rdata, 0); /* weight */
741 : 1 : dns_builder_add_uint16 (srv_rdata, 0); /* port */
742 : 1 : dns_builder_add_domain (srv_rdata, "target.example.org");
743 : 1 : dns_builder_add_answer_data (answer, srv_rdata);
744 : 1 : g_byte_array_unref (srv_rdata);
745 : :
746 : 1 : assert_query_succeeds ("example.org", G_RESOLVER_RECORD_SRV, answer,
747 : : "(@q 0, @q 0, @q 0, 'target.example.org')");
748 : :
749 : 1 : g_byte_array_free (answer, TRUE);
750 : : #endif
751 : 1 : }
752 : :
753 : : static void
754 : 1 : test_srv_invalid (void)
755 : : {
756 : : #ifndef HAVE_DN_COMP
757 : : g_test_skip ("The dn_comp() function was not available.");
758 : : return;
759 : : #else
760 : 1 : GByteArray *answer = NULL, *srv_rdata = NULL;
761 : :
762 : 1 : answer = dns_header ();
763 : :
764 : : /* Resource record */
765 : 1 : dns_builder_add_domain (answer, "example.org");
766 : 1 : dns_builder_add_uint16 (answer, g_resolver_record_type_to_rrtype (G_RESOLVER_RECORD_SRV));
767 : 1 : dns_builder_add_uint16 (answer, 1); /* qclass=C_IN */
768 : 1 : dns_builder_add_uint32 (answer, 0); /* ttl (ignored) */
769 : :
770 : : /* SRV rdata, https://datatracker.ietf.org/doc/html/rfc2782
771 : : *
772 : : * Use an invalid domain to trigger parsing failure. */
773 : 1 : srv_rdata = g_byte_array_new ();
774 : 1 : dns_builder_add_uint16 (srv_rdata, 0); /* priority */
775 : 1 : dns_builder_add_uint16 (srv_rdata, 0); /* weight */
776 : 1 : dns_builder_add_uint16 (srv_rdata, 0); /* port */
777 : 1 : dns_builder_add_invalid_domain (srv_rdata, answer->len + 2);
778 : 1 : dns_builder_add_answer_data (answer, srv_rdata);
779 : 1 : g_byte_array_unref (srv_rdata);
780 : :
781 : 1 : assert_query_fails ("example.org", G_RESOLVER_RECORD_SRV, answer);
782 : :
783 : 1 : g_byte_array_free (answer, TRUE);
784 : : #endif
785 : 1 : }
786 : :
787 : : static void
788 : 1 : test_srv_invalid_too_short (void)
789 : : {
790 : : #ifndef HAVE_DN_COMP
791 : : g_test_skip ("The dn_comp() function was not available.");
792 : : return;
793 : : #else
794 : 1 : GByteArray *answer = NULL, *srv_rdata = NULL;
795 : :
796 : 1 : answer = dns_header ();
797 : :
798 : : /* Resource record */
799 : 1 : dns_builder_add_domain (answer, "example.org");
800 : 1 : dns_builder_add_uint16 (answer, g_resolver_record_type_to_rrtype (G_RESOLVER_RECORD_SRV));
801 : 1 : dns_builder_add_uint16 (answer, 1); /* qclass=C_IN */
802 : 1 : dns_builder_add_uint32 (answer, 0); /* ttl (ignored) */
803 : :
804 : : /* SRV rdata, https://datatracker.ietf.org/doc/html/rfc2782
805 : : *
806 : : * Miss out the target field to trigger failure */
807 : 1 : srv_rdata = g_byte_array_new ();
808 : 1 : dns_builder_add_uint16 (srv_rdata, 0); /* priority */
809 : 1 : dns_builder_add_uint16 (srv_rdata, 0); /* weight */
810 : 1 : dns_builder_add_uint16 (srv_rdata, 0); /* port */
811 : : /* missing target field */
812 : 1 : dns_builder_add_answer_data (answer, srv_rdata);
813 : 1 : g_byte_array_unref (srv_rdata);
814 : :
815 : 1 : assert_query_fails ("example.org", G_RESOLVER_RECORD_SRV, answer);
816 : :
817 : 1 : g_byte_array_free (answer, TRUE);
818 : : #endif
819 : 1 : }
820 : :
821 : : static void
822 : 1 : test_srv_invalid_too_short2 (void)
823 : : {
824 : : #ifndef HAVE_DN_COMP
825 : : g_test_skip ("The dn_comp() function was not available.");
826 : : return;
827 : : #else
828 : 1 : GByteArray *answer = NULL, *srv_rdata = NULL;
829 : :
830 : 1 : answer = dns_header ();
831 : :
832 : : /* Resource record */
833 : 1 : dns_builder_add_domain (answer, "example.org");
834 : 1 : dns_builder_add_uint16 (answer, g_resolver_record_type_to_rrtype (G_RESOLVER_RECORD_SRV));
835 : 1 : dns_builder_add_uint16 (answer, 1); /* qclass=C_IN */
836 : 1 : dns_builder_add_uint32 (answer, 0); /* ttl (ignored) */
837 : :
838 : : /* SRV rdata, https://datatracker.ietf.org/doc/html/rfc2782
839 : : *
840 : : * Miss out the target and port fields to trigger failure */
841 : 1 : srv_rdata = g_byte_array_new ();
842 : 1 : dns_builder_add_uint16 (srv_rdata, 0); /* priority */
843 : 1 : dns_builder_add_uint16 (srv_rdata, 0); /* weight */
844 : : /* missing port and target fields */
845 : 1 : dns_builder_add_answer_data (answer, srv_rdata);
846 : 1 : g_byte_array_unref (srv_rdata);
847 : :
848 : 1 : assert_query_fails ("example.org", G_RESOLVER_RECORD_SRV, answer);
849 : :
850 : 1 : g_byte_array_free (answer, TRUE);
851 : : #endif
852 : 1 : }
853 : :
854 : : int
855 : 1 : main (int argc,
856 : : char *argv[])
857 : : {
858 : 1 : g_test_init (&argc, &argv, G_TEST_OPTION_ISOLATE_DIRS, NULL);
859 : :
860 : 1 : g_test_add_func ("/gresolver/invalid-header", test_invalid_header);
861 : 1 : g_test_add_func ("/gresolver/unknown-record-type", test_unknown_record_type);
862 : 1 : g_test_add_func ("/gresolver/mx/valid", test_mx_valid);
863 : 1 : g_test_add_func ("/gresolver/mx/invalid", test_mx_invalid);
864 : 1 : g_test_add_func ("/gresolver/mx/invalid/too-short", test_mx_invalid_too_short);
865 : 1 : g_test_add_func ("/gresolver/mx/invalid/too-short2", test_mx_invalid_too_short2);
866 : 1 : g_test_add_func ("/gresolver/ns/valid", test_ns_valid);
867 : 1 : g_test_add_func ("/gresolver/ns/invalid", test_ns_invalid);
868 : 1 : g_test_add_func ("/gresolver/soa/valid", test_soa_valid);
869 : 1 : g_test_add_func ("/gresolver/soa/invalid/mname", test_soa_invalid_mname);
870 : 1 : g_test_add_func ("/gresolver/soa/invalid/rname", test_soa_invalid_rname);
871 : 1 : g_test_add_func ("/gresolver/soa/invalid/too-short", test_soa_invalid_too_short);
872 : 1 : g_test_add_func ("/gresolver/srv/valid", test_srv_valid);
873 : 1 : g_test_add_func ("/gresolver/srv/invalid", test_srv_invalid);
874 : 1 : g_test_add_func ("/gresolver/srv/invalid/too-short", test_srv_invalid_too_short);
875 : 1 : g_test_add_func ("/gresolver/srv/invalid/too-short2", test_srv_invalid_too_short2);
876 : 1 : g_test_add_func ("/gresolver/txt/valid", test_txt_valid);
877 : 1 : g_test_add_func ("/gresolver/txt/valid/multiple-strings", test_txt_valid_multiple_strings);
878 : 1 : g_test_add_func ("/gresolver/txt/invalid/empty", test_txt_invalid_empty);
879 : 1 : g_test_add_func ("/gresolver/txt/invalid/overflow", test_txt_invalid_overflow);
880 : :
881 : 1 : return g_test_run ();
882 : : }
|