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 : 564 : dns_builder_add_uint8 (GByteArray *builder,
35 : : guint8 value)
36 : : {
37 : 564 : g_byte_array_append (builder, &value, 1);
38 : 564 : }
39 : :
40 : : static void
41 : 194 : dns_builder_add_uint16 (GByteArray *builder,
42 : : guint16 value)
43 : : {
44 : 194 : dns_builder_add_uint8 (builder, (value >> 8) & 0xFF);
45 : 194 : dns_builder_add_uint8 (builder, (value) & 0xFF);
46 : 194 : }
47 : :
48 : : static void
49 : 39 : dns_builder_add_uint32 (GByteArray *builder,
50 : : guint32 value)
51 : : {
52 : 39 : dns_builder_add_uint8 (builder, (value >> 24) & 0xFF);
53 : 39 : dns_builder_add_uint8 (builder, (value >> 16) & 0xFF);
54 : 39 : dns_builder_add_uint8 (builder, (value >> 8) & 0xFF);
55 : 39 : dns_builder_add_uint8 (builder, (value) & 0xFF);
56 : 39 : }
57 : :
58 : : static void
59 : 4 : dns_builder_add_length_prefixed_string (GByteArray *builder,
60 : : const char *string)
61 : : {
62 : : guint8 length;
63 : :
64 : 4 : g_assert (strlen (string) <= G_MAXUINT8);
65 : :
66 : 4 : length = (guint8) strlen (string);
67 : 4 : dns_builder_add_uint8 (builder, length);
68 : :
69 : : /* Don't include trailing NUL */
70 : 4 : g_byte_array_append (builder, (const guchar *)string, length);
71 : 4 : }
72 : :
73 : : static void
74 : 29 : dns_builder_add_domain (GByteArray *builder,
75 : : const char *string)
76 : : {
77 : : int ret;
78 : : guchar buffer[256];
79 : :
80 : 29 : ret = dn_comp (string, buffer, sizeof (buffer), NULL, NULL);
81 : 29 : g_assert (ret != -1);
82 : :
83 : 29 : g_byte_array_append (builder, buffer, ret);
84 : 29 : }
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 : 19 : dns_builder_add_answer_data (GByteArray *builder,
113 : : GByteArray *answer)
114 : : {
115 : 19 : dns_builder_add_uint16 (builder, answer->len); /* rdlength */
116 : 19 : g_byte_array_append (builder, answer->data, answer->len);
117 : 19 : }
118 : :
119 : : static GByteArray *
120 : 20 : dns_header (void)
121 : : {
122 : 20 : 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 : 20 : dns_builder_add_uint16 (answer, 0); /* ID */
127 : 20 : dns_builder_add_uint16 (answer, 0); /* |QR| Opcode |AA|TC|RD|RA| Z | RCODE | */
128 : 20 : dns_builder_add_uint16 (answer, 0); /* QDCOUNT */
129 : 20 : dns_builder_add_uint16 (answer, 1); /* ANCOUNT (1 answer) */
130 : 20 : dns_builder_add_uint16 (answer, 0); /* NSCOUNT */
131 : 20 : dns_builder_add_uint16 (answer, 0); /* ARCOUNT */
132 : :
133 : 20 : 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 : 6 : g_assert_true (!g_variant_is_floating (answer_variant));
181 : :
182 : 6 : g_variant_unref (expected_answer_variant);
183 : 6 : g_list_free_full (records, (GDestroyNotify) g_variant_unref);
184 : 6 : }
185 : : #endif /* HAVE_DN_COMP */
186 : :
187 : : static void
188 : 1 : test_invalid_header (void)
189 : : {
190 : : const struct
191 : : {
192 : : const guint8 *answer;
193 : : gsize answer_len;
194 : : GResolverError expected_error_code;
195 : : }
196 : 1 : vectors[] =
197 : : {
198 : : /* No answer: */
199 : : { (const guint8 *) "", 0, G_RESOLVER_ERROR_NOT_FOUND },
200 : : /* Definitely too short to be a valid header: */
201 : : { (const guint8 *) "\x20", 1, G_RESOLVER_ERROR_INTERNAL },
202 : : /* One byte too short to be a valid header: */
203 : : { (const guint8 *) "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 11, G_RESOLVER_ERROR_INTERNAL },
204 : : /* Valid header indicating no answers: */
205 : : { (const guint8 *) "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 12, G_RESOLVER_ERROR_NOT_FOUND },
206 : : };
207 : : gsize i;
208 : :
209 : 5 : for (i = 0; i < G_N_ELEMENTS (vectors); i++)
210 : : {
211 : 4 : GList *records = NULL;
212 : 4 : GError *local_error = NULL;
213 : :
214 : 4 : records = g_resolver_records_from_res_query ("example.org",
215 : : g_resolver_record_type_to_rrtype (G_RESOLVER_RECORD_NS),
216 : 4 : vectors[i].answer,
217 : 4 : vectors[i].answer_len,
218 : : 0,
219 : : &local_error);
220 : :
221 : 4 : g_assert_error (local_error, G_RESOLVER_ERROR, (gint) vectors[i].expected_error_code);
222 : 4 : g_assert_null (records);
223 : 4 : g_clear_error (&local_error);
224 : : }
225 : 1 : }
226 : :
227 : : static void
228 : 1 : test_record_ownership (void)
229 : : {
230 : : #ifndef HAVE_DN_COMP
231 : : g_test_skip ("The dn_comp() function was not available.");
232 : : return;
233 : : #else
234 : 1 : GByteArray *answer = NULL, *txt_rdata = NULL;
235 : 1 : GList *records = NULL;
236 : 1 : GError *local_error = NULL;
237 : : uint16_t rrtype;
238 : :
239 : 1 : rrtype = g_resolver_record_type_to_rrtype (G_RESOLVER_RECORD_TXT);
240 : 1 : answer = dns_header ();
241 : :
242 : : /* Resource record */
243 : 1 : dns_builder_add_domain (answer, "example.org");
244 : 1 : dns_builder_add_uint16 (answer, rrtype);
245 : 1 : dns_builder_add_uint16 (answer, 1); /* qclass=C_IN */
246 : 1 : dns_builder_add_uint32 (answer, 0); /* ttl (ignored) */
247 : :
248 : : /* TXT rdata, https://datatracker.ietf.org/doc/html/rfc1035#section-3.3.14 */
249 : 1 : txt_rdata = g_byte_array_new ();
250 : 1 : dns_builder_add_length_prefixed_string (txt_rdata, "some test content");
251 : 1 : dns_builder_add_answer_data (answer, txt_rdata);
252 : 1 : g_byte_array_unref (txt_rdata);
253 : :
254 : 1 : records = g_resolver_records_from_res_query ("example.org",
255 : : rrtype,
256 : 1 : answer->data,
257 : 1 : answer->len,
258 : : 0,
259 : : &local_error);
260 : 1 : g_assert_no_error (local_error);
261 : 1 : g_assert_nonnull (records);
262 : :
263 : 2 : for (const GList *iter = records; iter != NULL; iter = iter->next)
264 : 1 : g_assert_true (!g_variant_is_floating (records->data));
265 : :
266 : 1 : g_list_free_full (records, (GDestroyNotify) g_variant_unref);
267 : 1 : g_byte_array_free (answer, TRUE);
268 : : #endif
269 : 1 : }
270 : :
271 : : static void
272 : 1 : test_unknown_record_type (void)
273 : : {
274 : : #ifndef HAVE_DN_COMP
275 : : g_test_skip ("The dn_comp() function was not available.");
276 : : return;
277 : : #else
278 : 1 : GByteArray *answer = NULL;
279 : 1 : GList *records = NULL;
280 : 1 : GError *local_error = NULL;
281 : 1 : const guint type_id = 20; /* ISDN, not supported anywhere */
282 : :
283 : : /* An answer with an unsupported type chosen from
284 : : * https://en.wikipedia.org/wiki/List_of_DNS_record_types#[1]_Obsolete_record_types */
285 : 1 : answer = dns_header ();
286 : 1 : dns_builder_add_domain (answer, "example.org");
287 : 1 : dns_builder_add_uint16 (answer, type_id);
288 : 1 : dns_builder_add_uint16 (answer, 1); /* qclass=C_IN */
289 : 1 : dns_builder_add_uint32 (answer, 0); /* ttl (ignored) */
290 : 1 : dns_builder_add_uint16 (answer, 0); /* rdlength */
291 : :
292 : 1 : records = g_resolver_records_from_res_query ("example.org",
293 : : type_id,
294 : 1 : answer->data,
295 : 1 : answer->len,
296 : : 0,
297 : : &local_error);
298 : :
299 : 1 : g_assert_error (local_error, G_RESOLVER_ERROR, G_RESOLVER_ERROR_NOT_FOUND);
300 : 1 : g_assert_null (records);
301 : 1 : g_clear_error (&local_error);
302 : :
303 : 1 : g_byte_array_free (answer, TRUE);
304 : : #endif
305 : 1 : }
306 : :
307 : : static void
308 : 1 : test_mx_valid (void)
309 : : {
310 : : #ifndef HAVE_DN_COMP
311 : : g_test_skip ("The dn_comp() function was not available.");
312 : : return;
313 : : #else
314 : 1 : GByteArray *answer = NULL, *mx_rdata = NULL;
315 : :
316 : 1 : answer = dns_header ();
317 : :
318 : : /* Resource record */
319 : 1 : dns_builder_add_domain (answer, "example.org");
320 : 1 : dns_builder_add_uint16 (answer, g_resolver_record_type_to_rrtype (G_RESOLVER_RECORD_MX));
321 : 1 : dns_builder_add_uint16 (answer, 1); /* qclass=C_IN */
322 : 1 : dns_builder_add_uint32 (answer, 0); /* ttl (ignored) */
323 : :
324 : : /* MX rdata, https://datatracker.ietf.org/doc/html/rfc1035#section-3.3.9 */
325 : 1 : mx_rdata = g_byte_array_new ();
326 : 1 : dns_builder_add_uint16 (mx_rdata, 0); /* preference */
327 : 1 : dns_builder_add_domain (mx_rdata, "mail.example.org");
328 : 1 : dns_builder_add_answer_data (answer, mx_rdata);
329 : 1 : g_byte_array_unref (mx_rdata);
330 : :
331 : 1 : assert_query_succeeds ("example.org", G_RESOLVER_RECORD_MX, answer,
332 : : "(@q 0, 'mail.example.org')");
333 : :
334 : 1 : g_byte_array_free (answer, TRUE);
335 : : #endif
336 : 1 : }
337 : :
338 : : static void
339 : 1 : test_mx_invalid (void)
340 : : {
341 : : #ifndef HAVE_DN_COMP
342 : : g_test_skip ("The dn_comp() function was not available.");
343 : : return;
344 : : #else
345 : 1 : GByteArray *answer = NULL, *mx_rdata = NULL;
346 : :
347 : 1 : answer = dns_header ();
348 : :
349 : : /* Resource record */
350 : 1 : dns_builder_add_domain (answer, "example.org");
351 : 1 : dns_builder_add_uint16 (answer, g_resolver_record_type_to_rrtype (G_RESOLVER_RECORD_MX));
352 : 1 : dns_builder_add_uint16 (answer, 1); /* qclass=C_IN */
353 : 1 : dns_builder_add_uint32 (answer, 0); /* ttl (ignored) */
354 : :
355 : : /* MX rdata, https://datatracker.ietf.org/doc/html/rfc1035#section-3.3.9
356 : : *
357 : : * Use an invalid domain to trigger parsing failure. */
358 : 1 : mx_rdata = g_byte_array_new ();
359 : 1 : dns_builder_add_uint16 (mx_rdata, 0); /* preference */
360 : 1 : dns_builder_add_invalid_domain (mx_rdata, answer->len + 2);
361 : 1 : dns_builder_add_answer_data (answer, mx_rdata);
362 : 1 : g_byte_array_unref (mx_rdata);
363 : :
364 : 1 : assert_query_fails ("example.org", G_RESOLVER_RECORD_MX, answer);
365 : :
366 : 1 : g_byte_array_free (answer, TRUE);
367 : : #endif
368 : 1 : }
369 : :
370 : : static void
371 : 1 : test_mx_invalid_too_short (void)
372 : : {
373 : : #ifndef HAVE_DN_COMP
374 : : g_test_skip ("The dn_comp() function was not available.");
375 : : return;
376 : : #else
377 : 1 : GByteArray *answer = NULL, *mx_rdata = NULL;
378 : :
379 : 1 : answer = dns_header ();
380 : :
381 : : /* Resource record */
382 : 1 : dns_builder_add_domain (answer, "example.org");
383 : 1 : dns_builder_add_uint16 (answer, g_resolver_record_type_to_rrtype (G_RESOLVER_RECORD_MX));
384 : 1 : dns_builder_add_uint16 (answer, 1); /* qclass=C_IN */
385 : 1 : dns_builder_add_uint32 (answer, 0); /* ttl (ignored) */
386 : :
387 : : /* MX rdata, https://datatracker.ietf.org/doc/html/rfc1035#section-3.3.9
388 : : *
389 : : * Miss out the domain field to trigger failure */
390 : 1 : mx_rdata = g_byte_array_new ();
391 : 1 : dns_builder_add_uint16 (mx_rdata, 0); /* preference */
392 : : /* missing domain field */
393 : 1 : dns_builder_add_answer_data (answer, mx_rdata);
394 : 1 : g_byte_array_unref (mx_rdata);
395 : :
396 : 1 : assert_query_fails ("example.org", G_RESOLVER_RECORD_MX, answer);
397 : :
398 : 1 : g_byte_array_free (answer, TRUE);
399 : : #endif
400 : 1 : }
401 : :
402 : : static void
403 : 1 : test_mx_invalid_too_short2 (void)
404 : : {
405 : : #ifndef HAVE_DN_COMP
406 : : g_test_skip ("The dn_comp() function was not available.");
407 : : return;
408 : : #else
409 : 1 : GByteArray *answer = NULL, *mx_rdata = NULL;
410 : :
411 : 1 : answer = dns_header ();
412 : :
413 : : /* Resource record */
414 : 1 : dns_builder_add_domain (answer, "example.org");
415 : 1 : dns_builder_add_uint16 (answer, g_resolver_record_type_to_rrtype (G_RESOLVER_RECORD_MX));
416 : 1 : dns_builder_add_uint16 (answer, 1); /* qclass=C_IN */
417 : 1 : dns_builder_add_uint32 (answer, 0); /* ttl (ignored) */
418 : :
419 : : /* MX rdata, https://datatracker.ietf.org/doc/html/rfc1035#section-3.3.9
420 : : *
421 : : * Miss out all fields to trigger failure */
422 : 1 : mx_rdata = g_byte_array_new ();
423 : : /* missing preference and domain fields */
424 : 1 : dns_builder_add_answer_data (answer, mx_rdata);
425 : 1 : g_byte_array_unref (mx_rdata);
426 : :
427 : 1 : assert_query_fails ("example.org", G_RESOLVER_RECORD_MX, answer);
428 : :
429 : 1 : g_byte_array_free (answer, TRUE);
430 : : #endif
431 : 1 : }
432 : :
433 : : static void
434 : 1 : test_ns_valid (void)
435 : : {
436 : : #ifndef HAVE_DN_COMP
437 : : g_test_skip ("The dn_comp() function was not available.");
438 : : return;
439 : : #else
440 : 1 : GByteArray *answer = NULL, *ns_rdata = NULL;
441 : :
442 : 1 : answer = dns_header ();
443 : :
444 : : /* Resource record */
445 : 1 : dns_builder_add_domain (answer, "example.org");
446 : 1 : dns_builder_add_uint16 (answer, g_resolver_record_type_to_rrtype (G_RESOLVER_RECORD_NS));
447 : 1 : dns_builder_add_uint16 (answer, 1); /* qclass=C_IN */
448 : 1 : dns_builder_add_uint32 (answer, 0); /* ttl (ignored) */
449 : :
450 : : /* NS rdata, https://datatracker.ietf.org/doc/html/rfc1035#section-3.3.11 */
451 : 1 : ns_rdata = g_byte_array_new ();
452 : 1 : dns_builder_add_domain (ns_rdata, "ns.example.org");
453 : 1 : dns_builder_add_answer_data (answer, ns_rdata);
454 : 1 : g_byte_array_unref (ns_rdata);
455 : :
456 : 1 : assert_query_succeeds ("example.org", G_RESOLVER_RECORD_NS, answer,
457 : : "('ns.example.org',)");
458 : :
459 : 1 : g_byte_array_free (answer, TRUE);
460 : : #endif
461 : 1 : }
462 : :
463 : : static void
464 : 1 : test_ns_invalid (void)
465 : : {
466 : : #ifndef HAVE_DN_COMP
467 : : g_test_skip ("The dn_comp() function was not available.");
468 : : return;
469 : : #else
470 : 1 : GByteArray *answer = NULL, *ns_rdata = NULL;
471 : :
472 : 1 : answer = dns_header ();
473 : :
474 : : /* Resource record */
475 : 1 : dns_builder_add_domain (answer, "example.org");
476 : 1 : dns_builder_add_uint16 (answer, g_resolver_record_type_to_rrtype (G_RESOLVER_RECORD_NS));
477 : 1 : dns_builder_add_uint16 (answer, 1); /* qclass=C_IN */
478 : 1 : dns_builder_add_uint32 (answer, 0); /* ttl (ignored) */
479 : :
480 : : /* NS rdata, https://datatracker.ietf.org/doc/html/rfc1035#section-3.3.11
481 : : *
482 : : * Use an invalid domain to trigger parsing failure. */
483 : 1 : ns_rdata = g_byte_array_new ();
484 : 1 : dns_builder_add_invalid_domain (ns_rdata, answer->len + 2);
485 : 1 : dns_builder_add_answer_data (answer, ns_rdata);
486 : 1 : g_byte_array_unref (ns_rdata);
487 : :
488 : 1 : assert_query_fails ("example.org", G_RESOLVER_RECORD_NS, answer);
489 : :
490 : 1 : g_byte_array_free (answer, TRUE);
491 : : #endif
492 : 1 : }
493 : :
494 : : static void
495 : 1 : test_soa_valid (void)
496 : : {
497 : : #ifndef HAVE_DN_COMP
498 : : g_test_skip ("The dn_comp() function was not available.");
499 : : return;
500 : : #else
501 : 1 : GByteArray *answer = NULL, *soa_rdata = NULL;
502 : :
503 : 1 : answer = dns_header ();
504 : :
505 : : /* Resource record */
506 : 1 : dns_builder_add_domain (answer, "example.org");
507 : 1 : dns_builder_add_uint16 (answer, g_resolver_record_type_to_rrtype (G_RESOLVER_RECORD_SOA));
508 : 1 : dns_builder_add_uint16 (answer, 1); /* qclass=C_IN */
509 : 1 : dns_builder_add_uint32 (answer, 0); /* ttl (ignored) */
510 : :
511 : : /* SOA rdata, https://datatracker.ietf.org/doc/html/rfc1035#section-3.3.13 */
512 : 1 : soa_rdata = g_byte_array_new ();
513 : 1 : dns_builder_add_domain (soa_rdata, "mname.example.org");
514 : 1 : dns_builder_add_domain (soa_rdata, "rname.example.org");
515 : 1 : dns_builder_add_uint32 (soa_rdata, 0); /* serial */
516 : 1 : dns_builder_add_uint32 (soa_rdata, 0); /* refresh */
517 : 1 : dns_builder_add_uint32 (soa_rdata, 0); /* retry */
518 : 1 : dns_builder_add_uint32 (soa_rdata, 0); /* expire */
519 : 1 : dns_builder_add_uint32 (soa_rdata, 0); /* minimum */
520 : 1 : dns_builder_add_answer_data (answer, soa_rdata);
521 : 1 : g_byte_array_unref (soa_rdata);
522 : :
523 : 1 : assert_query_succeeds ("example.org", G_RESOLVER_RECORD_SOA, answer,
524 : : "('mname.example.org', 'rname.example.org', @u 0, @u 0, @u 0, @u 0, @u 0)");
525 : :
526 : 1 : g_byte_array_free (answer, TRUE);
527 : : #endif
528 : 1 : }
529 : :
530 : : static void
531 : 1 : test_soa_invalid_mname (void)
532 : : {
533 : : #ifndef HAVE_DN_COMP
534 : : g_test_skip ("The dn_comp() function was not available.");
535 : : return;
536 : : #else
537 : 1 : GByteArray *answer = NULL, *soa_rdata = NULL;
538 : :
539 : 1 : answer = dns_header ();
540 : :
541 : : /* Resource record */
542 : 1 : dns_builder_add_domain (answer, "example.org");
543 : 1 : dns_builder_add_uint16 (answer, g_resolver_record_type_to_rrtype (G_RESOLVER_RECORD_SOA));
544 : 1 : dns_builder_add_uint16 (answer, 1); /* qclass=C_IN */
545 : 1 : dns_builder_add_uint32 (answer, 0); /* ttl (ignored) */
546 : :
547 : : /* SOA rdata, https://datatracker.ietf.org/doc/html/rfc1035#section-3.3.13
548 : : *
549 : : * Use an invalid domain to trigger parsing failure. */
550 : 1 : soa_rdata = g_byte_array_new ();
551 : 1 : dns_builder_add_invalid_domain (soa_rdata, answer->len + 2); /* mname */
552 : 1 : dns_builder_add_domain (soa_rdata, "rname.example.org");
553 : 1 : dns_builder_add_uint32 (soa_rdata, 0); /* serial */
554 : 1 : dns_builder_add_uint32 (soa_rdata, 0); /* refresh */
555 : 1 : dns_builder_add_uint32 (soa_rdata, 0); /* retry */
556 : 1 : dns_builder_add_uint32 (soa_rdata, 0); /* expire */
557 : 1 : dns_builder_add_uint32 (soa_rdata, 0); /* minimum */
558 : 1 : dns_builder_add_answer_data (answer, soa_rdata);
559 : 1 : g_byte_array_unref (soa_rdata);
560 : :
561 : 1 : assert_query_fails ("example.org", G_RESOLVER_RECORD_SOA, answer);
562 : :
563 : 1 : g_byte_array_free (answer, TRUE);
564 : : #endif
565 : 1 : }
566 : :
567 : : static void
568 : 1 : test_soa_invalid_rname (void)
569 : : {
570 : : #ifndef HAVE_DN_COMP
571 : : g_test_skip ("The dn_comp() function was not available.");
572 : : return;
573 : : #else
574 : 1 : GByteArray *answer = NULL, *soa_rdata = NULL;
575 : :
576 : 1 : answer = dns_header ();
577 : :
578 : : /* Resource record */
579 : 1 : dns_builder_add_domain (answer, "example.org");
580 : 1 : dns_builder_add_uint16 (answer, g_resolver_record_type_to_rrtype (G_RESOLVER_RECORD_SOA));
581 : 1 : dns_builder_add_uint16 (answer, 1); /* qclass=C_IN */
582 : 1 : dns_builder_add_uint32 (answer, 0); /* ttl (ignored) */
583 : :
584 : : /* SOA rdata, https://datatracker.ietf.org/doc/html/rfc1035#section-3.3.13
585 : : *
586 : : * Use an invalid domain to trigger parsing failure. */
587 : 1 : soa_rdata = g_byte_array_new ();
588 : 1 : dns_builder_add_domain (soa_rdata, "mname.example.org");
589 : 1 : dns_builder_add_invalid_domain (soa_rdata, answer->len + 2); /* rname */
590 : 1 : dns_builder_add_uint32 (soa_rdata, 0); /* serial */
591 : 1 : dns_builder_add_uint32 (soa_rdata, 0); /* refresh */
592 : 1 : dns_builder_add_uint32 (soa_rdata, 0); /* retry */
593 : 1 : dns_builder_add_uint32 (soa_rdata, 0); /* expire */
594 : 1 : dns_builder_add_uint32 (soa_rdata, 0); /* minimum */
595 : 1 : dns_builder_add_answer_data (answer, soa_rdata);
596 : 1 : g_byte_array_unref (soa_rdata);
597 : :
598 : 1 : assert_query_fails ("example.org", G_RESOLVER_RECORD_SOA, answer);
599 : :
600 : 1 : g_byte_array_free (answer, TRUE);
601 : : #endif
602 : 1 : }
603 : :
604 : : static void
605 : 1 : test_soa_invalid_too_short (void)
606 : : {
607 : : #ifndef HAVE_DN_COMP
608 : : g_test_skip ("The dn_comp() function was not available.");
609 : : return;
610 : : #else
611 : 1 : GByteArray *answer = NULL, *soa_rdata = NULL;
612 : :
613 : 1 : answer = dns_header ();
614 : :
615 : : /* Resource record */
616 : 1 : dns_builder_add_domain (answer, "example.org");
617 : 1 : dns_builder_add_uint16 (answer, g_resolver_record_type_to_rrtype (G_RESOLVER_RECORD_SOA));
618 : 1 : dns_builder_add_uint16 (answer, 1); /* qclass=C_IN */
619 : 1 : dns_builder_add_uint32 (answer, 0); /* ttl (ignored) */
620 : :
621 : : /* SOA rdata, https://datatracker.ietf.org/doc/html/rfc1035#section-3.3.13
622 : : *
623 : : * Miss out one of the fields to trigger a failure. */
624 : 1 : soa_rdata = g_byte_array_new ();
625 : 1 : dns_builder_add_domain (soa_rdata, "mname.example.org");
626 : 1 : dns_builder_add_domain (soa_rdata, "rname.example.org");
627 : 1 : dns_builder_add_uint32 (soa_rdata, 0); /* serial */
628 : 1 : dns_builder_add_uint32 (soa_rdata, 0); /* refresh */
629 : 1 : dns_builder_add_uint32 (soa_rdata, 0); /* retry */
630 : 1 : dns_builder_add_uint32 (soa_rdata, 0); /* expire */
631 : : /* missing minimum field */
632 : 1 : dns_builder_add_answer_data (answer, soa_rdata);
633 : 1 : g_byte_array_unref (soa_rdata);
634 : :
635 : 1 : assert_query_fails ("example.org", G_RESOLVER_RECORD_SOA, answer);
636 : :
637 : 1 : g_byte_array_free (answer, TRUE);
638 : : #endif
639 : 1 : }
640 : :
641 : : static void
642 : 1 : test_txt_valid (void)
643 : : {
644 : : #ifndef HAVE_DN_COMP
645 : : g_test_skip ("The dn_comp() function was not available.");
646 : : return;
647 : : #else
648 : 1 : GByteArray *answer = NULL, *txt_rdata = NULL;
649 : :
650 : 1 : answer = dns_header ();
651 : :
652 : : /* Resource record */
653 : 1 : dns_builder_add_domain (answer, "example.org");
654 : 1 : dns_builder_add_uint16 (answer, g_resolver_record_type_to_rrtype (G_RESOLVER_RECORD_TXT));
655 : 1 : dns_builder_add_uint16 (answer, 1); /* qclass=C_IN */
656 : 1 : dns_builder_add_uint32 (answer, 0); /* ttl (ignored) */
657 : :
658 : : /* TXT rdata, https://datatracker.ietf.org/doc/html/rfc1035#section-3.3.14 */
659 : 1 : txt_rdata = g_byte_array_new ();
660 : 1 : dns_builder_add_length_prefixed_string (txt_rdata, "some test content");
661 : 1 : dns_builder_add_answer_data (answer, txt_rdata);
662 : 1 : g_byte_array_unref (txt_rdata);
663 : :
664 : 1 : assert_query_succeeds ("example.org", G_RESOLVER_RECORD_TXT, answer,
665 : : "(['some test content'],)");
666 : :
667 : 1 : g_byte_array_free (answer, TRUE);
668 : : #endif
669 : 1 : }
670 : :
671 : : static void
672 : 1 : test_txt_valid_multiple_strings (void)
673 : : {
674 : : #ifndef HAVE_DN_COMP
675 : : g_test_skip ("The dn_comp() function was not available.");
676 : : return;
677 : : #else
678 : 1 : GByteArray *answer = NULL, *txt_rdata = NULL;
679 : :
680 : 1 : answer = dns_header ();
681 : :
682 : : /* Resource record */
683 : 1 : dns_builder_add_domain (answer, "example.org");
684 : 1 : dns_builder_add_uint16 (answer, g_resolver_record_type_to_rrtype (G_RESOLVER_RECORD_TXT));
685 : 1 : dns_builder_add_uint16 (answer, 1); /* qclass=C_IN */
686 : 1 : dns_builder_add_uint32 (answer, 0); /* ttl (ignored) */
687 : :
688 : : /* TXT rdata, https://datatracker.ietf.org/doc/html/rfc1035#section-3.3.14 */
689 : 1 : txt_rdata = g_byte_array_new ();
690 : 1 : dns_builder_add_length_prefixed_string (txt_rdata, "some test content");
691 : 1 : dns_builder_add_length_prefixed_string (txt_rdata, "more test content");
692 : 1 : dns_builder_add_answer_data (answer, txt_rdata);
693 : 1 : g_byte_array_unref (txt_rdata);
694 : :
695 : 1 : assert_query_succeeds ("example.org", G_RESOLVER_RECORD_TXT, answer,
696 : : "(['some test content', 'more test content'],)");
697 : :
698 : 1 : g_byte_array_free (answer, TRUE);
699 : : #endif
700 : 1 : }
701 : :
702 : : static void
703 : 1 : test_txt_invalid_empty (void)
704 : : {
705 : : #ifndef HAVE_DN_COMP
706 : : g_test_skip ("The dn_comp() function was not available.");
707 : : return;
708 : : #else
709 : 1 : GByteArray *answer = NULL, *txt_rdata = NULL;
710 : :
711 : 1 : answer = dns_header ();
712 : :
713 : : /* Resource record */
714 : 1 : dns_builder_add_domain (answer, "example.org");
715 : 1 : dns_builder_add_uint16 (answer, g_resolver_record_type_to_rrtype (G_RESOLVER_RECORD_TXT));
716 : 1 : dns_builder_add_uint16 (answer, 1); /* qclass=C_IN */
717 : 1 : dns_builder_add_uint32 (answer, 0); /* ttl (ignored) */
718 : :
719 : : /* TXT rdata, https://datatracker.ietf.org/doc/html/rfc1035#section-3.3.14
720 : : *
721 : : * Provide zero character strings (i.e. an empty rdata section) to trigger
722 : : * failure. */
723 : 1 : txt_rdata = g_byte_array_new ();
724 : 1 : dns_builder_add_answer_data (answer, txt_rdata);
725 : 1 : g_byte_array_unref (txt_rdata);
726 : :
727 : 1 : assert_query_fails ("example.org", G_RESOLVER_RECORD_TXT, answer);
728 : :
729 : 1 : g_byte_array_free (answer, TRUE);
730 : : #endif
731 : 1 : }
732 : :
733 : : static void
734 : 1 : test_txt_invalid_overflow (void)
735 : : {
736 : : #ifndef HAVE_DN_COMP
737 : : g_test_skip ("The dn_comp() function was not available.");
738 : : return;
739 : : #else
740 : 1 : GByteArray *answer = NULL, *txt_rdata = NULL;
741 : :
742 : 1 : answer = dns_header ();
743 : :
744 : : /* Resource record */
745 : 1 : dns_builder_add_domain (answer, "example.org");
746 : 1 : dns_builder_add_uint16 (answer, g_resolver_record_type_to_rrtype (G_RESOLVER_RECORD_TXT));
747 : 1 : dns_builder_add_uint16 (answer, 1); /* qclass=C_IN */
748 : 1 : dns_builder_add_uint32 (answer, 0); /* ttl (ignored) */
749 : :
750 : : /* TXT rdata, https://datatracker.ietf.org/doc/html/rfc1035#section-3.3.14
751 : : *
752 : : * Use a character string whose length exceeds the remaining length in the
753 : : * answer record, to trigger failure. */
754 : 1 : txt_rdata = g_byte_array_new ();
755 : 1 : dns_builder_add_uint8 (txt_rdata, 10); /* length, but no content */
756 : 1 : dns_builder_add_answer_data (answer, txt_rdata);
757 : 1 : g_byte_array_unref (txt_rdata);
758 : :
759 : 1 : assert_query_fails ("example.org", G_RESOLVER_RECORD_TXT, answer);
760 : :
761 : 1 : g_byte_array_free (answer, TRUE);
762 : : #endif
763 : 1 : }
764 : :
765 : : static void
766 : 1 : test_srv_valid (void)
767 : : {
768 : : #ifndef HAVE_DN_COMP
769 : : g_test_skip ("The dn_comp() function was not available.");
770 : : return;
771 : : #else
772 : 1 : GByteArray *answer = NULL, *srv_rdata = NULL;
773 : :
774 : 1 : answer = dns_header ();
775 : :
776 : : /* Resource record */
777 : 1 : dns_builder_add_domain (answer, "example.org");
778 : 1 : dns_builder_add_uint16 (answer, g_resolver_record_type_to_rrtype (G_RESOLVER_RECORD_SRV));
779 : 1 : dns_builder_add_uint16 (answer, 1); /* qclass=C_IN */
780 : 1 : dns_builder_add_uint32 (answer, 0); /* ttl (ignored) */
781 : :
782 : : /* SRV rdata, https://datatracker.ietf.org/doc/html/rfc2782 */
783 : 1 : srv_rdata = g_byte_array_new ();
784 : 1 : dns_builder_add_uint16 (srv_rdata, 0); /* priority */
785 : 1 : dns_builder_add_uint16 (srv_rdata, 0); /* weight */
786 : 1 : dns_builder_add_uint16 (srv_rdata, 0); /* port */
787 : 1 : dns_builder_add_domain (srv_rdata, "target.example.org");
788 : 1 : dns_builder_add_answer_data (answer, srv_rdata);
789 : 1 : g_byte_array_unref (srv_rdata);
790 : :
791 : 1 : assert_query_succeeds ("example.org", G_RESOLVER_RECORD_SRV, answer,
792 : : "(@q 0, @q 0, @q 0, 'target.example.org')");
793 : :
794 : 1 : g_byte_array_free (answer, TRUE);
795 : : #endif
796 : 1 : }
797 : :
798 : : static void
799 : 1 : test_srv_invalid (void)
800 : : {
801 : : #ifndef HAVE_DN_COMP
802 : : g_test_skip ("The dn_comp() function was not available.");
803 : : return;
804 : : #else
805 : 1 : GByteArray *answer = NULL, *srv_rdata = NULL;
806 : :
807 : 1 : answer = dns_header ();
808 : :
809 : : /* Resource record */
810 : 1 : dns_builder_add_domain (answer, "example.org");
811 : 1 : dns_builder_add_uint16 (answer, g_resolver_record_type_to_rrtype (G_RESOLVER_RECORD_SRV));
812 : 1 : dns_builder_add_uint16 (answer, 1); /* qclass=C_IN */
813 : 1 : dns_builder_add_uint32 (answer, 0); /* ttl (ignored) */
814 : :
815 : : /* SRV rdata, https://datatracker.ietf.org/doc/html/rfc2782
816 : : *
817 : : * Use an invalid domain to trigger parsing failure. */
818 : 1 : srv_rdata = g_byte_array_new ();
819 : 1 : dns_builder_add_uint16 (srv_rdata, 0); /* priority */
820 : 1 : dns_builder_add_uint16 (srv_rdata, 0); /* weight */
821 : 1 : dns_builder_add_uint16 (srv_rdata, 0); /* port */
822 : 1 : dns_builder_add_invalid_domain (srv_rdata, answer->len + 2);
823 : 1 : dns_builder_add_answer_data (answer, srv_rdata);
824 : 1 : g_byte_array_unref (srv_rdata);
825 : :
826 : 1 : assert_query_fails ("example.org", G_RESOLVER_RECORD_SRV, answer);
827 : :
828 : 1 : g_byte_array_free (answer, TRUE);
829 : : #endif
830 : 1 : }
831 : :
832 : : static void
833 : 1 : test_srv_invalid_too_short (void)
834 : : {
835 : : #ifndef HAVE_DN_COMP
836 : : g_test_skip ("The dn_comp() function was not available.");
837 : : return;
838 : : #else
839 : 1 : GByteArray *answer = NULL, *srv_rdata = NULL;
840 : :
841 : 1 : answer = dns_header ();
842 : :
843 : : /* Resource record */
844 : 1 : dns_builder_add_domain (answer, "example.org");
845 : 1 : dns_builder_add_uint16 (answer, g_resolver_record_type_to_rrtype (G_RESOLVER_RECORD_SRV));
846 : 1 : dns_builder_add_uint16 (answer, 1); /* qclass=C_IN */
847 : 1 : dns_builder_add_uint32 (answer, 0); /* ttl (ignored) */
848 : :
849 : : /* SRV rdata, https://datatracker.ietf.org/doc/html/rfc2782
850 : : *
851 : : * Miss out the target field to trigger failure */
852 : 1 : srv_rdata = g_byte_array_new ();
853 : 1 : dns_builder_add_uint16 (srv_rdata, 0); /* priority */
854 : 1 : dns_builder_add_uint16 (srv_rdata, 0); /* weight */
855 : 1 : dns_builder_add_uint16 (srv_rdata, 0); /* port */
856 : : /* missing target field */
857 : 1 : dns_builder_add_answer_data (answer, srv_rdata);
858 : 1 : g_byte_array_unref (srv_rdata);
859 : :
860 : 1 : assert_query_fails ("example.org", G_RESOLVER_RECORD_SRV, answer);
861 : :
862 : 1 : g_byte_array_free (answer, TRUE);
863 : : #endif
864 : 1 : }
865 : :
866 : : static void
867 : 1 : test_srv_invalid_too_short2 (void)
868 : : {
869 : : #ifndef HAVE_DN_COMP
870 : : g_test_skip ("The dn_comp() function was not available.");
871 : : return;
872 : : #else
873 : 1 : GByteArray *answer = NULL, *srv_rdata = NULL;
874 : :
875 : 1 : answer = dns_header ();
876 : :
877 : : /* Resource record */
878 : 1 : dns_builder_add_domain (answer, "example.org");
879 : 1 : dns_builder_add_uint16 (answer, g_resolver_record_type_to_rrtype (G_RESOLVER_RECORD_SRV));
880 : 1 : dns_builder_add_uint16 (answer, 1); /* qclass=C_IN */
881 : 1 : dns_builder_add_uint32 (answer, 0); /* ttl (ignored) */
882 : :
883 : : /* SRV rdata, https://datatracker.ietf.org/doc/html/rfc2782
884 : : *
885 : : * Miss out the target and port fields to trigger failure */
886 : 1 : srv_rdata = g_byte_array_new ();
887 : 1 : dns_builder_add_uint16 (srv_rdata, 0); /* priority */
888 : 1 : dns_builder_add_uint16 (srv_rdata, 0); /* weight */
889 : : /* missing port and target fields */
890 : 1 : dns_builder_add_answer_data (answer, srv_rdata);
891 : 1 : g_byte_array_unref (srv_rdata);
892 : :
893 : 1 : assert_query_fails ("example.org", G_RESOLVER_RECORD_SRV, answer);
894 : :
895 : 1 : g_byte_array_free (answer, TRUE);
896 : : #endif
897 : 1 : }
898 : :
899 : : int
900 : 1 : main (int argc,
901 : : char *argv[])
902 : : {
903 : 1 : g_test_init (&argc, &argv, G_TEST_OPTION_ISOLATE_DIRS, NULL);
904 : :
905 : 1 : g_test_add_func ("/gresolver/invalid-header", test_invalid_header);
906 : 1 : g_test_add_func ("/gresolver/record-ownership", test_record_ownership);
907 : 1 : g_test_add_func ("/gresolver/unknown-record-type", test_unknown_record_type);
908 : 1 : g_test_add_func ("/gresolver/mx/valid", test_mx_valid);
909 : 1 : g_test_add_func ("/gresolver/mx/invalid", test_mx_invalid);
910 : 1 : g_test_add_func ("/gresolver/mx/invalid/too-short", test_mx_invalid_too_short);
911 : 1 : g_test_add_func ("/gresolver/mx/invalid/too-short2", test_mx_invalid_too_short2);
912 : 1 : g_test_add_func ("/gresolver/ns/valid", test_ns_valid);
913 : 1 : g_test_add_func ("/gresolver/ns/invalid", test_ns_invalid);
914 : 1 : g_test_add_func ("/gresolver/soa/valid", test_soa_valid);
915 : 1 : g_test_add_func ("/gresolver/soa/invalid/mname", test_soa_invalid_mname);
916 : 1 : g_test_add_func ("/gresolver/soa/invalid/rname", test_soa_invalid_rname);
917 : 1 : g_test_add_func ("/gresolver/soa/invalid/too-short", test_soa_invalid_too_short);
918 : 1 : g_test_add_func ("/gresolver/srv/valid", test_srv_valid);
919 : 1 : g_test_add_func ("/gresolver/srv/invalid", test_srv_invalid);
920 : 1 : g_test_add_func ("/gresolver/srv/invalid/too-short", test_srv_invalid_too_short);
921 : 1 : g_test_add_func ("/gresolver/srv/invalid/too-short2", test_srv_invalid_too_short2);
922 : 1 : g_test_add_func ("/gresolver/txt/valid", test_txt_valid);
923 : 1 : g_test_add_func ("/gresolver/txt/valid/multiple-strings", test_txt_valid_multiple_strings);
924 : 1 : g_test_add_func ("/gresolver/txt/invalid/empty", test_txt_invalid_empty);
925 : 1 : g_test_add_func ("/gresolver/txt/invalid/overflow", test_txt_invalid_overflow);
926 : :
927 : 1 : return g_test_run ();
928 : : }
|