]>
Commit | Line | Data |
---|---|---|
f282cdbe | 1 | /* Implement suppression of AAAA queries. |
6d7e8eda | 2 | Copyright (C) 2022-2023 Free Software Foundation, Inc. |
f282cdbe FW |
3 | This file is part of the GNU C Library. |
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 <resolv.h> | |
20 | #include <string.h> | |
21 | #include <resolv-internal.h> | |
22 | #include <resolv_context.h> | |
23 | #include <arpa/nameser.h> | |
24 | ||
25 | /* Returns true if the question type at P matches EXPECTED, and the | |
26 | class is IN. */ | |
27 | static bool | |
28 | qtype_matches (const unsigned char *p, int expected) | |
29 | { | |
30 | /* This assumes that T_A/C_IN constants are less than 256, which | |
31 | they are. */ | |
32 | return p[0] == 0 && p[1] == expected && p[2] == 0 && p[3] == C_IN; | |
33 | } | |
34 | ||
35 | /* Handle RES_NOAAAA translation of AAAA queries. To produce a Name | |
36 | Error (NXDOMAIN) repsonse for domain names that do not exist, it is | |
37 | still necessary to send a query. Using question type A is a | |
38 | conservative choice. In the returned answer, it is necessary to | |
39 | switch back the question type to AAAA. */ | |
40 | bool | |
41 | __res_handle_no_aaaa (struct resolv_context *ctx, | |
42 | const unsigned char *buf, int buflen, | |
43 | unsigned char *ans, int anssiz, int *result) | |
44 | { | |
45 | /* AAAA mode is not active, or the query looks invalid (will not be | |
46 | able to be parsed). */ | |
47 | if ((ctx->resp->options & RES_NOAAAA) == 0 | |
48 | || buflen <= sizeof (HEADER)) | |
49 | return false; | |
50 | ||
51 | /* The replacement A query is produced here. */ | |
52 | struct | |
53 | { | |
54 | HEADER header; | |
55 | unsigned char question[NS_MAXCDNAME + 4]; | |
56 | } replacement; | |
57 | memcpy (&replacement.header, buf, sizeof (replacement.header)); | |
58 | ||
59 | if (replacement.header.qr | |
60 | || replacement.header.opcode != 0 | |
61 | || replacement.header.rcode != 0 | |
62 | || ntohs (replacement.header.qdcount) != 1 | |
63 | || ntohs (replacement.header.ancount) != 0 | |
64 | || ntohs (replacement.header.nscount) != 0) | |
65 | /* Not a well-formed question. Let the core resolver code produce | |
66 | the proper error. */ | |
67 | return false; | |
68 | ||
69 | /* Disable EDNS0. */ | |
70 | replacement.header.arcount = htons (0); | |
71 | ||
72 | /* Extract the QNAME. */ | |
73 | int ret = __ns_name_unpack (buf, buf + buflen, buf + sizeof (HEADER), | |
74 | replacement.question, NS_MAXCDNAME); | |
75 | if (ret < 0) | |
76 | /* Format error. */ | |
77 | return false; | |
78 | ||
79 | /* Compute the end of the question name. */ | |
80 | const unsigned char *after_question = buf + sizeof (HEADER) + ret; | |
81 | ||
82 | /* Check that we are dealing with an AAAA query. */ | |
83 | if (buf + buflen - after_question < 4 | |
84 | || !qtype_matches (after_question, T_AAAA)) | |
85 | return false; | |
86 | ||
87 | /* Find the place to store the type/class data in the replacement | |
88 | query. */ | |
89 | after_question = replacement.question; | |
90 | /* This cannot fail because __ns_name_unpack above produced a valid | |
91 | domain name. */ | |
92 | (void) __ns_name_skip (&after_question, &replacement.question[NS_MAXCDNAME]); | |
93 | unsigned char *start_of_query = (unsigned char *) &replacement; | |
94 | const unsigned char *end_of_query = after_question + 4; | |
95 | ||
96 | /* Produce an A/IN query. */ | |
97 | { | |
98 | unsigned char *p = (unsigned char *) after_question; | |
99 | p[0] = 0; | |
100 | p[1] = T_A; | |
101 | p[2] = 0; | |
102 | p[3] = C_IN; | |
103 | } | |
104 | ||
105 | /* Clear the output buffer, to avoid reading undefined data when | |
106 | rewriting the result from A to AAAA. */ | |
107 | memset (ans, 0, anssiz); | |
108 | ||
109 | /* Always perform the message translation, independent of the error | |
110 | code. */ | |
111 | ret = __res_context_send (ctx, | |
112 | start_of_query, end_of_query - start_of_query, | |
113 | NULL, 0, ans, anssiz, | |
114 | NULL, NULL, NULL, NULL, NULL); | |
115 | ||
116 | /* Patch in the AAAA question type if there is room and the A query | |
117 | type was received. */ | |
118 | after_question = ans + sizeof (HEADER); | |
119 | if (__ns_name_skip (&after_question, ans + anssiz) == 0 | |
120 | && ans + anssiz - after_question >= 4 | |
121 | && qtype_matches (after_question, T_A)) | |
122 | { | |
123 | ((unsigned char *) after_question)[1] = T_AAAA; | |
124 | ||
125 | /* Create an aligned copy of the header. Hide all data except | |
126 | the question from the response. Put back the header. There is | |
127 | no need to change the response code. The zero answer count turns | |
128 | a positive response with data into a no-data response. */ | |
129 | memcpy (&replacement.header, ans, sizeof (replacement.header)); | |
130 | replacement.header.ancount = htons (0); | |
131 | replacement.header.nscount = htons (0); | |
132 | replacement.header.arcount = htons (0); | |
133 | memcpy (ans, &replacement.header, sizeof (replacement.header)); | |
134 | ||
135 | /* Truncate the reply. */ | |
136 | if (ret <= 0) | |
137 | *result = ret; | |
138 | else | |
139 | *result = after_question - ans + 4; | |
140 | } | |
141 | ||
142 | return true; | |
143 | } |