Change-Id: I9d07bfd1bfd0c6c431db00ab7c1ab25a4d3c495b
Reviewed-on: http://photon-jenkins.eng.vmware.com:8082/6614
Tested-by: gerrit-photon <photon-checkins@vmware.com>
Reviewed-by: Alexey Makhalov <amakhalov@vmware.com>
| 1 | 1 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,2134 @@ |
| 0 |
+From e14a27723cc3a154d67f3f26e719d08c0ba9ad25 Mon Sep 17 00:00:00 2001 |
|
| 1 |
+From: Florian Weimer <fweimer@redhat.com> |
|
| 2 |
+Date: Thu, 13 Apr 2017 13:09:38 +0200 |
|
| 3 |
+Subject: [PATCH 1/1] resolv: Reduce EDNS payload size to 1200 bytes [BZ |
|
| 4 |
+ #21361] |
|
| 5 |
+ |
|
| 6 |
+This hardens the stub resolver against fragmentation-based attacks. |
|
| 7 |
+--- |
|
| 8 |
+ ChangeLog | 21 ++ |
|
| 9 |
+ NEWS | 3 +- |
|
| 10 |
+ include/resolv.h | 3 - |
|
| 11 |
+ resolv/Makefile | 2 + |
|
| 12 |
+ resolv/res_mkquery.c | 28 ++- |
|
| 13 |
+ resolv/res_query.c | 23 ++- |
|
| 14 |
+ resolv/resolv-internal.h | 18 ++ |
|
| 15 |
+ resolv/tst-resolv-edns.c | 501 +++++++++++++++++++++++++++++++++++++++++++++++ |
|
| 16 |
+ support/resolv_test.c | 56 +++++- |
|
| 17 |
+ support/resolv_test.h | 11 ++ |
|
| 18 |
+ 10 files changed, 652 insertions(+), 14 deletions(-) |
|
| 19 |
+ create mode 100644 resolv/tst-resolv-edns.c |
|
| 20 |
+ |
|
| 21 |
+diff --git a/include/resolv.h b/include/resolv.h |
|
| 22 |
+index 95dcd3c..e8f477c 100644 |
|
| 23 |
+--- a/include/resolv.h |
|
| 24 |
+@@ -38,8 +38,6 @@ |
|
| 25 |
+ extern int res_ourserver_p (const res_state __statp, |
|
| 26 |
+ const struct sockaddr_in6 *__inp); |
|
| 27 |
+ extern void __res_iclose (res_state statp, bool free_addr); |
|
| 28 |
+-extern int __res_nopt(res_state statp, int n0, u_char *buf, int buflen, |
|
| 29 |
+- int anslen); |
|
| 30 |
+ libc_hidden_proto (__res_ninit) |
|
| 31 |
+ libc_hidden_proto (__res_maybe_init) |
|
| 32 |
+ libc_hidden_proto (__res_nclose) |
|
| 33 |
+@@ -91,7 +89,6 @@ libresolv_hidden_proto (__res_nameinquery) |
|
| 34 |
+ libresolv_hidden_proto (__res_queriesmatch) |
|
| 35 |
+ libresolv_hidden_proto (__res_nsend) |
|
| 36 |
+ libresolv_hidden_proto (__b64_ntop) |
|
| 37 |
+-libresolv_hidden_proto (__res_nopt) |
|
| 38 |
+ libresolv_hidden_proto (__dn_count_labels) |
|
| 39 |
+ libresolv_hidden_proto (__p_secstodate) |
|
| 40 |
+ |
|
| 41 |
+diff --git a/resolv/Makefile b/resolv/Makefile |
|
| 42 |
+index c69b24b..d41fd46 100644 |
|
| 43 |
+--- a/resolv/Makefile |
|
| 44 |
+@@ -39,7 +39,9 @@ |
|
| 45 |
+ ifeq ($(have-thread-library),yes) |
|
| 46 |
+ extra-libs += libanl |
|
| 47 |
+ routines += gai_sigqueue |
|
| 48 |
+-tests += tst-res_hconf_reorder |
|
| 49 |
++tests += \ |
|
| 50 |
++ tst-res_hconf_reorder \ |
|
| 51 |
++ tst-resolv-edns |
|
| 52 |
+ |
|
| 53 |
+ # This test sends millions of packets and is rather slow. |
|
| 54 |
+ xtests += tst-resolv-qtypes |
|
| 55 |
+@@ -124,3 +126,4 @@ |
|
| 56 |
+ $(evaluate-test) |
|
| 57 |
+ |
|
| 58 |
+ $(objpfx)tst-resolv-qtypes: $(objpfx)libresolv.so $(shared-thread-library) |
|
| 59 |
++$(objpfx)tst-resolv-edns: $(objpfx)libresolv.so $(shared-thread-library) |
|
| 60 |
+diff --git a/resolv/res_mkquery.c b/resolv/res_mkquery.c |
|
| 61 |
+index 4532b58..8279d15 100644 |
|
| 62 |
+--- a/resolv/res_mkquery.c |
|
| 63 |
+@@ -69,7 +69,7 @@ |
|
| 64 |
+ #include <netinet/in.h> |
|
| 65 |
+ #include <arpa/nameser.h> |
|
| 66 |
+ #include <netdb.h> |
|
| 67 |
+-#include <resolv.h> |
|
| 68 |
++#include <resolv/resolv-internal.h> |
|
| 69 |
+ #include <stdio.h> |
|
| 70 |
+ #include <string.h> |
|
| 71 |
+ #include <sys/time.h> |
|
| 72 |
+@@ -225,7 +225,30 @@ __res_nopt(res_state statp, |
|
| 73 |
+ *cp++ = 0; /* "." */ |
|
| 74 |
+ |
|
| 75 |
+ NS_PUT16(T_OPT, cp); /* TYPE */ |
|
| 76 |
+- NS_PUT16(MIN(anslen, 0xffff), cp); /* CLASS = UDP payload size */ |
|
| 77 |
++ |
|
| 78 |
++ /* Lowering the advertised buffer size based on the actual |
|
| 79 |
++ answer buffer size is desirable because the server will |
|
| 80 |
++ minimize the reply to fit into the UDP packet (and A |
|
| 81 |
++ non-minimal response might not fit the buffer). |
|
| 82 |
++ |
|
| 83 |
++ The RESOLV_EDNS_BUFFER_SIZE limit could still result in TCP |
|
| 84 |
++ fallback and a non-minimal response which has to be |
|
| 85 |
++ hard-truncated in the stub resolver, but this is price to |
|
| 86 |
++ pay for avoiding fragmentation. (This issue does not |
|
| 87 |
++ affect the nss_dns functions because they use the stub |
|
| 88 |
++ resolver in such a way that it allocates a properly sized |
|
| 89 |
++ response buffer.) */ |
|
| 90 |
++ {
|
|
| 91 |
++ uint16_t buffer_size; |
|
| 92 |
++ if (anslen < 512) |
|
| 93 |
++ buffer_size = 512; |
|
| 94 |
++ else if (anslen > RESOLV_EDNS_BUFFER_SIZE) |
|
| 95 |
++ buffer_size = RESOLV_EDNS_BUFFER_SIZE; |
|
| 96 |
++ else |
|
| 97 |
++ buffer_size = anslen; |
|
| 98 |
++ NS_PUT16 (buffer_size, cp); |
|
| 99 |
++ } |
|
| 100 |
++ |
|
| 101 |
+ *cp++ = NOERROR; /* extended RCODE */ |
|
| 102 |
+ *cp++ = 0; /* EDNS version */ |
|
| 103 |
+ |
|
| 104 |
+@@ -243,4 +266,3 @@ __res_nopt(res_state statp, |
|
| 105 |
+ |
|
| 106 |
+ return cp - buf; |
|
| 107 |
+ } |
|
| 108 |
+-libresolv_hidden_def (__res_nopt) |
|
| 109 |
+diff --git a/resolv/res_query.c b/resolv/res_query.c |
|
| 110 |
+index 6f3eada..c3ebcbf 100644 |
|
| 111 |
+--- a/resolv/res_query.c |
|
| 112 |
+@@ -78,6 +78,7 @@ |
|
| 113 |
+ #include <stdio.h> |
|
| 114 |
+ #include <stdlib.h> |
|
| 115 |
+ #include <string.h> |
|
| 116 |
++#include <resolv/resolv-internal.h> |
|
| 117 |
+ |
|
| 118 |
+ /* Options. Leave them on. */ |
|
| 119 |
+ /* #undef DEBUG */ |
|
| 120 |
+@@ -147,7 +148,10 @@ __libc_res_nquery(res_state statp, |
|
| 121 |
+ if ((oflags & RES_F_EDNS0ERR) == 0 |
|
| 122 |
+ && (statp->options & (RES_USE_EDNS0|RES_USE_DNSSEC)) != 0) |
|
| 123 |
+ {
|
|
| 124 |
+- n = __res_nopt(statp, n, query1, bufsize, anslen / 2); |
|
| 125 |
++ /* Use RESOLV_EDNS_BUFFER_SIZE because the receive |
|
| 126 |
++ buffer can be reallocated. */ |
|
| 127 |
++ n = __res_nopt (statp, n, query1, bufsize, |
|
| 128 |
++ RESOLV_EDNS_BUFFER_SIZE); |
|
| 129 |
+ if (n < 0) |
|
| 130 |
+ goto unspec_nomem; |
|
| 131 |
+ } |
|
| 132 |
+@@ -168,8 +172,10 @@ __libc_res_nquery(res_state statp, |
|
| 133 |
+ if (n > 0 |
|
| 134 |
+ && (oflags & RES_F_EDNS0ERR) == 0 |
|
| 135 |
+ && (statp->options & (RES_USE_EDNS0|RES_USE_DNSSEC)) != 0) |
|
| 136 |
+- n = __res_nopt(statp, n, query2, bufsize - nused - n, |
|
| 137 |
+- anslen / 2); |
|
| 138 |
++ /* Use RESOLV_EDNS_BUFFER_SIZE because the receive |
|
| 139 |
++ buffer can be reallocated. */ |
|
| 140 |
++ n = __res_nopt (statp, n, query2, bufsize, |
|
| 141 |
++ RESOLV_EDNS_BUFFER_SIZE); |
|
| 142 |
+ nquery2 = n; |
|
| 143 |
+ } |
|
| 144 |
+ |
|
| 145 |
+@@ -183,7 +189,16 @@ __libc_res_nquery(res_state statp, |
|
| 146 |
+ if (n > 0 |
|
| 147 |
+ && (oflags & RES_F_EDNS0ERR) == 0 |
|
| 148 |
+ && (statp->options & (RES_USE_EDNS0|RES_USE_DNSSEC)) != 0) |
|
| 149 |
+- n = __res_nopt(statp, n, query1, bufsize, anslen); |
|
| 150 |
++ {
|
|
| 151 |
++ /* Use RESOLV_EDNS_BUFFER_SIZE if the receive buffer |
|
| 152 |
++ can be reallocated. */ |
|
| 153 |
++ size_t advertise; |
|
| 154 |
++ if (answerp == NULL) |
|
| 155 |
++ advertise = anslen; |
|
| 156 |
++ else |
|
| 157 |
++ advertise = RESOLV_EDNS_BUFFER_SIZE; |
|
| 158 |
++ n = __res_nopt (statp, n, query1, bufsize, advertise); |
|
| 159 |
++ } |
|
| 160 |
+ |
|
| 161 |
+ nquery1 = n; |
|
| 162 |
+ } |
|
| 163 |
+diff --git a/resolv/resolv-internal.h b/resolv/resolv-internal.h |
|
| 164 |
+new file mode 100644 |
|
| 165 |
+index d35df1c..0d69ce1 |
|
| 166 |
+--- /dev/null |
|
| 167 |
+@@ -0,0 +1,59 @@ |
|
| 168 |
++/* libresolv interfaces for internal use across glibc. |
|
| 169 |
++ Copyright (C) 2016-2017 Free Software Foundation, Inc. |
|
| 170 |
++ This file is part of the GNU C Library. |
|
| 171 |
++ |
|
| 172 |
++ The GNU C Library is free software; you can redistribute it and/or |
|
| 173 |
++ modify it under the terms of the GNU Lesser General Public |
|
| 174 |
++ License as published by the Free Software Foundation; either |
|
| 175 |
++ version 2.1 of the License, or (at your option) any later version. |
|
| 176 |
++ |
|
| 177 |
++ The GNU C Library is distributed in the hope that it will be useful, |
|
| 178 |
++ but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
| 179 |
++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
|
| 180 |
++ Lesser General Public License for more details. |
|
| 181 |
++ |
|
| 182 |
++ You should have received a copy of the GNU Lesser General Public |
|
| 183 |
++ License along with the GNU C Library; if not, see |
|
| 184 |
++ <http://www.gnu.org/licenses/>. */ |
|
| 185 |
++ |
|
| 186 |
++#ifndef _RESOLV_INTERNAL_H |
|
| 187 |
++#define _RESOLV_INTERNAL_H 1 |
|
| 188 |
++ |
|
| 189 |
++#include <resolv.h> |
|
| 190 |
++#include <stdbool.h> |
|
| 191 |
++ |
|
| 192 |
++/* Resolver flags. Used for _flags in struct __res_state. */ |
|
| 193 |
++#define RES_F_VC 0x00000001 /* Socket is TCP. */ |
|
| 194 |
++#define RES_F_CONN 0x00000002 /* Socket is connected. */ |
|
| 195 |
++#define RES_F_EDNS0ERR 0x00000004 /* EDNS0 caused errors. */ |
|
| 196 |
++ |
|
| 197 |
++ |
|
| 198 |
++/* Internal version of RES_USE_INET6 which does not trigger a |
|
| 199 |
++ deprecation warning. */ |
|
| 200 |
++#define DEPRECATED_RES_USE_INET6 0x00002000 |
|
| 201 |
++ |
|
| 202 |
++static inline bool |
|
| 203 |
++res_use_inet6 (void) |
|
| 204 |
++{
|
|
| 205 |
++ return _res.options & DEPRECATED_RES_USE_INET6; |
|
| 206 |
++} |
|
| 207 |
++ |
|
| 208 |
++enum |
|
| 209 |
++ {
|
|
| 210 |
++ /* The advertized EDNS buffer size. The value 1200 is derived |
|
| 211 |
++ from the IPv6 minimum MTU (1280 bytes) minus some arbitrary |
|
| 212 |
++ space for tunneling overhead. If the DNS server does not react |
|
| 213 |
++ to ICMP Fragmentation Needed But DF Set messages, this should |
|
| 214 |
++ avoid all UDP fragments on current networks. Avoiding UDP |
|
| 215 |
++ fragments is desirable because it prevents fragmentation-based |
|
| 216 |
++ spoofing attacks because the randomness in a DNS packet is |
|
| 217 |
++ concentrated in the first fragment (with the headers) and does |
|
| 218 |
++ not protect subsequent fragments. */ |
|
| 219 |
++ RESOLV_EDNS_BUFFER_SIZE = 1200, |
|
| 220 |
++ }; |
|
| 221 |
++ |
|
| 222 |
++/* Add an OPT record to a DNS query. */ |
|
| 223 |
++int __res_nopt (res_state, int n0, unsigned char *buf, int buflen, |
|
| 224 |
++ int anslen) attribute_hidden; |
|
| 225 |
++ |
|
| 226 |
++#endif /* _RESOLV_INTERNAL_H */ |
|
| 227 |
+diff --git a/resolv/tst-resolv-edns.c b/resolv/tst-resolv-edns.c |
|
| 228 |
+new file mode 100644 |
|
| 229 |
+index 0000000..f17dbc3 |
|
| 230 |
+--- /dev/null |
|
| 231 |
+@@ -0,0 +1,501 @@ |
|
| 232 |
++/* Test EDNS handling in the stub resolver. |
|
| 233 |
++ Copyright (C) 2016-2017 Free Software Foundation, Inc. |
|
| 234 |
++ This file is part of the GNU C Library. |
|
| 235 |
++ |
|
| 236 |
++ The GNU C Library is free software; you can redistribute it and/or |
|
| 237 |
++ modify it under the terms of the GNU Lesser General Public |
|
| 238 |
++ License as published by the Free Software Foundation; either |
|
| 239 |
++ version 2.1 of the License, or (at your option) any later version. |
|
| 240 |
++ |
|
| 241 |
++ The GNU C Library is distributed in the hope that it will be useful, |
|
| 242 |
++ but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
| 243 |
++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
|
| 244 |
++ Lesser General Public License for more details. |
|
| 245 |
++ |
|
| 246 |
++ You should have received a copy of the GNU Lesser General Public |
|
| 247 |
++ License along with the GNU C Library; if not, see |
|
| 248 |
++ <http://www.gnu.org/licenses/>. */ |
|
| 249 |
++ |
|
| 250 |
++#include <errno.h> |
|
| 251 |
++#include <netdb.h> |
|
| 252 |
++#include <stdio.h> |
|
| 253 |
++#include <stdlib.h> |
|
| 254 |
++#include <string.h> |
|
| 255 |
++#include <support/check.h> |
|
| 256 |
++#include <support/resolv_test.h> |
|
| 257 |
++#include <support/support.h> |
|
| 258 |
++#include <support/test-driver.h> |
|
| 259 |
++#include <support/xthread.h> |
|
| 260 |
++ |
|
| 261 |
++/* Data produced by a test query. */ |
|
| 262 |
++struct response_data |
|
| 263 |
++{
|
|
| 264 |
++ char *qname; |
|
| 265 |
++ uint16_t qtype; |
|
| 266 |
++ struct resolv_edns_info edns; |
|
| 267 |
++}; |
|
| 268 |
++ |
|
| 269 |
++/* Global array used by put_response and get_response to record |
|
| 270 |
++ response data. The test DNS server returns the index of the array |
|
| 271 |
++ element which contains the actual response data. This enables the |
|
| 272 |
++ test case to return arbitrary amounts of data with the limited |
|
| 273 |
++ number of bits which fit into an IP addres. |
|
| 274 |
++ |
|
| 275 |
++ The volatile specifier is needed because the test case accesses |
|
| 276 |
++ these variables from a callback function called from a function |
|
| 277 |
++ which is marked as __THROW (i.e., a leaf function which actually is |
|
| 278 |
++ not). */ |
|
| 279 |
++static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; |
|
| 280 |
++static struct response_data ** volatile response_data_array; |
|
| 281 |
++volatile static size_t response_data_count; |
|
| 282 |
++ |
|
| 283 |
++/* Extract information from the query, store it in a struct |
|
| 284 |
++ response_data object, and return its index in the |
|
| 285 |
++ response_data_array. */ |
|
| 286 |
++static unsigned int |
|
| 287 |
++put_response (const struct resolv_response_context *ctx, |
|
| 288 |
++ const char *qname, uint16_t qtype) |
|
| 289 |
++{
|
|
| 290 |
++ xpthread_mutex_lock (&mutex); |
|
| 291 |
++ ++response_data_count; |
|
| 292 |
++ /* We only can represent 2**24 indexes in 10.0.0.0/8. */ |
|
| 293 |
++ TEST_VERIFY (response_data_count < (1 << 24)); |
|
| 294 |
++ response_data_array = xrealloc |
|
| 295 |
++ (response_data_array, sizeof (*response_data_array) * response_data_count); |
|
| 296 |
++ unsigned int index = response_data_count - 1; |
|
| 297 |
++ struct response_data *data = xmalloc (sizeof (*data)); |
|
| 298 |
++ *data = (struct response_data) |
|
| 299 |
++ {
|
|
| 300 |
++ .qname = xstrdup (qname), |
|
| 301 |
++ .qtype = qtype, |
|
| 302 |
++ .edns = ctx->edns, |
|
| 303 |
++ }; |
|
| 304 |
++ response_data_array[index] = data; |
|
| 305 |
++ xpthread_mutex_unlock (&mutex); |
|
| 306 |
++ return index; |
|
| 307 |
++} |
|
| 308 |
++ |
|
| 309 |
++/* Verify the index into the response_data array and return the data |
|
| 310 |
++ at it. */ |
|
| 311 |
++static struct response_data * |
|
| 312 |
++get_response (unsigned int index) |
|
| 313 |
++{
|
|
| 314 |
++ xpthread_mutex_lock (&mutex); |
|
| 315 |
++ TEST_VERIFY_EXIT (index < response_data_count); |
|
| 316 |
++ struct response_data *result = response_data_array[index]; |
|
| 317 |
++ xpthread_mutex_unlock (&mutex); |
|
| 318 |
++ return result; |
|
| 319 |
++} |
|
| 320 |
++ |
|
| 321 |
++/* Deallocate all response data. */ |
|
| 322 |
++static void |
|
| 323 |
++free_response_data (void) |
|
| 324 |
++{
|
|
| 325 |
++ xpthread_mutex_lock (&mutex); |
|
| 326 |
++ size_t count = response_data_count; |
|
| 327 |
++ struct response_data **array = response_data_array; |
|
| 328 |
++ for (unsigned int i = 0; i < count; ++i) |
|
| 329 |
++ {
|
|
| 330 |
++ struct response_data *data = array[i]; |
|
| 331 |
++ free (data->qname); |
|
| 332 |
++ free (data); |
|
| 333 |
++ } |
|
| 334 |
++ free (array); |
|
| 335 |
++ response_data_array = NULL; |
|
| 336 |
++ response_data_count = 0; |
|
| 337 |
++ xpthread_mutex_unlock (&mutex); |
|
| 338 |
++} |
|
| 339 |
++ |
|
| 340 |
++#define EDNS_PROBE_EXAMPLE "edns-probe.example" |
|
| 341 |
++ |
|
| 342 |
++static void |
|
| 343 |
++response (const struct resolv_response_context *ctx, |
|
| 344 |
++ struct resolv_response_builder *b, |
|
| 345 |
++ const char *qname, uint16_t qclass, uint16_t qtype) |
|
| 346 |
++{
|
|
| 347 |
++ TEST_VERIFY_EXIT (qname != NULL); |
|
| 348 |
++ |
|
| 349 |
++ /* The "tcp." prefix can be used to request TCP fallback. */ |
|
| 350 |
++ const char *qname_compare = qname; |
|
| 351 |
++ bool force_tcp; |
|
| 352 |
++ if (strncmp ("tcp.", qname_compare, strlen ("tcp.")) == 0)
|
|
| 353 |
++ {
|
|
| 354 |
++ force_tcp = true; |
|
| 355 |
++ qname_compare += strlen ("tcp.");
|
|
| 356 |
++ } |
|
| 357 |
++ else |
|
| 358 |
++ force_tcp = false; |
|
| 359 |
++ |
|
| 360 |
++ enum {edns_probe} requested_qname;
|
|
| 361 |
++ if (strcmp (qname_compare, EDNS_PROBE_EXAMPLE) == 0) |
|
| 362 |
++ requested_qname = edns_probe; |
|
| 363 |
++ else |
|
| 364 |
++ {
|
|
| 365 |
++ support_record_failure (); |
|
| 366 |
++ printf ("error: unexpected QNAME: %s\n", qname);
|
|
| 367 |
++ return; |
|
| 368 |
++ } |
|
| 369 |
++ TEST_VERIFY_EXIT (qclass == C_IN); |
|
| 370 |
++ struct resolv_response_flags flags = {.tc = force_tcp && !ctx->tcp};
|
|
| 371 |
++ resolv_response_init (b, flags); |
|
| 372 |
++ resolv_response_add_question (b, qname, qclass, qtype); |
|
| 373 |
++ if (flags.tc) |
|
| 374 |
++ return; |
|
| 375 |
++ |
|
| 376 |
++ if (test_verbose) |
|
| 377 |
++ printf ("info: edns=%d payload_size=%d\n",
|
|
| 378 |
++ ctx->edns.active, ctx->edns.payload_size); |
|
| 379 |
++ |
|
| 380 |
++ /* Encode the response_data object in multiple address records. |
|
| 381 |
++ Each record carries two bytes of payload data, and an index. */ |
|
| 382 |
++ resolv_response_section (b, ns_s_an); |
|
| 383 |
++ switch (requested_qname) |
|
| 384 |
++ {
|
|
| 385 |
++ case edns_probe: |
|
| 386 |
++ {
|
|
| 387 |
++ unsigned int index = put_response (ctx, qname, qtype); |
|
| 388 |
++ switch (qtype) |
|
| 389 |
++ {
|
|
| 390 |
++ case T_A: |
|
| 391 |
++ {
|
|
| 392 |
++ uint32_t addr = htonl (0x0a000000 | index); |
|
| 393 |
++ resolv_response_open_record (b, qname, qclass, qtype, 0); |
|
| 394 |
++ resolv_response_add_data (b, &addr, sizeof (addr)); |
|
| 395 |
++ resolv_response_close_record (b); |
|
| 396 |
++ } |
|
| 397 |
++ break; |
|
| 398 |
++ case T_AAAA: |
|
| 399 |
++ {
|
|
| 400 |
++ char addr[16] |
|
| 401 |
++ = {0x20, 0x01, 0xd, 0xb8, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
| 402 |
++ index >> 16, index >> 8, index}; |
|
| 403 |
++ resolv_response_open_record (b, qname, qclass, qtype, 0); |
|
| 404 |
++ resolv_response_add_data (b, &addr, sizeof (addr)); |
|
| 405 |
++ resolv_response_close_record (b); |
|
| 406 |
++ } |
|
| 407 |
++ } |
|
| 408 |
++ } |
|
| 409 |
++ break; |
|
| 410 |
++ } |
|
| 411 |
++} |
|
| 412 |
++ |
|
| 413 |
++/* Update *DATA with data from ADDRESS of SIZE. Set the corresponding |
|
| 414 |
++ flag in SHADOW for each byte written. */ |
|
| 415 |
++static struct response_data * |
|
| 416 |
++decode_address (const void *address, size_t size) |
|
| 417 |
++{
|
|
| 418 |
++ switch (size) |
|
| 419 |
++ {
|
|
| 420 |
++ case 4: |
|
| 421 |
++ TEST_VERIFY (memcmp (address, "\x0a", 1) == 0); |
|
| 422 |
++ break; |
|
| 423 |
++ case 16: |
|
| 424 |
++ TEST_VERIFY (memcmp (address, "\x20\x01\x0d\xb8", 4) == 0); |
|
| 425 |
++ break; |
|
| 426 |
++ default: |
|
| 427 |
++ FAIL_EXIT1 ("unexpected address size %zu", size);
|
|
| 428 |
++ } |
|
| 429 |
++ const unsigned char *addr = address; |
|
| 430 |
++ unsigned int index = addr[size - 3] * 256 * 256 |
|
| 431 |
++ + addr[size - 2] * 256 |
|
| 432 |
++ + addr[size - 1]; |
|
| 433 |
++ return get_response (index); |
|
| 434 |
++} |
|
| 435 |
++ |
|
| 436 |
++static struct response_data * |
|
| 437 |
++decode_hostent (struct hostent *e) |
|
| 438 |
++{
|
|
| 439 |
++ TEST_VERIFY_EXIT (e != NULL); |
|
| 440 |
++ TEST_VERIFY_EXIT (e->h_addr_list[0] != NULL); |
|
| 441 |
++ TEST_VERIFY (e->h_addr_list[1] == NULL); |
|
| 442 |
++ return decode_address (e->h_addr_list[0], e->h_length); |
|
| 443 |
++} |
|
| 444 |
++ |
|
| 445 |
++static struct response_data * |
|
| 446 |
++decode_addrinfo (struct addrinfo *ai, int family) |
|
| 447 |
++{
|
|
| 448 |
++ struct response_data *data = NULL; |
|
| 449 |
++ while (ai != NULL) |
|
| 450 |
++ {
|
|
| 451 |
++ if (ai->ai_family == family) |
|
| 452 |
++ {
|
|
| 453 |
++ struct response_data *new_data; |
|
| 454 |
++ switch (family) |
|
| 455 |
++ {
|
|
| 456 |
++ case AF_INET: |
|
| 457 |
++ {
|
|
| 458 |
++ struct sockaddr_in *pin = (struct sockaddr_in *) ai->ai_addr; |
|
| 459 |
++ new_data = decode_address (&pin->sin_addr.s_addr, 4); |
|
| 460 |
++ } |
|
| 461 |
++ break; |
|
| 462 |
++ case AF_INET6: |
|
| 463 |
++ {
|
|
| 464 |
++ struct sockaddr_in6 *pin = (struct sockaddr_in6 *) ai->ai_addr; |
|
| 465 |
++ new_data = decode_address (&pin->sin6_addr.s6_addr, 16); |
|
| 466 |
++ } |
|
| 467 |
++ break; |
|
| 468 |
++ default: |
|
| 469 |
++ FAIL_EXIT1 ("invalid address family %d", ai->ai_family);
|
|
| 470 |
++ } |
|
| 471 |
++ if (data == NULL) |
|
| 472 |
++ data = new_data; |
|
| 473 |
++ else |
|
| 474 |
++ /* Check pointer equality because this should be the same |
|
| 475 |
++ response (same index). */ |
|
| 476 |
++ TEST_VERIFY (data == new_data); |
|
| 477 |
++ } |
|
| 478 |
++ ai = ai->ai_next; |
|
| 479 |
++ } |
|
| 480 |
++ TEST_VERIFY_EXIT (data != NULL); |
|
| 481 |
++ return data; |
|
| 482 |
++} |
|
| 483 |
++ |
|
| 484 |
++/* Updated by the main test loop in accordance with what is set in |
|
| 485 |
++ _res.options. */ |
|
| 486 |
++static bool use_edns; |
|
| 487 |
++static bool use_dnssec; |
|
| 488 |
++ |
|
| 489 |
++/* Verify the decoded response data against the flags above. */ |
|
| 490 |
++static void |
|
| 491 |
++verify_response_data_payload (struct response_data *data, |
|
| 492 |
++ size_t expected_payload) |
|
| 493 |
++{
|
|
| 494 |
++ bool edns = use_edns || use_dnssec; |
|
| 495 |
++ TEST_VERIFY (data->edns.active == edns); |
|
| 496 |
++ if (!edns) |
|
| 497 |
++ expected_payload = 0; |
|
| 498 |
++ if (data->edns.payload_size != expected_payload) |
|
| 499 |
++ {
|
|
| 500 |
++ support_record_failure (); |
|
| 501 |
++ printf ("error: unexpected payload size %d (edns=%d)\n",
|
|
| 502 |
++ (int) data->edns.payload_size, edns); |
|
| 503 |
++ } |
|
| 504 |
++ uint16_t expected_flags = 0; |
|
| 505 |
++ if (use_dnssec) |
|
| 506 |
++ expected_flags |= 0x8000; /* DO flag. */ |
|
| 507 |
++ if (data->edns.flags != expected_flags) |
|
| 508 |
++ {
|
|
| 509 |
++ support_record_failure (); |
|
| 510 |
++ printf ("error: unexpected EDNS flags 0x%04x (edns=%d)\n",
|
|
| 511 |
++ (int) data->edns.flags, edns); |
|
| 512 |
++ } |
|
| 513 |
++} |
|
| 514 |
++ |
|
| 515 |
++/* Same as verify_response_data_payload, but use the default |
|
| 516 |
++ payload. */ |
|
| 517 |
++static void |
|
| 518 |
++verify_response_data (struct response_data *data) |
|
| 519 |
++{
|
|
| 520 |
++ verify_response_data_payload (data, 1200); |
|
| 521 |
++} |
|
| 522 |
++ |
|
| 523 |
++static void |
|
| 524 |
++check_hostent (struct hostent *e) |
|
| 525 |
++{
|
|
| 526 |
++ TEST_VERIFY_EXIT (e != NULL); |
|
| 527 |
++ verify_response_data (decode_hostent (e)); |
|
| 528 |
++} |
|
| 529 |
++ |
|
| 530 |
++static void |
|
| 531 |
++do_ai (int family) |
|
| 532 |
++{
|
|
| 533 |
++ struct addrinfo hints = { .ai_family = family };
|
|
| 534 |
++ struct addrinfo *ai; |
|
| 535 |
++ int ret = getaddrinfo (EDNS_PROBE_EXAMPLE, "80", &hints, &ai); |
|
| 536 |
++ TEST_VERIFY_EXIT (ret == 0); |
|
| 537 |
++ switch (family) |
|
| 538 |
++ {
|
|
| 539 |
++ case AF_INET: |
|
| 540 |
++ case AF_INET6: |
|
| 541 |
++ verify_response_data (decode_addrinfo (ai, family)); |
|
| 542 |
++ break; |
|
| 543 |
++ case AF_UNSPEC: |
|
| 544 |
++ verify_response_data (decode_addrinfo (ai, AF_INET)); |
|
| 545 |
++ verify_response_data (decode_addrinfo (ai, AF_INET6)); |
|
| 546 |
++ break; |
|
| 547 |
++ default: |
|
| 548 |
++ FAIL_EXIT1 ("invalid address family %d", family);
|
|
| 549 |
++ } |
|
| 550 |
++ freeaddrinfo (ai); |
|
| 551 |
++} |
|
| 552 |
++ |
|
| 553 |
++enum res_op |
|
| 554 |
++{
|
|
| 555 |
++ res_op_search, |
|
| 556 |
++ res_op_query, |
|
| 557 |
++ res_op_querydomain, |
|
| 558 |
++ res_op_nsearch, |
|
| 559 |
++ res_op_nquery, |
|
| 560 |
++ res_op_nquerydomain, |
|
| 561 |
++ |
|
| 562 |
++ res_op_last = res_op_nquerydomain, |
|
| 563 |
++}; |
|
| 564 |
++ |
|
| 565 |
++static const char * |
|
| 566 |
++res_op_string (enum res_op op) |
|
| 567 |
++{
|
|
| 568 |
++ switch (op) |
|
| 569 |
++ {
|
|
| 570 |
++ case res_op_search: |
|
| 571 |
++ return "res_search"; |
|
| 572 |
++ case res_op_query: |
|
| 573 |
++ return "res_query"; |
|
| 574 |
++ case res_op_querydomain: |
|
| 575 |
++ return "res_querydomain"; |
|
| 576 |
++ case res_op_nsearch: |
|
| 577 |
++ return "res_nsearch"; |
|
| 578 |
++ case res_op_nquery: |
|
| 579 |
++ return "res_nquery"; |
|
| 580 |
++ case res_op_nquerydomain: |
|
| 581 |
++ return "res_nquerydomain"; |
|
| 582 |
++ } |
|
| 583 |
++ FAIL_EXIT1 ("invalid res_op value %d", (int) op);
|
|
| 584 |
++} |
|
| 585 |
++ |
|
| 586 |
++/* Call libresolv function OP to look up PROBE_NAME, with an answer |
|
| 587 |
++ buffer of SIZE bytes. Check that the advertised UDP buffer size is |
|
| 588 |
++ in fact EXPECTED_BUFFER_SIZE. */ |
|
| 589 |
++static void |
|
| 590 |
++do_res_search (const char *probe_name, enum res_op op, size_t size, |
|
| 591 |
++ size_t expected_buffer_size) |
|
| 592 |
++{
|
|
| 593 |
++ if (test_verbose) |
|
| 594 |
++ printf ("info: testing %s with buffer size %zu\n",
|
|
| 595 |
++ res_op_string (op), size); |
|
| 596 |
++ unsigned char *buffer = xmalloc (size); |
|
| 597 |
++ int ret = -1; |
|
| 598 |
++ switch (op) |
|
| 599 |
++ {
|
|
| 600 |
++ case res_op_search: |
|
| 601 |
++ ret = res_search (probe_name, C_IN, T_A, buffer, size); |
|
| 602 |
++ break; |
|
| 603 |
++ case res_op_query: |
|
| 604 |
++ ret = res_query (probe_name, C_IN, T_A, buffer, size); |
|
| 605 |
++ break; |
|
| 606 |
++ case res_op_nsearch: |
|
| 607 |
++ ret = res_nsearch (&_res, probe_name, C_IN, T_A, buffer, size); |
|
| 608 |
++ break; |
|
| 609 |
++ case res_op_nquery: |
|
| 610 |
++ ret = res_nquery (&_res, probe_name, C_IN, T_A, buffer, size); |
|
| 611 |
++ break; |
|
| 612 |
++ case res_op_querydomain: |
|
| 613 |
++ case res_op_nquerydomain: |
|
| 614 |
++ {
|
|
| 615 |
++ char *example_stripped = xstrdup (probe_name); |
|
| 616 |
++ char *dot_example = strstr (example_stripped, ".example"); |
|
| 617 |
++ if (dot_example != NULL && strcmp (dot_example, ".example") == 0) |
|
| 618 |
++ {
|
|
| 619 |
++ /* Truncate the domain name. */ |
|
| 620 |
++ *dot_example = '\0'; |
|
| 621 |
++ if (op == res_op_querydomain) |
|
| 622 |
++ ret = res_querydomain |
|
| 623 |
++ (example_stripped, "example", C_IN, T_A, buffer, size); |
|
| 624 |
++ else |
|
| 625 |
++ ret = res_nquerydomain |
|
| 626 |
++ (&_res, example_stripped, "example", C_IN, T_A, buffer, size); |
|
| 627 |
++ } |
|
| 628 |
++ else |
|
| 629 |
++ FAIL_EXIT1 ("invalid probe name: %s", probe_name);
|
|
| 630 |
++ free (example_stripped); |
|
| 631 |
++ } |
|
| 632 |
++ break; |
|
| 633 |
++ } |
|
| 634 |
++ TEST_VERIFY_EXIT (ret > 12); |
|
| 635 |
++ unsigned char *end = buffer + ret; |
|
| 636 |
++ |
|
| 637 |
++ HEADER *hd = (HEADER *) buffer; |
|
| 638 |
++ TEST_VERIFY (ntohs (hd->qdcount) == 1); |
|
| 639 |
++ TEST_VERIFY (ntohs (hd->ancount) == 1); |
|
| 640 |
++ /* Skip over the header. */ |
|
| 641 |
++ unsigned char *p = buffer + sizeof (*hd); |
|
| 642 |
++ /* Skip over the question. */ |
|
| 643 |
++ ret = dn_skipname (p, end); |
|
| 644 |
++ TEST_VERIFY_EXIT (ret > 0); |
|
| 645 |
++ p += ret; |
|
| 646 |
++ TEST_VERIFY_EXIT (end - p >= 4); |
|
| 647 |
++ p += 4; |
|
| 648 |
++ /* Skip over the RNAME and the RR header, but stop at the RDATA |
|
| 649 |
++ length. */ |
|
| 650 |
++ ret = dn_skipname (p, end); |
|
| 651 |
++ TEST_VERIFY_EXIT (ret > 0); |
|
| 652 |
++ p += ret; |
|
| 653 |
++ TEST_VERIFY_EXIT (end - p >= 2 + 2 + 4 + 2 + 4); |
|
| 654 |
++ p += 2 + 2 + 4; |
|
| 655 |
++ /* The IP address should be 4 bytes long. */ |
|
| 656 |
++ TEST_VERIFY_EXIT (p[0] == 0); |
|
| 657 |
++ TEST_VERIFY_EXIT (p[1] == 4); |
|
| 658 |
++ /* Extract the address information. */ |
|
| 659 |
++ p += 2; |
|
| 660 |
++ struct response_data *data = decode_address (p, 4); |
|
| 661 |
++ |
|
| 662 |
++ verify_response_data_payload (data, expected_buffer_size); |
|
| 663 |
++ |
|
| 664 |
++ free (buffer); |
|
| 665 |
++} |
|
| 666 |
++ |
|
| 667 |
++static void |
|
| 668 |
++run_test (const char *probe_name) |
|
| 669 |
++{
|
|
| 670 |
++ if (test_verbose) |
|
| 671 |
++ printf ("\ninfo: * use_edns=%d use_dnssec=%d\n",
|
|
| 672 |
++ use_edns, use_dnssec); |
|
| 673 |
++ check_hostent (gethostbyname (probe_name)); |
|
| 674 |
++ check_hostent (gethostbyname2 (probe_name, AF_INET)); |
|
| 675 |
++ check_hostent (gethostbyname2 (probe_name, AF_INET6)); |
|
| 676 |
++ do_ai (AF_UNSPEC); |
|
| 677 |
++ do_ai (AF_INET); |
|
| 678 |
++ do_ai (AF_INET6); |
|
| 679 |
++ |
|
| 680 |
++ for (int op = 0; op <= res_op_last; ++op) |
|
| 681 |
++ {
|
|
| 682 |
++ do_res_search (probe_name, op, 301, 512); |
|
| 683 |
++ do_res_search (probe_name, op, 511, 512); |
|
| 684 |
++ do_res_search (probe_name, op, 512, 512); |
|
| 685 |
++ do_res_search (probe_name, op, 513, 513); |
|
| 686 |
++ do_res_search (probe_name, op, 657, 657); |
|
| 687 |
++ do_res_search (probe_name, op, 1199, 1199); |
|
| 688 |
++ do_res_search (probe_name, op, 1200, 1200); |
|
| 689 |
++ do_res_search (probe_name, op, 1201, 1200); |
|
| 690 |
++ do_res_search (probe_name, op, 65535, 1200); |
|
| 691 |
++ } |
|
| 692 |
++} |
|
| 693 |
++ |
|
| 694 |
++static int |
|
| 695 |
++do_test (void) |
|
| 696 |
++{
|
|
| 697 |
++ for (int do_edns = 0; do_edns < 2; ++do_edns) |
|
| 698 |
++ for (int do_dnssec = 0; do_dnssec < 2; ++do_dnssec) |
|
| 699 |
++ for (int do_tcp = 0; do_tcp < 2; ++do_tcp) |
|
| 700 |
++ {
|
|
| 701 |
++ struct resolv_test *aux = resolv_test_start |
|
| 702 |
++ ((struct resolv_redirect_config) |
|
| 703 |
++ {
|
|
| 704 |
++ .response_callback = response, |
|
| 705 |
++ }); |
|
| 706 |
++ |
|
| 707 |
++ use_edns = do_edns; |
|
| 708 |
++ if (do_edns) |
|
| 709 |
++ _res.options |= RES_USE_EDNS0; |
|
| 710 |
++ use_dnssec = do_dnssec; |
|
| 711 |
++ if (do_dnssec) |
|
| 712 |
++ _res.options |= RES_USE_DNSSEC; |
|
| 713 |
++ |
|
| 714 |
++ char *probe_name = xstrdup (EDNS_PROBE_EXAMPLE); |
|
| 715 |
++ if (do_tcp) |
|
| 716 |
++ {
|
|
| 717 |
++ char *n = xasprintf ("tcp.%s", probe_name);
|
|
| 718 |
++ free (probe_name); |
|
| 719 |
++ probe_name = n; |
|
| 720 |
++ } |
|
| 721 |
++ |
|
| 722 |
++ run_test (probe_name); |
|
| 723 |
++ |
|
| 724 |
++ free (probe_name); |
|
| 725 |
++ resolv_test_end (aux); |
|
| 726 |
++ } |
|
| 727 |
++ |
|
| 728 |
++ free_response_data (); |
|
| 729 |
++ return 0; |
|
| 730 |
++} |
|
| 731 |
++ |
|
| 732 |
++#include <support/test-driver.c> |
|
| 733 |
+diff --git a/support/resolv_test.c b/support/resolv_test.c |
|
| 734 |
+new file mode 100644 |
|
| 735 |
+index 49ed210..5c5a463 |
|
| 736 |
+--- /dev/null |
|
| 737 |
+@@ -0,0 +1,1201 @@ |
|
| 738 |
++/* DNS test framework and libresolv redirection. |
|
| 739 |
++ Copyright (C) 2016-2017 Free Software Foundation, Inc. |
|
| 740 |
++ This file is part of the GNU C Library. |
|
| 741 |
++ |
|
| 742 |
++ The GNU C Library is free software; you can redistribute it and/or |
|
| 743 |
++ modify it under the terms of the GNU Lesser General Public |
|
| 744 |
++ License as published by the Free Software Foundation; either |
|
| 745 |
++ version 2.1 of the License, or (at your option) any later version. |
|
| 746 |
++ |
|
| 747 |
++ The GNU C Library is distributed in the hope that it will be useful, |
|
| 748 |
++ but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
| 749 |
++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
|
| 750 |
++ Lesser General Public License for more details. |
|
| 751 |
++ |
|
| 752 |
++ You should have received a copy of the GNU Lesser General Public |
|
| 753 |
++ License along with the GNU C Library; if not, see |
|
| 754 |
++ <http://www.gnu.org/licenses/>. */ |
|
| 755 |
++ |
|
| 756 |
++#include <support/resolv_test.h> |
|
| 757 |
++ |
|
| 758 |
++#include <arpa/inet.h> |
|
| 759 |
++#include <errno.h> |
|
| 760 |
++#include <fcntl.h> |
|
| 761 |
++#include <nss.h> |
|
| 762 |
++#include <resolv.h> |
|
| 763 |
++#include <search.h> |
|
| 764 |
++#include <stdlib.h> |
|
| 765 |
++#include <string.h> |
|
| 766 |
++#include <support/check.h> |
|
| 767 |
++#include <support/namespace.h> |
|
| 768 |
++#include <support/support.h> |
|
| 769 |
++#include <support/test-driver.h> |
|
| 770 |
++#include <support/xsocket.h> |
|
| 771 |
++#include <support/xthread.h> |
|
| 772 |
++#include <support/xunistd.h> |
|
| 773 |
++#include <unistd.h> |
|
| 774 |
++ |
|
| 775 |
++/* Response builder. */ |
|
| 776 |
++ |
|
| 777 |
++enum |
|
| 778 |
++ {
|
|
| 779 |
++ max_response_length = 65536 |
|
| 780 |
++ }; |
|
| 781 |
++ |
|
| 782 |
++/* List of pointers to be freed. The hash table implementation |
|
| 783 |
++ (struct hsearch_data) does not provide a way to deallocate all |
|
| 784 |
++ objects, so this approach is used to avoid memory leaks. */ |
|
| 785 |
++struct to_be_freed |
|
| 786 |
++{
|
|
| 787 |
++ struct to_be_freed *next; |
|
| 788 |
++ void *ptr; |
|
| 789 |
++}; |
|
| 790 |
++ |
|
| 791 |
++struct resolv_response_builder |
|
| 792 |
++{
|
|
| 793 |
++ const unsigned char *query_buffer; |
|
| 794 |
++ size_t query_length; |
|
| 795 |
++ |
|
| 796 |
++ size_t offset; /* Bytes written so far in buffer. */ |
|
| 797 |
++ ns_sect section; /* Current section in the DNS packet. */ |
|
| 798 |
++ unsigned int truncate_bytes; /* Bytes to remove at end of response. */ |
|
| 799 |
++ bool drop; /* Discard generated response. */ |
|
| 800 |
++ bool close; /* Close TCP client connection. */ |
|
| 801 |
++ |
|
| 802 |
++ /* Offset of the two-byte RDATA length field in the currently |
|
| 803 |
++ written RDATA sub-structure. 0 if no RDATA is being written. */ |
|
| 804 |
++ size_t current_rdata_offset; |
|
| 805 |
++ |
|
| 806 |
++ /* Hash table for locating targets for label compression. */ |
|
| 807 |
++ struct hsearch_data compression_offsets; |
|
| 808 |
++ /* List of pointers which need to be freed. Used for domain names |
|
| 809 |
++ involved in label compression. */ |
|
| 810 |
++ struct to_be_freed *to_be_freed; |
|
| 811 |
++ |
|
| 812 |
++ /* Must be last. Not zeroed for performance reasons. */ |
|
| 813 |
++ unsigned char buffer[max_response_length]; |
|
| 814 |
++}; |
|
| 815 |
++ |
|
| 816 |
++/* Response builder. */ |
|
| 817 |
++ |
|
| 818 |
++/* Add a pointer to the list of pointers to be freed when B is |
|
| 819 |
++ deallocated. */ |
|
| 820 |
++static void |
|
| 821 |
++response_push_pointer_to_free (struct resolv_response_builder *b, void *ptr) |
|
| 822 |
++{
|
|
| 823 |
++ if (ptr == NULL) |
|
| 824 |
++ return; |
|
| 825 |
++ struct to_be_freed *e = xmalloc (sizeof (*e)); |
|
| 826 |
++ *e = (struct to_be_freed) {b->to_be_freed, ptr};
|
|
| 827 |
++ b->to_be_freed = e; |
|
| 828 |
++} |
|
| 829 |
++ |
|
| 830 |
++void |
|
| 831 |
++resolv_response_init (struct resolv_response_builder *b, |
|
| 832 |
++ struct resolv_response_flags flags) |
|
| 833 |
++{
|
|
| 834 |
++ if (b->offset > 0) |
|
| 835 |
++ FAIL_EXIT1 ("response_init: called at offset %zu", b->offset);
|
|
| 836 |
++ if (b->query_length < 12) |
|
| 837 |
++ FAIL_EXIT1 ("response_init called for a query of size %zu",
|
|
| 838 |
++ b->query_length); |
|
| 839 |
++ if (flags.rcode > 15) |
|
| 840 |
++ FAIL_EXIT1 ("response_init: invalid RCODE %u", flags.rcode);
|
|
| 841 |
++ |
|
| 842 |
++ /* Copy the transaction ID. */ |
|
| 843 |
++ b->buffer[0] = b->query_buffer[0]; |
|
| 844 |
++ b->buffer[1] = b->query_buffer[1]; |
|
| 845 |
++ |
|
| 846 |
++ /* Initialize the flags. */ |
|
| 847 |
++ b->buffer[2] = 0x80; /* Mark as response. */ |
|
| 848 |
++ b->buffer[2] |= b->query_buffer[2] & 0x01; /* Copy the RD bit. */ |
|
| 849 |
++ if (flags.tc) |
|
| 850 |
++ b->buffer[2] |= 0x02; |
|
| 851 |
++ b->buffer[3] = 0x80 | flags.rcode; /* Always set RA. */ |
|
| 852 |
++ |
|
| 853 |
++ /* Fill in the initial section count values. */ |
|
| 854 |
++ b->buffer[4] = flags.qdcount >> 8; |
|
| 855 |
++ b->buffer[5] = flags.qdcount; |
|
| 856 |
++ b->buffer[6] = flags.ancount >> 8; |
|
| 857 |
++ b->buffer[7] = flags.ancount; |
|
| 858 |
++ b->buffer[8] = flags.nscount >> 8; |
|
| 859 |
++ b->buffer[9] = flags.nscount; |
|
| 860 |
++ b->buffer[10] = flags.adcount >> 8; |
|
| 861 |
++ b->buffer[11] = flags.adcount; |
|
| 862 |
++ |
|
| 863 |
++ b->offset = 12; |
|
| 864 |
++} |
|
| 865 |
++ |
|
| 866 |
++void |
|
| 867 |
++resolv_response_section (struct resolv_response_builder *b, ns_sect section) |
|
| 868 |
++{
|
|
| 869 |
++ if (b->offset == 0) |
|
| 870 |
++ FAIL_EXIT1 ("resolv_response_section: response_init not called before");
|
|
| 871 |
++ if (section < b->section) |
|
| 872 |
++ FAIL_EXIT1 ("resolv_response_section: cannot go back to previous section");
|
|
| 873 |
++ b->section = section; |
|
| 874 |
++} |
|
| 875 |
++ |
|
| 876 |
++/* Add a single byte to B. */ |
|
| 877 |
++static inline void |
|
| 878 |
++response_add_byte (struct resolv_response_builder *b, unsigned char ch) |
|
| 879 |
++{
|
|
| 880 |
++ if (b->offset == max_response_length) |
|
| 881 |
++ FAIL_EXIT1 ("DNS response exceeds 64 KiB limit");
|
|
| 882 |
++ b->buffer[b->offset] = ch; |
|
| 883 |
++ ++b->offset; |
|
| 884 |
++} |
|
| 885 |
++ |
|
| 886 |
++/* Add a 16-bit word VAL to B, in big-endian format. */ |
|
| 887 |
++static void |
|
| 888 |
++response_add_16 (struct resolv_response_builder *b, uint16_t val) |
|
| 889 |
++{
|
|
| 890 |
++ response_add_byte (b, val >> 8); |
|
| 891 |
++ response_add_byte (b, val); |
|
| 892 |
++} |
|
| 893 |
++ |
|
| 894 |
++/* Increment the pers-section record counter in the packet header. */ |
|
| 895 |
++static void |
|
| 896 |
++response_count_increment (struct resolv_response_builder *b) |
|
| 897 |
++{
|
|
| 898 |
++ unsigned int offset = b->section; |
|
| 899 |
++ offset = 4 + 2 * offset; |
|
| 900 |
++ ++b->buffer[offset + 1]; |
|
| 901 |
++ if (b->buffer[offset + 1] == 0) |
|
| 902 |
++ {
|
|
| 903 |
++ /* Carry. */ |
|
| 904 |
++ ++b->buffer[offset]; |
|
| 905 |
++ if (b->buffer[offset] == 0) |
|
| 906 |
++ /* Overflow. */ |
|
| 907 |
++ FAIL_EXIT1 ("too many records in section");
|
|
| 908 |
++ } |
|
| 909 |
++} |
|
| 910 |
++ |
|
| 911 |
++void |
|
| 912 |
++resolv_response_add_question (struct resolv_response_builder *b, |
|
| 913 |
++ const char *name, uint16_t class, uint16_t type) |
|
| 914 |
++{
|
|
| 915 |
++ if (b->offset == 0) |
|
| 916 |
++ FAIL_EXIT1 ("resolv_response_add_question: "
|
|
| 917 |
++ "resolv_response_init not called"); |
|
| 918 |
++ if (b->section != ns_s_qd) |
|
| 919 |
++ FAIL_EXIT1 ("resolv_response_add_question: "
|
|
| 920 |
++ "must be called in the question section"); |
|
| 921 |
++ |
|
| 922 |
++ resolv_response_add_name (b, name); |
|
| 923 |
++ response_add_16 (b, type); |
|
| 924 |
++ response_add_16 (b, class); |
|
| 925 |
++ |
|
| 926 |
++ response_count_increment (b); |
|
| 927 |
++} |
|
| 928 |
++ |
|
| 929 |
++void |
|
| 930 |
++resolv_response_add_name (struct resolv_response_builder *b, |
|
| 931 |
++ const char *const origname) |
|
| 932 |
++{
|
|
| 933 |
++ /* Normalized name. */ |
|
| 934 |
++ char *name; |
|
| 935 |
++ /* Normalized name with case preserved. */ |
|
| 936 |
++ char *name_case; |
|
| 937 |
++ {
|
|
| 938 |
++ size_t namelen = strlen (origname); |
|
| 939 |
++ /* Remove trailing dots. FIXME: Handle trailing quoted dots. */ |
|
| 940 |
++ while (namelen > 0 && origname[namelen - 1] == '.') |
|
| 941 |
++ --namelen; |
|
| 942 |
++ name = xmalloc (namelen + 1); |
|
| 943 |
++ name_case = xmalloc (namelen + 1); |
|
| 944 |
++ /* Copy and convert to lowercase. FIXME: This needs to normalize |
|
| 945 |
++ escaping as well. */ |
|
| 946 |
++ for (size_t i = 0; i < namelen; ++i) |
|
| 947 |
++ {
|
|
| 948 |
++ char ch = origname[i]; |
|
| 949 |
++ name_case[i] = ch; |
|
| 950 |
++ if ('A' <= ch && ch <= 'Z')
|
|
| 951 |
++ ch = ch - 'A' + 'a'; |
|
| 952 |
++ name[i] = ch; |
|
| 953 |
++ } |
|
| 954 |
++ name[namelen] = 0; |
|
| 955 |
++ name_case[namelen] = 0; |
|
| 956 |
++ } |
|
| 957 |
++ char *name_start = name; |
|
| 958 |
++ char *name_case_start = name_case; |
|
| 959 |
++ |
|
| 960 |
++ bool compression = false; |
|
| 961 |
++ while (*name) |
|
| 962 |
++ {
|
|
| 963 |
++ /* Search for a previous name we can reference. */ |
|
| 964 |
++ ENTRY new_entry = |
|
| 965 |
++ {
|
|
| 966 |
++ .key = name, |
|
| 967 |
++ .data = (void *) (uintptr_t) b->offset, |
|
| 968 |
++ }; |
|
| 969 |
++ |
|
| 970 |
++ /* If the label can be a compression target because it is at a |
|
| 971 |
++ reachable offset, add it to the hash table. */ |
|
| 972 |
++ ACTION action; |
|
| 973 |
++ if (b->offset < (1 << 12)) |
|
| 974 |
++ action = ENTER; |
|
| 975 |
++ else |
|
| 976 |
++ action = FIND; |
|
| 977 |
++ |
|
| 978 |
++ /* Search for known compression offsets in the hash table. */ |
|
| 979 |
++ ENTRY *e; |
|
| 980 |
++ if (hsearch_r (new_entry, action, &e, &b->compression_offsets) == 0) |
|
| 981 |
++ {
|
|
| 982 |
++ if (action == FIND && errno == ESRCH) |
|
| 983 |
++ /* Fall through. */ |
|
| 984 |
++ e = NULL; |
|
| 985 |
++ else |
|
| 986 |
++ FAIL_EXIT1 ("hsearch_r failure in name compression: %m");
|
|
| 987 |
++ } |
|
| 988 |
++ |
|
| 989 |
++ /* The name is known. Reference the previous location. */ |
|
| 990 |
++ if (e != NULL && e->data != new_entry.data) |
|
| 991 |
++ {
|
|
| 992 |
++ size_t old_offset = (uintptr_t) e->data; |
|
| 993 |
++ response_add_byte (b, 0xC0 | (old_offset >> 8)); |
|
| 994 |
++ response_add_byte (b, old_offset); |
|
| 995 |
++ compression = true; |
|
| 996 |
++ break; |
|
| 997 |
++ } |
|
| 998 |
++ |
|
| 999 |
++ /* The name does not exist yet. Write one label. First, add |
|
| 1000 |
++ room for the label length. */ |
|
| 1001 |
++ size_t buffer_label_offset = b->offset; |
|
| 1002 |
++ response_add_byte (b, 0); |
|
| 1003 |
++ |
|
| 1004 |
++ /* Copy the label. */ |
|
| 1005 |
++ while (true) |
|
| 1006 |
++ {
|
|
| 1007 |
++ char ch = *name_case; |
|
| 1008 |
++ if (ch == '\0') |
|
| 1009 |
++ break; |
|
| 1010 |
++ ++name; |
|
| 1011 |
++ ++name_case; |
|
| 1012 |
++ if (ch == '.') |
|
| 1013 |
++ break; |
|
| 1014 |
++ /* FIXME: Handle escaping. */ |
|
| 1015 |
++ response_add_byte (b, ch); |
|
| 1016 |
++ } |
|
| 1017 |
++ |
|
| 1018 |
++ /* Patch in the label length. */ |
|
| 1019 |
++ size_t label_length = b->offset - buffer_label_offset - 1; |
|
| 1020 |
++ if (label_length == 0) |
|
| 1021 |
++ FAIL_EXIT1 ("empty label in name compression: %s", origname);
|
|
| 1022 |
++ if (label_length > 63) |
|
| 1023 |
++ FAIL_EXIT1 ("label too long in name compression: %s", origname);
|
|
| 1024 |
++ b->buffer[buffer_label_offset] = label_length; |
|
| 1025 |
++ |
|
| 1026 |
++ /* Continue with the tail of the name and the next label. */ |
|
| 1027 |
++ } |
|
| 1028 |
++ |
|
| 1029 |
++ if (compression) |
|
| 1030 |
++ {
|
|
| 1031 |
++ /* If we found an immediate match for the name, we have not put |
|
| 1032 |
++ it into the hash table, and can free it immediately. */ |
|
| 1033 |
++ if (name == name_start) |
|
| 1034 |
++ free (name_start); |
|
| 1035 |
++ else |
|
| 1036 |
++ response_push_pointer_to_free (b, name_start); |
|
| 1037 |
++ } |
|
| 1038 |
++ else |
|
| 1039 |
++ {
|
|
| 1040 |
++ /* Terminate the sequence of labels. With compression, this is |
|
| 1041 |
++ implicit in the compression reference. */ |
|
| 1042 |
++ response_add_byte (b, 0); |
|
| 1043 |
++ response_push_pointer_to_free (b, name_start); |
|
| 1044 |
++ } |
|
| 1045 |
++ |
|
| 1046 |
++ free (name_case_start); |
|
| 1047 |
++} |
|
| 1048 |
++ |
|
| 1049 |
++void |
|
| 1050 |
++resolv_response_open_record (struct resolv_response_builder *b, |
|
| 1051 |
++ const char *name, |
|
| 1052 |
++ uint16_t class, uint16_t type, uint32_t ttl) |
|
| 1053 |
++{
|
|
| 1054 |
++ if (b->section == ns_s_qd) |
|
| 1055 |
++ FAIL_EXIT1 ("resolv_response_open_record called in question section");
|
|
| 1056 |
++ if (b->current_rdata_offset != 0) |
|
| 1057 |
++ FAIL_EXIT1 ("resolv_response_open_record called with open record");
|
|
| 1058 |
++ |
|
| 1059 |
++ resolv_response_add_name (b, name); |
|
| 1060 |
++ response_add_16 (b, type); |
|
| 1061 |
++ response_add_16 (b, class); |
|
| 1062 |
++ response_add_16 (b, ttl >> 16); |
|
| 1063 |
++ response_add_16 (b, ttl); |
|
| 1064 |
++ |
|
| 1065 |
++ b->current_rdata_offset = b->offset; |
|
| 1066 |
++ /* Add room for the RDATA length. */ |
|
| 1067 |
++ response_add_16 (b, 0); |
|
| 1068 |
++} |
|
| 1069 |
++ |
|
| 1070 |
++ |
|
| 1071 |
++void |
|
| 1072 |
++resolv_response_close_record (struct resolv_response_builder *b) |
|
| 1073 |
++{
|
|
| 1074 |
++ size_t rdata_offset = b->current_rdata_offset; |
|
| 1075 |
++ if (rdata_offset == 0) |
|
| 1076 |
++ FAIL_EXIT1 ("response_close_record called without open record");
|
|
| 1077 |
++ size_t rdata_length = b->offset - rdata_offset - 2; |
|
| 1078 |
++ if (rdata_length > 65535) |
|
| 1079 |
++ FAIL_EXIT1 ("RDATA length %zu exceeds limit", rdata_length);
|
|
| 1080 |
++ b->buffer[rdata_offset] = rdata_length >> 8; |
|
| 1081 |
++ b->buffer[rdata_offset + 1] = rdata_length; |
|
| 1082 |
++ response_count_increment (b); |
|
| 1083 |
++ b->current_rdata_offset = 0; |
|
| 1084 |
++} |
|
| 1085 |
++ |
|
| 1086 |
++void |
|
| 1087 |
++resolv_response_add_data (struct resolv_response_builder *b, |
|
| 1088 |
++ const void *data, size_t length) |
|
| 1089 |
++{
|
|
| 1090 |
++ size_t remaining = max_response_length - b->offset; |
|
| 1091 |
++ if (remaining < length) |
|
| 1092 |
++ FAIL_EXIT1 ("resolv_response_add_data: not enough room for %zu bytes",
|
|
| 1093 |
++ length); |
|
| 1094 |
++ memcpy (b->buffer + b->offset, data, length); |
|
| 1095 |
++ b->offset += length; |
|
| 1096 |
++} |
|
| 1097 |
++ |
|
| 1098 |
++void |
|
| 1099 |
++resolv_response_drop (struct resolv_response_builder *b) |
|
| 1100 |
++{
|
|
| 1101 |
++ b->drop = true; |
|
| 1102 |
++} |
|
| 1103 |
++ |
|
| 1104 |
++void |
|
| 1105 |
++resolv_response_close (struct resolv_response_builder *b) |
|
| 1106 |
++{
|
|
| 1107 |
++ b->close = true; |
|
| 1108 |
++} |
|
| 1109 |
++ |
|
| 1110 |
++void |
|
| 1111 |
++resolv_response_truncate_data (struct resolv_response_builder *b, size_t count) |
|
| 1112 |
++{
|
|
| 1113 |
++ if (count > 65535) |
|
| 1114 |
++ FAIL_EXIT1 ("resolv_response_truncate_data: argument too large: %zu",
|
|
| 1115 |
++ count); |
|
| 1116 |
++ b->truncate_bytes = count; |
|
| 1117 |
++} |
|
| 1118 |
++ |
|
| 1119 |
++ |
|
| 1120 |
++size_t |
|
| 1121 |
++resolv_response_length (const struct resolv_response_builder *b) |
|
| 1122 |
++{
|
|
| 1123 |
++ return b->offset; |
|
| 1124 |
++} |
|
| 1125 |
++ |
|
| 1126 |
++unsigned char * |
|
| 1127 |
++resolv_response_buffer (const struct resolv_response_builder *b) |
|
| 1128 |
++{
|
|
| 1129 |
++ unsigned char *result = xmalloc (b->offset); |
|
| 1130 |
++ memcpy (result, b->buffer, b->offset); |
|
| 1131 |
++ return result; |
|
| 1132 |
++} |
|
| 1133 |
++ |
|
| 1134 |
++static struct resolv_response_builder * |
|
| 1135 |
++response_builder_allocate |
|
| 1136 |
++ (const unsigned char *query_buffer, size_t query_length) |
|
| 1137 |
++{
|
|
| 1138 |
++ struct resolv_response_builder *b = xmalloc (sizeof (*b)); |
|
| 1139 |
++ memset (b, 0, offsetof (struct resolv_response_builder, buffer)); |
|
| 1140 |
++ b->query_buffer = query_buffer; |
|
| 1141 |
++ b->query_length = query_length; |
|
| 1142 |
++ TEST_VERIFY_EXIT (hcreate_r (10000, &b->compression_offsets) != 0); |
|
| 1143 |
++ return b; |
|
| 1144 |
++} |
|
| 1145 |
++ |
|
| 1146 |
++static void |
|
| 1147 |
++response_builder_free (struct resolv_response_builder *b) |
|
| 1148 |
++{
|
|
| 1149 |
++ struct to_be_freed *current = b->to_be_freed; |
|
| 1150 |
++ while (current != NULL) |
|
| 1151 |
++ {
|
|
| 1152 |
++ struct to_be_freed *next = current->next; |
|
| 1153 |
++ free (current->ptr); |
|
| 1154 |
++ free (current); |
|
| 1155 |
++ current = next; |
|
| 1156 |
++ } |
|
| 1157 |
++ hdestroy_r (&b->compression_offsets); |
|
| 1158 |
++ free (b); |
|
| 1159 |
++} |
|
| 1160 |
++ |
|
| 1161 |
++/* DNS query processing. */ |
|
| 1162 |
++ |
|
| 1163 |
++/* Data extracted from the question section of a DNS packet. */ |
|
| 1164 |
++struct query_info |
|
| 1165 |
++{
|
|
| 1166 |
++ char qname[MAXDNAME]; |
|
| 1167 |
++ uint16_t qclass; |
|
| 1168 |
++ uint16_t qtype; |
|
| 1169 |
++ struct resolv_edns_info edns; |
|
| 1170 |
++}; |
|
| 1171 |
++ |
|
| 1172 |
++/* Update *INFO from the specified DNS packet. */ |
|
| 1173 |
++static void |
|
| 1174 |
++parse_query (struct query_info *info, |
|
| 1175 |
++ const unsigned char *buffer, size_t length) |
|
| 1176 |
++{
|
|
| 1177 |
++ HEADER hd; |
|
| 1178 |
++ _Static_assert (sizeof (hd) == 12, "DNS header size"); |
|
| 1179 |
++ if (length < sizeof (hd)) |
|
| 1180 |
++ FAIL_EXIT1 ("malformed DNS query: too short: %zu bytes", length);
|
|
| 1181 |
++ memcpy (&hd, buffer, sizeof (hd)); |
|
| 1182 |
++ |
|
| 1183 |
++ if (ntohs (hd.qdcount) != 1) |
|
| 1184 |
++ FAIL_EXIT1 ("malformed DNS query: wrong question count: %d",
|
|
| 1185 |
++ (int) ntohs (hd.qdcount)); |
|
| 1186 |
++ if (ntohs (hd.ancount) != 0) |
|
| 1187 |
++ FAIL_EXIT1 ("malformed DNS query: wrong answer count: %d",
|
|
| 1188 |
++ (int) ntohs (hd.ancount)); |
|
| 1189 |
++ if (ntohs (hd.nscount) != 0) |
|
| 1190 |
++ FAIL_EXIT1 ("malformed DNS query: wrong authority count: %d",
|
|
| 1191 |
++ (int) ntohs (hd.nscount)); |
|
| 1192 |
++ if (ntohs (hd.arcount) > 1) |
|
| 1193 |
++ FAIL_EXIT1 ("malformed DNS query: wrong additional count: %d",
|
|
| 1194 |
++ (int) ntohs (hd.arcount)); |
|
| 1195 |
++ |
|
| 1196 |
++ int ret = dn_expand (buffer, buffer + length, buffer + sizeof (hd), |
|
| 1197 |
++ info->qname, sizeof (info->qname)); |
|
| 1198 |
++ if (ret < 0) |
|
| 1199 |
++ FAIL_EXIT1 ("malformed DNS query: cannot uncompress QNAME");
|
|
| 1200 |
++ |
|
| 1201 |
++ /* Obtain QTYPE and QCLASS. */ |
|
| 1202 |
++ size_t remaining = length - (12 + ret); |
|
| 1203 |
++ struct |
|
| 1204 |
++ {
|
|
| 1205 |
++ uint16_t qtype; |
|
| 1206 |
++ uint16_t qclass; |
|
| 1207 |
++ } qtype_qclass; |
|
| 1208 |
++ if (remaining < sizeof (qtype_qclass)) |
|
| 1209 |
++ FAIL_EXIT1 ("malformed DNS query: "
|
|
| 1210 |
++ "query lacks QCLASS/QTYPE, QNAME: %s", info->qname); |
|
| 1211 |
++ memcpy (&qtype_qclass, buffer + 12 + ret, sizeof (qtype_qclass)); |
|
| 1212 |
++ info->qclass = ntohs (qtype_qclass.qclass); |
|
| 1213 |
++ info->qtype = ntohs (qtype_qclass.qtype); |
|
| 1214 |
++ |
|
| 1215 |
++ memset (&info->edns, 0, sizeof (info->edns)); |
|
| 1216 |
++ if (ntohs (hd.arcount) > 0) |
|
| 1217 |
++ {
|
|
| 1218 |
++ /* Parse EDNS record. */ |
|
| 1219 |
++ struct __attribute__ ((packed, aligned (1))) |
|
| 1220 |
++ {
|
|
| 1221 |
++ uint8_t root; |
|
| 1222 |
++ uint16_t rtype; |
|
| 1223 |
++ uint16_t payload; |
|
| 1224 |
++ uint8_t edns_extended_rcode; |
|
| 1225 |
++ uint8_t edns_version; |
|
| 1226 |
++ uint16_t flags; |
|
| 1227 |
++ uint16_t rdatalen; |
|
| 1228 |
++ } rr; |
|
| 1229 |
++ _Static_assert (sizeof (rr) == 11, "EDNS record size"); |
|
| 1230 |
++ |
|
| 1231 |
++ if (remaining < 4 + sizeof (rr)) |
|
| 1232 |
++ FAIL_EXIT1 ("mailformed DNS query: no room for EDNS record");
|
|
| 1233 |
++ memcpy (&rr, buffer + 12 + ret + 4, sizeof (rr)); |
|
| 1234 |
++ if (rr.root != 0) |
|
| 1235 |
++ FAIL_EXIT1 ("malformed DNS query: invalid OPT RNAME: %d\n", rr.root);
|
|
| 1236 |
++ if (rr.rtype != htons (41)) |
|
| 1237 |
++ FAIL_EXIT1 ("malformed DNS query: invalid OPT type: %d\n",
|
|
| 1238 |
++ ntohs (rr.rtype)); |
|
| 1239 |
++ info->edns.active = true; |
|
| 1240 |
++ info->edns.extended_rcode = rr.edns_extended_rcode; |
|
| 1241 |
++ info->edns.version = rr.edns_version; |
|
| 1242 |
++ info->edns.flags = ntohs (rr.flags); |
|
| 1243 |
++ info->edns.payload_size = ntohs (rr.payload); |
|
| 1244 |
++ } |
|
| 1245 |
++} |
|
| 1246 |
++ |
|
| 1247 |
++ |
|
| 1248 |
++/* Main testing framework. */ |
|
| 1249 |
++ |
|
| 1250 |
++/* Per-server information. One struct is allocated for each test |
|
| 1251 |
++ server. */ |
|
| 1252 |
++struct resolv_test_server |
|
| 1253 |
++{
|
|
| 1254 |
++ /* Local address of the server. UDP and TCP use the same port. */ |
|
| 1255 |
++ struct sockaddr_in address; |
|
| 1256 |
++ |
|
| 1257 |
++ /* File descriptor of the UDP server, or -1 if this server is |
|
| 1258 |
++ disabled. */ |
|
| 1259 |
++ int socket_udp; |
|
| 1260 |
++ |
|
| 1261 |
++ /* File descriptor of the TCP server, or -1 if this server is |
|
| 1262 |
++ disabled. */ |
|
| 1263 |
++ int socket_tcp; |
|
| 1264 |
++ |
|
| 1265 |
++ /* Counter of the number of responses processed so far. */ |
|
| 1266 |
++ size_t response_number; |
|
| 1267 |
++ |
|
| 1268 |
++ /* Thread handles for the server threads (if not disabled in the |
|
| 1269 |
++ configuration). */ |
|
| 1270 |
++ pthread_t thread_udp; |
|
| 1271 |
++ pthread_t thread_tcp; |
|
| 1272 |
++}; |
|
| 1273 |
++ |
|
| 1274 |
++/* Main struct for keeping track of libresolv redirection and |
|
| 1275 |
++ testing. */ |
|
| 1276 |
++struct resolv_test |
|
| 1277 |
++{
|
|
| 1278 |
++ /* After initialization, any access to the struct must be performed |
|
| 1279 |
++ while this lock is acquired. */ |
|
| 1280 |
++ pthread_mutex_t lock; |
|
| 1281 |
++ |
|
| 1282 |
++ /* Data for each test server. */ |
|
| 1283 |
++ struct resolv_test_server servers[resolv_max_test_servers]; |
|
| 1284 |
++ |
|
| 1285 |
++ /* Used if config.single_thread_udp is true. */ |
|
| 1286 |
++ pthread_t thread_udp_single; |
|
| 1287 |
++ |
|
| 1288 |
++ struct resolv_redirect_config config; |
|
| 1289 |
++ bool termination_requested; |
|
| 1290 |
++}; |
|
| 1291 |
++ |
|
| 1292 |
++/* Function implementing a server thread. */ |
|
| 1293 |
++typedef void (*thread_callback) (struct resolv_test *, int server_index); |
|
| 1294 |
++ |
|
| 1295 |
++/* Storage for thread-specific data, for passing to the |
|
| 1296 |
++ thread_callback function. */ |
|
| 1297 |
++struct thread_closure |
|
| 1298 |
++{
|
|
| 1299 |
++ struct resolv_test *obj; /* Current test object. */ |
|
| 1300 |
++ thread_callback callback; /* Function to call. */ |
|
| 1301 |
++ int server_index; /* Index of the implemented server. */ |
|
| 1302 |
++}; |
|
| 1303 |
++ |
|
| 1304 |
++/* Wrap response_callback as a function which can be passed to |
|
| 1305 |
++ pthread_create. */ |
|
| 1306 |
++static void * |
|
| 1307 |
++thread_callback_wrapper (void *arg) |
|
| 1308 |
++{
|
|
| 1309 |
++ struct thread_closure *closure = arg; |
|
| 1310 |
++ closure->callback (closure->obj, closure->server_index); |
|
| 1311 |
++ free (closure); |
|
| 1312 |
++ return NULL; |
|
| 1313 |
++} |
|
| 1314 |
++ |
|
| 1315 |
++/* Start a server thread for the specified SERVER_INDEX, implemented |
|
| 1316 |
++ by CALLBACK. */ |
|
| 1317 |
++static pthread_t |
|
| 1318 |
++start_server_thread (struct resolv_test *obj, int server_index, |
|
| 1319 |
++ thread_callback callback) |
|
| 1320 |
++{
|
|
| 1321 |
++ struct thread_closure *closure = xmalloc (sizeof (*closure)); |
|
| 1322 |
++ *closure = (struct thread_closure) |
|
| 1323 |
++ {
|
|
| 1324 |
++ .obj = obj, |
|
| 1325 |
++ .callback = callback, |
|
| 1326 |
++ .server_index = server_index, |
|
| 1327 |
++ }; |
|
| 1328 |
++ return xpthread_create (NULL, thread_callback_wrapper, closure); |
|
| 1329 |
++} |
|
| 1330 |
++ |
|
| 1331 |
++/* Process one UDP query. Return false if a termination requested has |
|
| 1332 |
++ been detected. */ |
|
| 1333 |
++static bool |
|
| 1334 |
++server_thread_udp_process_one (struct resolv_test *obj, int server_index) |
|
| 1335 |
++{
|
|
| 1336 |
++ unsigned char query[512]; |
|
| 1337 |
++ struct sockaddr_storage peer; |
|
| 1338 |
++ socklen_t peerlen = sizeof (peer); |
|
| 1339 |
++ size_t length = xrecvfrom (obj->servers[server_index].socket_udp, |
|
| 1340 |
++ query, sizeof (query), 0, |
|
| 1341 |
++ (struct sockaddr *) &peer, &peerlen); |
|
| 1342 |
++ /* Check for termination. */ |
|
| 1343 |
++ {
|
|
| 1344 |
++ bool termination_requested; |
|
| 1345 |
++ xpthread_mutex_lock (&obj->lock); |
|
| 1346 |
++ termination_requested = obj->termination_requested; |
|
| 1347 |
++ xpthread_mutex_unlock (&obj->lock); |
|
| 1348 |
++ if (termination_requested) |
|
| 1349 |
++ return false; |
|
| 1350 |
++ } |
|
| 1351 |
++ |
|
| 1352 |
++ |
|
| 1353 |
++ struct query_info qinfo; |
|
| 1354 |
++ parse_query (&qinfo, query, length); |
|
| 1355 |
++ if (test_verbose > 0) |
|
| 1356 |
++ {
|
|
| 1357 |
++ if (test_verbose > 1) |
|
| 1358 |
++ printf ("info: UDP server %d: incoming query:"
|
|
| 1359 |
++ " %zd bytes, %s/%u/%u, tnxid=0x%02x%02x\n", |
|
| 1360 |
++ server_index, length, qinfo.qname, qinfo.qclass, qinfo.qtype, |
|
| 1361 |
++ query[0], query[1]); |
|
| 1362 |
++ else |
|
| 1363 |
++ printf ("info: UDP server %d: incoming query:"
|
|
| 1364 |
++ " %zd bytes, %s/%u/%u\n", |
|
| 1365 |
++ server_index, length, qinfo.qname, qinfo.qclass, qinfo.qtype); |
|
| 1366 |
++ } |
|
| 1367 |
++ |
|
| 1368 |
++ struct resolv_response_context ctx = |
|
| 1369 |
++ {
|
|
| 1370 |
++ .query_buffer = query, |
|
| 1371 |
++ .query_length = length, |
|
| 1372 |
++ .server_index = server_index, |
|
| 1373 |
++ .tcp = false, |
|
| 1374 |
++ .edns = qinfo.edns, |
|
| 1375 |
++ }; |
|
| 1376 |
++ struct resolv_response_builder *b = response_builder_allocate (query, length); |
|
| 1377 |
++ obj->config.response_callback |
|
| 1378 |
++ (&ctx, b, qinfo.qname, qinfo.qclass, qinfo.qtype); |
|
| 1379 |
++ |
|
| 1380 |
++ if (b->drop) |
|
| 1381 |
++ {
|
|
| 1382 |
++ if (test_verbose) |
|
| 1383 |
++ printf ("info: UDP server %d: dropping response to %s/%u/%u\n",
|
|
| 1384 |
++ server_index, qinfo.qname, qinfo.qclass, qinfo.qtype); |
|
| 1385 |
++ } |
|
| 1386 |
++ else |
|
| 1387 |
++ {
|
|
| 1388 |
++ if (test_verbose) |
|
| 1389 |
++ {
|
|
| 1390 |
++ if (b->offset >= 12) |
|
| 1391 |
++ printf ("info: UDP server %d: sending response:"
|
|
| 1392 |
++ " %zu bytes, RCODE %d (for %s/%u/%u)\n", |
|
| 1393 |
++ server_index, b->offset, b->buffer[3] & 0x0f, |
|
| 1394 |
++ qinfo.qname, qinfo.qclass, qinfo.qtype); |
|
| 1395 |
++ else |
|
| 1396 |
++ printf ("info: UDP server %d: sending response: %zu bytes"
|
|
| 1397 |
++ " (for %s/%u/%u)\n", |
|
| 1398 |
++ server_index, b->offset, |
|
| 1399 |
++ qinfo.qname, qinfo.qclass, qinfo.qtype); |
|
| 1400 |
++ if (b->truncate_bytes > 0) |
|
| 1401 |
++ printf ("info: truncated by %u bytes\n", b->truncate_bytes);
|
|
| 1402 |
++ } |
|
| 1403 |
++ size_t to_send = b->offset; |
|
| 1404 |
++ if (to_send < b->truncate_bytes) |
|
| 1405 |
++ to_send = 0; |
|
| 1406 |
++ else |
|
| 1407 |
++ to_send -= b->truncate_bytes; |
|
| 1408 |
++ |
|
| 1409 |
++ /* Ignore most errors here because the other end may have closed |
|
| 1410 |
++ the socket. */ |
|
| 1411 |
++ if (sendto (obj->servers[server_index].socket_udp, |
|
| 1412 |
++ b->buffer, to_send, 0, |
|
| 1413 |
++ (struct sockaddr *) &peer, peerlen) < 0) |
|
| 1414 |
++ TEST_VERIFY_EXIT (errno != EBADF); |
|
| 1415 |
++ } |
|
| 1416 |
++ response_builder_free (b); |
|
| 1417 |
++ return true; |
|
| 1418 |
++} |
|
| 1419 |
++ |
|
| 1420 |
++/* UDP thread_callback function. Variant for one thread per |
|
| 1421 |
++ server. */ |
|
| 1422 |
++static void |
|
| 1423 |
++server_thread_udp (struct resolv_test *obj, int server_index) |
|
| 1424 |
++{
|
|
| 1425 |
++ while (server_thread_udp_process_one (obj, server_index)) |
|
| 1426 |
++ ; |
|
| 1427 |
++} |
|
| 1428 |
++ |
|
| 1429 |
++/* Single-threaded UDP processing function, for the single_thread_udp |
|
| 1430 |
++ case. */ |
|
| 1431 |
++static void * |
|
| 1432 |
++server_thread_udp_single (void *closure) |
|
| 1433 |
++{
|
|
| 1434 |
++ struct resolv_test *obj = closure; |
|
| 1435 |
++ |
|
| 1436 |
++ struct pollfd fds[resolv_max_test_servers]; |
|
| 1437 |
++ for (int server_index = 0; server_index < resolv_max_test_servers; |
|
| 1438 |
++ ++server_index) |
|
| 1439 |
++ if (obj->config.servers[server_index].disable_udp) |
|
| 1440 |
++ fds[server_index] = (struct pollfd) {.fd = -1};
|
|
| 1441 |
++ else |
|
| 1442 |
++ {
|
|
| 1443 |
++ fds[server_index] = (struct pollfd) |
|
| 1444 |
++ {
|
|
| 1445 |
++ .fd = obj->servers[server_index].socket_udp, |
|
| 1446 |
++ .events = POLLIN |
|
| 1447 |
++ }; |
|
| 1448 |
++ |
|
| 1449 |
++ /* Make the socket non-blocking. */ |
|
| 1450 |
++ int flags = fcntl (obj->servers[server_index].socket_udp, F_GETFL, 0); |
|
| 1451 |
++ if (flags < 0) |
|
| 1452 |
++ FAIL_EXIT1 ("fcntl (F_GETFL): %m");
|
|
| 1453 |
++ flags |= O_NONBLOCK; |
|
| 1454 |
++ if (fcntl (obj->servers[server_index].socket_udp, F_SETFL, flags) < 0) |
|
| 1455 |
++ FAIL_EXIT1 ("fcntl (F_SETFL): %m");
|
|
| 1456 |
++ } |
|
| 1457 |
++ |
|
| 1458 |
++ while (true) |
|
| 1459 |
++ {
|
|
| 1460 |
++ xpoll (fds, resolv_max_test_servers, -1); |
|
| 1461 |
++ for (int server_index = 0; server_index < resolv_max_test_servers; |
|
| 1462 |
++ ++server_index) |
|
| 1463 |
++ if (fds[server_index].revents != 0) |
|
| 1464 |
++ {
|
|
| 1465 |
++ if (!server_thread_udp_process_one (obj, server_index)) |
|
| 1466 |
++ goto out; |
|
| 1467 |
++ fds[server_index].revents = 0; |
|
| 1468 |
++ } |
|
| 1469 |
++ } |
|
| 1470 |
++ |
|
| 1471 |
++ out: |
|
| 1472 |
++ return NULL; |
|
| 1473 |
++} |
|
| 1474 |
++ |
|
| 1475 |
++/* Start the single UDP handler thread (for the single_thread_udp |
|
| 1476 |
++ case). */ |
|
| 1477 |
++static void |
|
| 1478 |
++start_server_thread_udp_single (struct resolv_test *obj) |
|
| 1479 |
++{
|
|
| 1480 |
++ obj->thread_udp_single |
|
| 1481 |
++ = xpthread_create (NULL, server_thread_udp_single, obj); |
|
| 1482 |
++} |
|
| 1483 |
++ |
|
| 1484 |
++/* Data describing a TCP client connect. */ |
|
| 1485 |
++struct tcp_thread_closure |
|
| 1486 |
++{
|
|
| 1487 |
++ struct resolv_test *obj; |
|
| 1488 |
++ int server_index; |
|
| 1489 |
++ int client_socket; |
|
| 1490 |
++}; |
|
| 1491 |
++ |
|
| 1492 |
++/* Read a complete DNS query packet. If EOF_OK, an immediate |
|
| 1493 |
++ end-of-file condition is acceptable. */ |
|
| 1494 |
++static bool |
|
| 1495 |
++read_fully (int fd, void *buf, size_t len, bool eof_ok) |
|
| 1496 |
++{
|
|
| 1497 |
++ const void *const end = buf + len; |
|
| 1498 |
++ while (buf < end) |
|
| 1499 |
++ {
|
|
| 1500 |
++ ssize_t ret = read (fd, buf, end - buf); |
|
| 1501 |
++ if (ret == 0) |
|
| 1502 |
++ {
|
|
| 1503 |
++ if (!eof_ok) |
|
| 1504 |
++ {
|
|
| 1505 |
++ support_record_failure (); |
|
| 1506 |
++ printf ("error: unexpected EOF on TCP connection\n");
|
|
| 1507 |
++ } |
|
| 1508 |
++ return false; |
|
| 1509 |
++ } |
|
| 1510 |
++ else if (ret < 0) |
|
| 1511 |
++ {
|
|
| 1512 |
++ if (!eof_ok || errno != ECONNRESET) |
|
| 1513 |
++ {
|
|
| 1514 |
++ support_record_failure (); |
|
| 1515 |
++ printf ("error: TCP read: %m\n");
|
|
| 1516 |
++ } |
|
| 1517 |
++ return false; |
|
| 1518 |
++ } |
|
| 1519 |
++ buf += ret; |
|
| 1520 |
++ eof_ok = false; |
|
| 1521 |
++ } |
|
| 1522 |
++ return true; |
|
| 1523 |
++} |
|
| 1524 |
++ |
|
| 1525 |
++/* Write an array of iovecs. Terminate the process on failure. */ |
|
| 1526 |
++static void |
|
| 1527 |
++writev_fully (int fd, struct iovec *buffers, size_t count) |
|
| 1528 |
++{
|
|
| 1529 |
++ while (count > 0) |
|
| 1530 |
++ {
|
|
| 1531 |
++ /* Skip zero-length write requests. */ |
|
| 1532 |
++ if (buffers->iov_len == 0) |
|
| 1533 |
++ {
|
|
| 1534 |
++ ++buffers; |
|
| 1535 |
++ --count; |
|
| 1536 |
++ continue; |
|
| 1537 |
++ } |
|
| 1538 |
++ /* Try to rewrite the remaing buffers. */ |
|
| 1539 |
++ ssize_t ret = writev (fd, buffers, count); |
|
| 1540 |
++ if (ret < 0) |
|
| 1541 |
++ FAIL_EXIT1 ("writev: %m");
|
|
| 1542 |
++ if (ret == 0) |
|
| 1543 |
++ FAIL_EXIT1 ("writev: invalid return value zero");
|
|
| 1544 |
++ /* Find the buffers that were successfully written. */ |
|
| 1545 |
++ while (ret > 0) |
|
| 1546 |
++ {
|
|
| 1547 |
++ if (count == 0) |
|
| 1548 |
++ FAIL_EXIT1 ("internal writev consistency failure");
|
|
| 1549 |
++ /* Current buffer was partially written. */ |
|
| 1550 |
++ if (buffers->iov_len > (size_t) ret) |
|
| 1551 |
++ {
|
|
| 1552 |
++ buffers->iov_base += ret; |
|
| 1553 |
++ buffers->iov_len -= ret; |
|
| 1554 |
++ ret = 0; |
|
| 1555 |
++ } |
|
| 1556 |
++ else |
|
| 1557 |
++ {
|
|
| 1558 |
++ ret -= buffers->iov_len; |
|
| 1559 |
++ buffers->iov_len = 0; |
|
| 1560 |
++ ++buffers; |
|
| 1561 |
++ --count; |
|
| 1562 |
++ } |
|
| 1563 |
++ } |
|
| 1564 |
++ } |
|
| 1565 |
++} |
|
| 1566 |
++ |
|
| 1567 |
++/* Thread callback for handling a single established TCP connection to |
|
| 1568 |
++ a client. */ |
|
| 1569 |
++static void * |
|
| 1570 |
++server_thread_tcp_client (void *arg) |
|
| 1571 |
++{
|
|
| 1572 |
++ struct tcp_thread_closure *closure = arg; |
|
| 1573 |
++ |
|
| 1574 |
++ while (true) |
|
| 1575 |
++ {
|
|
| 1576 |
++ /* Read packet length. */ |
|
| 1577 |
++ uint16_t query_length; |
|
| 1578 |
++ if (!read_fully (closure->client_socket, |
|
| 1579 |
++ &query_length, sizeof (query_length), true)) |
|
| 1580 |
++ break; |
|
| 1581 |
++ query_length = ntohs (query_length); |
|
| 1582 |
++ |
|
| 1583 |
++ /* Read the packet. */ |
|
| 1584 |
++ unsigned char *query_buffer = xmalloc (query_length); |
|
| 1585 |
++ read_fully (closure->client_socket, query_buffer, query_length, false); |
|
| 1586 |
++ |
|
| 1587 |
++ struct query_info qinfo; |
|
| 1588 |
++ parse_query (&qinfo, query_buffer, query_length); |
|
| 1589 |
++ if (test_verbose > 0) |
|
| 1590 |
++ {
|
|
| 1591 |
++ if (test_verbose > 1) |
|
| 1592 |
++ printf ("info: UDP server %d: incoming query:"
|
|
| 1593 |
++ " %d bytes, %s/%u/%u, tnxid=0x%02x%02x\n", |
|
| 1594 |
++ closure->server_index, query_length, |
|
| 1595 |
++ qinfo.qname, qinfo.qclass, qinfo.qtype, |
|
| 1596 |
++ query_buffer[0], query_buffer[1]); |
|
| 1597 |
++ else |
|
| 1598 |
++ printf ("info: TCP server %d: incoming query:"
|
|
| 1599 |
++ " %u bytes, %s/%u/%u\n", |
|
| 1600 |
++ closure->server_index, query_length, |
|
| 1601 |
++ qinfo.qname, qinfo.qclass, qinfo.qtype); |
|
| 1602 |
++ } |
|
| 1603 |
++ |
|
| 1604 |
++ struct resolv_response_context ctx = |
|
| 1605 |
++ {
|
|
| 1606 |
++ .query_buffer = query_buffer, |
|
| 1607 |
++ .query_length = query_length, |
|
| 1608 |
++ .server_index = closure->server_index, |
|
| 1609 |
++ .tcp = true, |
|
| 1610 |
++ .edns = qinfo.edns, |
|
| 1611 |
++ }; |
|
| 1612 |
++ struct resolv_response_builder *b = response_builder_allocate |
|
| 1613 |
++ (query_buffer, query_length); |
|
| 1614 |
++ closure->obj->config.response_callback |
|
| 1615 |
++ (&ctx, b, qinfo.qname, qinfo.qclass, qinfo.qtype); |
|
| 1616 |
++ |
|
| 1617 |
++ if (b->drop) |
|
| 1618 |
++ {
|
|
| 1619 |
++ if (test_verbose) |
|
| 1620 |
++ printf ("info: TCP server %d: dropping response to %s/%u/%u\n",
|
|
| 1621 |
++ closure->server_index, |
|
| 1622 |
++ qinfo.qname, qinfo.qclass, qinfo.qtype); |
|
| 1623 |
++ } |
|
| 1624 |
++ else |
|
| 1625 |
++ {
|
|
| 1626 |
++ if (test_verbose) |
|
| 1627 |
++ printf ("info: TCP server %d: sending response: %zu bytes"
|
|
| 1628 |
++ " (for %s/%u/%u)\n", |
|
| 1629 |
++ closure->server_index, b->offset, |
|
| 1630 |
++ qinfo.qname, qinfo.qclass, qinfo.qtype); |
|
| 1631 |
++ uint16_t length = htons (b->offset); |
|
| 1632 |
++ size_t to_send = b->offset; |
|
| 1633 |
++ if (to_send < b->truncate_bytes) |
|
| 1634 |
++ to_send = 0; |
|
| 1635 |
++ else |
|
| 1636 |
++ to_send -= b->truncate_bytes; |
|
| 1637 |
++ struct iovec buffers[2] = |
|
| 1638 |
++ {
|
|
| 1639 |
++ {&length, sizeof (length)},
|
|
| 1640 |
++ {b->buffer, to_send}
|
|
| 1641 |
++ }; |
|
| 1642 |
++ writev_fully (closure->client_socket, buffers, 2); |
|
| 1643 |
++ } |
|
| 1644 |
++ bool close_flag = b->close; |
|
| 1645 |
++ response_builder_free (b); |
|
| 1646 |
++ free (query_buffer); |
|
| 1647 |
++ if (close_flag) |
|
| 1648 |
++ break; |
|
| 1649 |
++ } |
|
| 1650 |
++ |
|
| 1651 |
++ xclose (closure->client_socket); |
|
| 1652 |
++ free (closure); |
|
| 1653 |
++ return NULL; |
|
| 1654 |
++} |
|
| 1655 |
++ |
|
| 1656 |
++/* thread_callback for the TCP case. Accept connections and create a |
|
| 1657 |
++ new thread for each client. */ |
|
| 1658 |
++static void |
|
| 1659 |
++server_thread_tcp (struct resolv_test *obj, int server_index) |
|
| 1660 |
++{
|
|
| 1661 |
++ while (true) |
|
| 1662 |
++ {
|
|
| 1663 |
++ /* Get the client conenction. */ |
|
| 1664 |
++ int client_socket = xaccept |
|
| 1665 |
++ (obj->servers[server_index].socket_tcp, NULL, NULL); |
|
| 1666 |
++ |
|
| 1667 |
++ /* Check for termination. */ |
|
| 1668 |
++ xpthread_mutex_lock (&obj->lock); |
|
| 1669 |
++ if (obj->termination_requested) |
|
| 1670 |
++ {
|
|
| 1671 |
++ xpthread_mutex_unlock (&obj->lock); |
|
| 1672 |
++ xclose (client_socket); |
|
| 1673 |
++ break; |
|
| 1674 |
++ } |
|
| 1675 |
++ xpthread_mutex_unlock (&obj->lock); |
|
| 1676 |
++ |
|
| 1677 |
++ /* Spawn a new thread for handling this connection. */ |
|
| 1678 |
++ struct tcp_thread_closure *closure = xmalloc (sizeof (*closure)); |
|
| 1679 |
++ *closure = (struct tcp_thread_closure) |
|
| 1680 |
++ {
|
|
| 1681 |
++ .obj = obj, |
|
| 1682 |
++ .server_index = server_index, |
|
| 1683 |
++ .client_socket = client_socket, |
|
| 1684 |
++ }; |
|
| 1685 |
++ |
|
| 1686 |
++ pthread_t thr |
|
| 1687 |
++ = xpthread_create (NULL, server_thread_tcp_client, closure); |
|
| 1688 |
++ /* TODO: We should keep track of this thread so that we can |
|
| 1689 |
++ block in resolv_test_end until it has exited. */ |
|
| 1690 |
++ xpthread_detach (thr); |
|
| 1691 |
++ } |
|
| 1692 |
++} |
|
| 1693 |
++ |
|
| 1694 |
++/* Create UDP and TCP server sockets. */ |
|
| 1695 |
++static void |
|
| 1696 |
++make_server_sockets (struct resolv_test_server *server) |
|
| 1697 |
++{
|
|
| 1698 |
++ while (true) |
|
| 1699 |
++ {
|
|
| 1700 |
++ server->socket_udp = xsocket (AF_INET, SOCK_DGRAM, IPPROTO_UDP); |
|
| 1701 |
++ server->socket_tcp = xsocket (AF_INET, SOCK_STREAM, IPPROTO_TCP); |
|
| 1702 |
++ |
|
| 1703 |
++ /* Pick the address for the UDP socket. */ |
|
| 1704 |
++ server->address = (struct sockaddr_in) |
|
| 1705 |
++ {
|
|
| 1706 |
++ .sin_family = AF_INET, |
|
| 1707 |
++ .sin_addr = {.s_addr = htonl (INADDR_LOOPBACK)}
|
|
| 1708 |
++ }; |
|
| 1709 |
++ xbind (server->socket_udp, |
|
| 1710 |
++ (struct sockaddr *)&server->address, sizeof (server->address)); |
|
| 1711 |
++ |
|
| 1712 |
++ /* Retrieve the address. */ |
|
| 1713 |
++ socklen_t addrlen = sizeof (server->address); |
|
| 1714 |
++ xgetsockname (server->socket_udp, |
|
| 1715 |
++ (struct sockaddr *)&server->address, &addrlen); |
|
| 1716 |
++ |
|
| 1717 |
++ /* Bind the TCP socket to the same address. */ |
|
| 1718 |
++ {
|
|
| 1719 |
++ int on = 1; |
|
| 1720 |
++ xsetsockopt (server->socket_tcp, SOL_SOCKET, SO_REUSEADDR, |
|
| 1721 |
++ &on, sizeof (on)); |
|
| 1722 |
++ } |
|
| 1723 |
++ if (bind (server->socket_tcp, |
|
| 1724 |
++ (struct sockaddr *)&server->address, |
|
| 1725 |
++ sizeof (server->address)) != 0) |
|
| 1726 |
++ {
|
|
| 1727 |
++ /* Port collision. The UDP bind succeeded, but the TCP BIND |
|
| 1728 |
++ failed. We assume here that the kernel will pick the |
|
| 1729 |
++ next local UDP address randomly. */ |
|
| 1730 |
++ if (errno == EADDRINUSE) |
|
| 1731 |
++ {
|
|
| 1732 |
++ xclose (server->socket_udp); |
|
| 1733 |
++ xclose (server->socket_tcp); |
|
| 1734 |
++ continue; |
|
| 1735 |
++ } |
|
| 1736 |
++ FAIL_EXIT1 ("TCP bind: %m");
|
|
| 1737 |
++ } |
|
| 1738 |
++ xlisten (server->socket_tcp, 5); |
|
| 1739 |
++ break; |
|
| 1740 |
++ } |
|
| 1741 |
++} |
|
| 1742 |
++ |
|
| 1743 |
++/* One-time initialization of NSS. */ |
|
| 1744 |
++static void |
|
| 1745 |
++resolv_redirect_once (void) |
|
| 1746 |
++{
|
|
| 1747 |
++ /* Only use nss_dns. */ |
|
| 1748 |
++ __nss_configure_lookup ("hosts", "dns");
|
|
| 1749 |
++ __nss_configure_lookup ("networks", "dns");
|
|
| 1750 |
++ /* Enter a network namespace for isolation and firewall state |
|
| 1751 |
++ cleanup. The tests will still work if these steps fail, but they |
|
| 1752 |
++ may be less reliable. */ |
|
| 1753 |
++ support_become_root (); |
|
| 1754 |
++ support_enter_network_namespace (); |
|
| 1755 |
++} |
|
| 1756 |
++pthread_once_t resolv_redirect_once_var = PTHREAD_ONCE_INIT; |
|
| 1757 |
++ |
|
| 1758 |
++void |
|
| 1759 |
++resolv_test_init (void) |
|
| 1760 |
++{
|
|
| 1761 |
++ /* Perform one-time initialization of NSS. */ |
|
| 1762 |
++ xpthread_once (&resolv_redirect_once_var, resolv_redirect_once); |
|
| 1763 |
++} |
|
| 1764 |
++ |
|
| 1765 |
++/* Copy the search path from CONFIG.search to the _res object. */ |
|
| 1766 |
++static void |
|
| 1767 |
++set_search_path (struct resolv_redirect_config config) |
|
| 1768 |
++{
|
|
| 1769 |
++ memset (_res.defdname, 0, sizeof (_res.defdname)); |
|
| 1770 |
++ memset (_res.dnsrch, 0, sizeof (_res.dnsrch)); |
|
| 1771 |
++ |
|
| 1772 |
++ char *current = _res.defdname; |
|
| 1773 |
++ char *end = current + sizeof (_res.defdname); |
|
| 1774 |
++ |
|
| 1775 |
++ for (unsigned int i = 0; |
|
| 1776 |
++ i < sizeof (config.search) / sizeof (config.search[0]); ++i) |
|
| 1777 |
++ {
|
|
| 1778 |
++ if (config.search[i] == NULL) |
|
| 1779 |
++ continue; |
|
| 1780 |
++ |
|
| 1781 |
++ size_t length = strlen (config.search[i]) + 1; |
|
| 1782 |
++ size_t remaining = end - current; |
|
| 1783 |
++ TEST_VERIFY_EXIT (length <= remaining); |
|
| 1784 |
++ memcpy (current, config.search[i], length); |
|
| 1785 |
++ _res.dnsrch[i] = current; |
|
| 1786 |
++ current += length; |
|
| 1787 |
++ } |
|
| 1788 |
++} |
|
| 1789 |
++ |
|
| 1790 |
++struct resolv_test * |
|
| 1791 |
++resolv_test_start (struct resolv_redirect_config config) |
|
| 1792 |
++{
|
|
| 1793 |
++ /* Apply configuration defaults. */ |
|
| 1794 |
++ if (config.nscount == 0) |
|
| 1795 |
++ config.nscount = resolv_max_test_servers; |
|
| 1796 |
++ |
|
| 1797 |
++ struct resolv_test *obj = xmalloc (sizeof (*obj)); |
|
| 1798 |
++ *obj = (struct resolv_test) {
|
|
| 1799 |
++ .config = config, |
|
| 1800 |
++ .lock = PTHREAD_MUTEX_INITIALIZER, |
|
| 1801 |
++ }; |
|
| 1802 |
++ |
|
| 1803 |
++ resolv_test_init (); |
|
| 1804 |
++ |
|
| 1805 |
++ /* Create all the servers, to reserve the necessary ports. */ |
|
| 1806 |
++ for (int server_index = 0; server_index < config.nscount; ++server_index) |
|
| 1807 |
++ make_server_sockets (obj->servers + server_index); |
|
| 1808 |
++ |
|
| 1809 |
++ /* Start server threads. Disable the server ports, as |
|
| 1810 |
++ requested. */ |
|
| 1811 |
++ for (int server_index = 0; server_index < config.nscount; ++server_index) |
|
| 1812 |
++ {
|
|
| 1813 |
++ struct resolv_test_server *server = obj->servers + server_index; |
|
| 1814 |
++ if (config.servers[server_index].disable_udp) |
|
| 1815 |
++ {
|
|
| 1816 |
++ xclose (server->socket_udp); |
|
| 1817 |
++ server->socket_udp = -1; |
|
| 1818 |
++ } |
|
| 1819 |
++ else if (!config.single_thread_udp) |
|
| 1820 |
++ server->thread_udp = start_server_thread (obj, server_index, |
|
| 1821 |
++ server_thread_udp); |
|
| 1822 |
++ if (config.servers[server_index].disable_tcp) |
|
| 1823 |
++ {
|
|
| 1824 |
++ xclose (server->socket_tcp); |
|
| 1825 |
++ server->socket_tcp = -1; |
|
| 1826 |
++ } |
|
| 1827 |
++ else |
|
| 1828 |
++ server->thread_tcp = start_server_thread (obj, server_index, |
|
| 1829 |
++ server_thread_tcp); |
|
| 1830 |
++ } |
|
| 1831 |
++ if (config.single_thread_udp) |
|
| 1832 |
++ start_server_thread_udp_single (obj); |
|
| 1833 |
++ |
|
| 1834 |
++ int timeout = 1; |
|
| 1835 |
++ |
|
| 1836 |
++ /* Initialize libresolv. */ |
|
| 1837 |
++ TEST_VERIFY_EXIT (res_init () == 0); |
|
| 1838 |
++ |
|
| 1839 |
++ /* Disable IPv6 name server addresses. The code below only |
|
| 1840 |
++ overrides the IPv4 addresses. */ |
|
| 1841 |
++ __res_iclose (&_res, true); |
|
| 1842 |
++ _res._u._ext.nscount = 0; |
|
| 1843 |
++ |
|
| 1844 |
++ /* Redirect queries to the server socket. */ |
|
| 1845 |
++ if (test_verbose) |
|
| 1846 |
++ {
|
|
| 1847 |
++ printf ("info: old timeout value: %d\n", _res.retrans);
|
|
| 1848 |
++ printf ("info: old retry attempt value: %d\n", _res.retry);
|
|
| 1849 |
++ printf ("info: old _res.options: 0x%lx\n", _res.options);
|
|
| 1850 |
++ printf ("info: old _res.nscount value: %d\n", _res.nscount);
|
|
| 1851 |
++ printf ("info: old _res.ndots value: %d\n", _res.ndots);
|
|
| 1852 |
++ } |
|
| 1853 |
++ _res.retrans = timeout; |
|
| 1854 |
++ _res.retry = 4; |
|
| 1855 |
++ _res.nscount = config.nscount; |
|
| 1856 |
++ _res.options = RES_INIT | RES_RECURSE | RES_DEFNAMES | RES_DNSRCH; |
|
| 1857 |
++ _res.ndots = 1; |
|
| 1858 |
++ if (test_verbose) |
|
| 1859 |
++ {
|
|
| 1860 |
++ printf ("info: new timeout value: %d\n", _res.retrans);
|
|
| 1861 |
++ printf ("info: new retry attempt value: %d\n", _res.retry);
|
|
| 1862 |
++ printf ("info: new _res.options: 0x%lx\n", _res.options);
|
|
| 1863 |
++ printf ("info: new _res.nscount value: %d\n", _res.nscount);
|
|
| 1864 |
++ printf ("info: new _res.ndots value: %d\n", _res.ndots);
|
|
| 1865 |
++ } |
|
| 1866 |
++ for (int server_index = 0; server_index < config.nscount; ++server_index) |
|
| 1867 |
++ {
|
|
| 1868 |
++ _res.nsaddr_list[server_index] = obj->servers[server_index].address; |
|
| 1869 |
++ if (test_verbose) |
|
| 1870 |
++ {
|
|
| 1871 |
++ char buf[256]; |
|
| 1872 |
++ TEST_VERIFY_EXIT |
|
| 1873 |
++ (inet_ntop (AF_INET, &obj->servers[server_index].address.sin_addr, |
|
| 1874 |
++ buf, sizeof (buf)) != NULL); |
|
| 1875 |
++ printf ("info: server %d: %s/%u\n",
|
|
| 1876 |
++ server_index, buf, |
|
| 1877 |
++ htons (obj->servers[server_index].address.sin_port)); |
|
| 1878 |
++ } |
|
| 1879 |
++ } |
|
| 1880 |
++ |
|
| 1881 |
++ set_search_path (config); |
|
| 1882 |
++ |
|
| 1883 |
++ return obj; |
|
| 1884 |
++} |
|
| 1885 |
++ |
|
| 1886 |
++void |
|
| 1887 |
++resolv_test_end (struct resolv_test *obj) |
|
| 1888 |
++{
|
|
| 1889 |
++ res_close (); |
|
| 1890 |
++ |
|
| 1891 |
++ xpthread_mutex_lock (&obj->lock); |
|
| 1892 |
++ obj->termination_requested = true; |
|
| 1893 |
++ xpthread_mutex_unlock (&obj->lock); |
|
| 1894 |
++ |
|
| 1895 |
++ /* Send trigger packets to unblock the server threads. */ |
|
| 1896 |
++ for (int server_index = 0; server_index < obj->config.nscount; |
|
| 1897 |
++ ++server_index) |
|
| 1898 |
++ {
|
|
| 1899 |
++ if (!obj->config.servers[server_index].disable_udp) |
|
| 1900 |
++ {
|
|
| 1901 |
++ int sock = xsocket (AF_INET, SOCK_DGRAM, IPPROTO_UDP); |
|
| 1902 |
++ xsendto (sock, "", 1, 0, |
|
| 1903 |
++ (struct sockaddr *) &obj->servers[server_index].address, |
|
| 1904 |
++ sizeof (obj->servers[server_index].address)); |
|
| 1905 |
++ xclose (sock); |
|
| 1906 |
++ } |
|
| 1907 |
++ if (!obj->config.servers[server_index].disable_tcp) |
|
| 1908 |
++ {
|
|
| 1909 |
++ int sock = xsocket (AF_INET, SOCK_STREAM, IPPROTO_TCP); |
|
| 1910 |
++ xconnect (sock, |
|
| 1911 |
++ (struct sockaddr *) &obj->servers[server_index].address, |
|
| 1912 |
++ sizeof (obj->servers[server_index].address)); |
|
| 1913 |
++ xclose (sock); |
|
| 1914 |
++ } |
|
| 1915 |
++ } |
|
| 1916 |
++ |
|
| 1917 |
++ if (obj->config.single_thread_udp) |
|
| 1918 |
++ xpthread_join (obj->thread_udp_single); |
|
| 1919 |
++ |
|
| 1920 |
++ /* Wait for the server threads to terminate. */ |
|
| 1921 |
++ for (int server_index = 0; server_index < obj->config.nscount; |
|
| 1922 |
++ ++server_index) |
|
| 1923 |
++ {
|
|
| 1924 |
++ if (!obj->config.servers[server_index].disable_udp) |
|
| 1925 |
++ {
|
|
| 1926 |
++ if (!obj->config.single_thread_udp) |
|
| 1927 |
++ xpthread_join (obj->servers[server_index].thread_udp); |
|
| 1928 |
++ xclose (obj->servers[server_index].socket_udp); |
|
| 1929 |
++ } |
|
| 1930 |
++ if (!obj->config.servers[server_index].disable_tcp) |
|
| 1931 |
++ {
|
|
| 1932 |
++ xpthread_join (obj->servers[server_index].thread_tcp); |
|
| 1933 |
++ xclose (obj->servers[server_index].socket_tcp); |
|
| 1934 |
++ } |
|
| 1935 |
++ } |
|
| 1936 |
++ |
|
| 1937 |
++ free (obj); |
|
| 1938 |
++} |
|
| 1939 |
+diff --git a/support/resolv_test.h b/support/resolv_test.h |
|
| 1940 |
+new file mode 100644 |
|
| 1941 |
+index 7a9f1f7..6498751 |
|
| 1942 |
+--- /dev/null |
|
| 1943 |
+@@ -0,0 +1,180 @@ |
|
| 1944 |
++/* DNS test framework and libresolv redirection. |
|
| 1945 |
++ Copyright (C) 2016-2017 Free Software Foundation, Inc. |
|
| 1946 |
++ This file is part of the GNU C Library. |
|
| 1947 |
++ |
|
| 1948 |
++ The GNU C Library is free software; you can redistribute it and/or |
|
| 1949 |
++ modify it under the terms of the GNU Lesser General Public |
|
| 1950 |
++ License as published by the Free Software Foundation; either |
|
| 1951 |
++ version 2.1 of the License, or (at your option) any later version. |
|
| 1952 |
++ |
|
| 1953 |
++ The GNU C Library is distributed in the hope that it will be useful, |
|
| 1954 |
++ but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
| 1955 |
++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
|
| 1956 |
++ Lesser General Public License for more details. |
|
| 1957 |
++ |
|
| 1958 |
++ You should have received a copy of the GNU Lesser General Public |
|
| 1959 |
++ License along with the GNU C Library; if not, see |
|
| 1960 |
++ <http://www.gnu.org/licenses/>. */ |
|
| 1961 |
++ |
|
| 1962 |
++#ifndef SUPPORT_RESOLV_TEST_H |
|
| 1963 |
++#define SUPPORT_RESOLV_TEST_H |
|
| 1964 |
++ |
|
| 1965 |
++#include <arpa/nameser.h> |
|
| 1966 |
++#include <stdbool.h> |
|
| 1967 |
++#include <sys/cdefs.h> |
|
| 1968 |
++ |
|
| 1969 |
++__BEGIN_DECLS |
|
| 1970 |
++ |
|
| 1971 |
++/* Information about EDNS properties of a DNS query. */ |
|
| 1972 |
++struct resolv_edns_info |
|
| 1973 |
++{
|
|
| 1974 |
++ bool active; |
|
| 1975 |
++ uint8_t extended_rcode; |
|
| 1976 |
++ uint8_t version; |
|
| 1977 |
++ uint16_t flags; |
|
| 1978 |
++ uint16_t payload_size; |
|
| 1979 |
++}; |
|
| 1980 |
++ |
|
| 1981 |
++/* This struct provides context information when the response callback |
|
| 1982 |
++ specified in struct resolv_redirect_config is invoked. */ |
|
| 1983 |
++struct resolv_response_context |
|
| 1984 |
++{
|
|
| 1985 |
++ const unsigned char *query_buffer; |
|
| 1986 |
++ size_t query_length; |
|
| 1987 |
++ int server_index; |
|
| 1988 |
++ bool tcp; |
|
| 1989 |
++ struct resolv_edns_info edns; |
|
| 1990 |
++}; |
|
| 1991 |
++ |
|
| 1992 |
++/* This opaque struct is used to construct responses from within the |
|
| 1993 |
++ response callback function. */ |
|
| 1994 |
++struct resolv_response_builder; |
|
| 1995 |
++ |
|
| 1996 |
++/* This opaque struct collects information about the resolver testing |
|
| 1997 |
++ currently in progress. */ |
|
| 1998 |
++struct resolv_test; |
|
| 1999 |
++ |
|
| 2000 |
++enum |
|
| 2001 |
++ {
|
|
| 2002 |
++ /* Maximum number of test servers supported by the framework. */ |
|
| 2003 |
++ resolv_max_test_servers = 3, |
|
| 2004 |
++ }; |
|
| 2005 |
++ |
|
| 2006 |
++/* Configuration settings specific to individual test servers. */ |
|
| 2007 |
++struct resolv_redirect_server_config |
|
| 2008 |
++{
|
|
| 2009 |
++ bool disable_tcp; /* If true, no TCP server is listening. */ |
|
| 2010 |
++ bool disable_udp; /* If true, no UDP server is listening. */ |
|
| 2011 |
++}; |
|
| 2012 |
++ |
|
| 2013 |
++/* Instructions for setting up the libresolv redirection. */ |
|
| 2014 |
++struct resolv_redirect_config |
|
| 2015 |
++{
|
|
| 2016 |
++ /* The response_callback function is called for every incoming DNS |
|
| 2017 |
++ packet, over UDP or TCP. It must be specified, the other |
|
| 2018 |
++ configuration settings are optional. */ |
|
| 2019 |
++ void (*response_callback) (const struct resolv_response_context *, |
|
| 2020 |
++ struct resolv_response_builder *, |
|
| 2021 |
++ const char *qname, |
|
| 2022 |
++ uint16_t qclass, uint16_t qtype); |
|
| 2023 |
++ |
|
| 2024 |
++ /* Per-server configuration. */ |
|
| 2025 |
++ struct resolv_redirect_server_config servers[resolv_max_test_servers]; |
|
| 2026 |
++ |
|
| 2027 |
++ /* Search path entries. The first entry serves as the default |
|
| 2028 |
++ domain name as well. */ |
|
| 2029 |
++ const char *search[7]; |
|
| 2030 |
++ |
|
| 2031 |
++ /* Number of servers to activate in resolv. 0 means the default, |
|
| 2032 |
++ resolv_max_test_servers. */ |
|
| 2033 |
++ int nscount; |
|
| 2034 |
++ |
|
| 2035 |
++ /* If true, use a single thread to process all UDP queries. This |
|
| 2036 |
++ may results in more predictable ordering of queries and |
|
| 2037 |
++ responses. */ |
|
| 2038 |
++ bool single_thread_udp; |
|
| 2039 |
++}; |
|
| 2040 |
++ |
|
| 2041 |
++/* Configure NSS to use, nss_dns only for aplicable databases, and try |
|
| 2042 |
++ to put the process into a network namespace for better isolation. |
|
| 2043 |
++ This may have to be called before resolv_test_start, before the |
|
| 2044 |
++ process creates any threads. Otherwise, initialization is |
|
| 2045 |
++ performed by resolv_test_start implicitly. */ |
|
| 2046 |
++void resolv_test_init (void); |
|
| 2047 |
++ |
|
| 2048 |
++/* Initiate resolver testing. This updates the _res variable as |
|
| 2049 |
++ needed. As a side effect, NSS is reconfigured to use nss_dns only |
|
| 2050 |
++ for aplicable databases, and the process may enter a network |
|
| 2051 |
++ namespace for better isolation. */ |
|
| 2052 |
++struct resolv_test *resolv_test_start (struct resolv_redirect_config); |
|
| 2053 |
++ |
|
| 2054 |
++/* Call this function at the end of resolver testing, to free |
|
| 2055 |
++ resources and report pending errors (if any). */ |
|
| 2056 |
++void resolv_test_end (struct resolv_test *); |
|
| 2057 |
++ |
|
| 2058 |
++/* The remaining facilities in this file are used for constructing |
|
| 2059 |
++ response packets from the response_callback function. */ |
|
| 2060 |
++ |
|
| 2061 |
++/* Special settings for constructing responses from the callback. */ |
|
| 2062 |
++struct resolv_response_flags |
|
| 2063 |
++{
|
|
| 2064 |
++ /* 4-bit response code to incorporate into the response. */ |
|
| 2065 |
++ unsigned char rcode; |
|
| 2066 |
++ |
|
| 2067 |
++ /* If true, the TC (truncation) flag will be set. */ |
|
| 2068 |
++ bool tc; |
|
| 2069 |
++ |
|
| 2070 |
++ /* Initial section count values. Can be used to artificially |
|
| 2071 |
++ increase the counts, for malformed packet testing.*/ |
|
| 2072 |
++ unsigned short qdcount; |
|
| 2073 |
++ unsigned short ancount; |
|
| 2074 |
++ unsigned short nscount; |
|
| 2075 |
++ unsigned short adcount; |
|
| 2076 |
++}; |
|
| 2077 |
++ |
|
| 2078 |
++/* Begin a new response with the requested flags. Must be called |
|
| 2079 |
++ first. */ |
|
| 2080 |
++void resolv_response_init (struct resolv_response_builder *, |
|
| 2081 |
++ struct resolv_response_flags); |
|
| 2082 |
++ |
|
| 2083 |
++/* Switches to the section in the response packet. Only forward |
|
| 2084 |
++ movement is supported. */ |
|
| 2085 |
++void resolv_response_section (struct resolv_response_builder *, ns_sect); |
|
| 2086 |
++ |
|
| 2087 |
++/* Add a question record to the question section. */ |
|
| 2088 |
++void resolv_response_add_question (struct resolv_response_builder *, |
|
| 2089 |
++ const char *name, uint16_t class, |
|
| 2090 |
++ uint16_t type); |
|
| 2091 |
++/* Starts a new resource record with the specified owner name, class, |
|
| 2092 |
++ type, and TTL. Data is supplied with resolv_response_add_data or |
|
| 2093 |
++ resolv_response_add_name. */ |
|
| 2094 |
++void resolv_response_open_record (struct resolv_response_builder *, |
|
| 2095 |
++ const char *name, uint16_t class, |
|
| 2096 |
++ uint16_t type, uint32_t ttl); |
|
| 2097 |
++ |
|
| 2098 |
++/* Add unstructed bytes to the RDATA part of a resource record. */ |
|
| 2099 |
++void resolv_response_add_data (struct resolv_response_builder *, |
|
| 2100 |
++ const void *, size_t); |
|
| 2101 |
++ |
|
| 2102 |
++/* Add a compressed domain name to the RDATA part of a resource |
|
| 2103 |
++ record. */ |
|
| 2104 |
++void resolv_response_add_name (struct resolv_response_builder *, |
|
| 2105 |
++ const char *name); |
|
| 2106 |
++ |
|
| 2107 |
++/* Mark the end of the constructed record. Must be called last. */ |
|
| 2108 |
++void resolv_response_close_record (struct resolv_response_builder *); |
|
| 2109 |
++ |
|
| 2110 |
++/* Drop this query packet (that is, do not send a response, not even |
|
| 2111 |
++ an empty packet). */ |
|
| 2112 |
++void resolv_response_drop (struct resolv_response_builder *); |
|
| 2113 |
++ |
|
| 2114 |
++/* In TCP mode, close the connection after this packet (if a response |
|
| 2115 |
++ is sent). */ |
|
| 2116 |
++void resolv_response_close (struct resolv_response_builder *); |
|
| 2117 |
++ |
|
| 2118 |
++/* The size of the response packet built so far. */ |
|
| 2119 |
++size_t resolv_response_length (const struct resolv_response_builder *); |
|
| 2120 |
++ |
|
| 2121 |
++__END_DECLS |
|
| 2122 |
++ |
|
| 2123 |
++#endif /* SUPPORT_RESOLV_TEST_H */ |
|
| 2124 |
+-- |
|
| 2125 |
+2.9.3 |
| ... | ... |
@@ -6,7 +6,7 @@ |
| 6 | 6 |
Summary: Main C library |
| 7 | 7 |
Name: glibc |
| 8 | 8 |
Version: 2.22 |
| 9 |
-Release: 22%{?dist}
|
|
| 9 |
+Release: 23%{?dist}
|
|
| 10 | 10 |
License: LGPLv2+ |
| 11 | 11 |
URL: http://www.gnu.org/software/libc |
| 12 | 12 |
Group: Applications/System |
| ... | ... |
@@ -51,6 +51,7 @@ Patch23: glibc-fix-CVE-2018-6485.patch |
| 51 | 51 |
Patch24: glibc-fix-CVE-2017-18269.patch |
| 52 | 52 |
Patch25: glibc-fix-CVE-2018-11236.patch |
| 53 | 53 |
Patch26: glibc-fix-CVE-2017-15671.patch |
| 54 |
+Patch27: glibc-fix-CVE-2017-12132.patch |
|
| 54 | 55 |
Provides: rtld(GNU_HASH) |
| 55 | 56 |
Requires: filesystem |
| 56 | 57 |
%description |
| ... | ... |
@@ -103,6 +104,7 @@ sed -i 's/\\$$(pwd)/`pwd`/' timezone/Makefile |
| 103 | 103 |
%patch24 -p1 |
| 104 | 104 |
%patch25 -p1 |
| 105 | 105 |
%patch26 -p1 |
| 106 |
+%patch27 -p1 |
|
| 106 | 107 |
|
| 107 | 108 |
install -vdm 755 %{_builddir}/%{name}-build
|
| 108 | 109 |
# do not try to explicitly provide GLIBC_PRIVATE versioned libraries |
| ... | ... |
@@ -230,6 +232,8 @@ popd |
| 230 | 230 |
%{_datarootdir}/locale/locale.alias
|
| 231 | 231 |
|
| 232 | 232 |
%changelog |
| 233 |
+* Tue Jan 29 2019 Keerthana K <keerthanak@vmware.com> 2.22-23 |
|
| 234 |
+- Fix for CVE-2017-12132. |
|
| 233 | 235 |
* Thu Aug 09 2018 Keerthana K <keerthanak@vmware.com> 2.22-22 |
| 234 | 236 |
- Fix for CVE-2017-15761. |
| 235 | 237 |
* Tue Jun 26 2018 Keerthana K <keerthnanak@vmware.com> 2.22-21 |