1 /*
2 * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
3 *
4 * Permission to use, copy, modify, and/or distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
9 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
10 * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
11 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
12 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
13 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
14 * PERFORMANCE OF THIS SOFTWARE.
15 */
16
17 /*! \file */
18
19 #include <stdint.h>
20 #include <stdlib.h>
21
22 #include <isc/buffer.h>
23 #include <isc/util.h>
24
25 #include <dns/name.h>
26 #include <dns/rdata.h>
27 #include <dns/rdataset.h>
28 #include <dns/compress.h>
29
30 void
dns_rdataset_init(dns_rdataset_t * rdataset)31 dns_rdataset_init(dns_rdataset_t *rdataset) {
32
33 /*
34 * Make 'rdataset' a valid, disassociated rdataset.
35 */
36
37 REQUIRE(rdataset != NULL);
38
39 rdataset->methods = NULL;
40 ISC_LINK_INIT(rdataset, link);
41 rdataset->rdclass = 0;
42 rdataset->type = 0;
43 rdataset->ttl = 0;
44 rdataset->covers = 0;
45 rdataset->attributes = 0;
46 rdataset->count = UINT32_MAX;
47 rdataset->private1 = NULL;
48 rdataset->private2 = NULL;
49 }
50
51 void
dns_rdataset_disassociate(dns_rdataset_t * rdataset)52 dns_rdataset_disassociate(dns_rdataset_t *rdataset) {
53
54 /*
55 * Disassociate 'rdataset' from its rdata, allowing it to be reused.
56 */
57
58 REQUIRE(rdataset->methods != NULL);
59
60 (rdataset->methods->disassociate)(rdataset);
61 rdataset->methods = NULL;
62 ISC_LINK_INIT(rdataset, link);
63 rdataset->rdclass = 0;
64 rdataset->type = 0;
65 rdataset->ttl = 0;
66 rdataset->covers = 0;
67 rdataset->attributes = 0;
68 rdataset->count = UINT32_MAX;
69 rdataset->private1 = NULL;
70 rdataset->private2 = NULL;
71 }
72
73 int
dns_rdataset_isassociated(dns_rdataset_t * rdataset)74 dns_rdataset_isassociated(dns_rdataset_t *rdataset) {
75 /*
76 * Is 'rdataset' associated?
77 */
78
79 if (rdataset->methods != NULL)
80 return (1);
81
82 return (0);
83 }
84
85 static void
question_disassociate(dns_rdataset_t * rdataset)86 question_disassociate(dns_rdataset_t *rdataset) {
87 UNUSED(rdataset);
88 }
89
90 static isc_result_t
question_cursor(dns_rdataset_t * rdataset)91 question_cursor(dns_rdataset_t *rdataset) {
92 UNUSED(rdataset);
93
94 return (ISC_R_NOMORE);
95 }
96
97 static void
question_current(dns_rdataset_t * rdataset,dns_rdata_t * rdata)98 question_current(dns_rdataset_t *rdataset, dns_rdata_t *rdata) {
99 /*
100 * This routine should never be called.
101 */
102 UNUSED(rdataset);
103 UNUSED(rdata);
104
105 REQUIRE(0);
106 }
107
108 static void
question_clone(dns_rdataset_t * source,dns_rdataset_t * target)109 question_clone(dns_rdataset_t *source, dns_rdataset_t *target) {
110 *target = *source;
111 }
112
113 static unsigned int
question_count(dns_rdataset_t * rdataset)114 question_count(dns_rdataset_t *rdataset) {
115 /*
116 * This routine should never be called.
117 */
118 UNUSED(rdataset);
119 REQUIRE(0);
120
121 return (0);
122 }
123
124 static dns_rdatasetmethods_t question_methods = {
125 question_disassociate,
126 question_cursor,
127 question_cursor,
128 question_current,
129 question_clone,
130 question_count,
131 };
132
133 void
dns_rdataset_makequestion(dns_rdataset_t * rdataset,dns_rdataclass_t rdclass,dns_rdatatype_t type)134 dns_rdataset_makequestion(dns_rdataset_t *rdataset, dns_rdataclass_t rdclass,
135 dns_rdatatype_t type)
136 {
137
138 /*
139 * Make 'rdataset' a valid, associated, question rdataset, with a
140 * question class of 'rdclass' and type 'type'.
141 */
142
143 REQUIRE(rdataset->methods == NULL);
144
145 rdataset->methods = &question_methods;
146 rdataset->rdclass = rdclass;
147 rdataset->type = type;
148 rdataset->attributes |= DNS_RDATASETATTR_QUESTION;
149 }
150
151 isc_result_t
dns_rdataset_first(dns_rdataset_t * rdataset)152 dns_rdataset_first(dns_rdataset_t *rdataset) {
153
154 /*
155 * Move the rdata cursor to the first rdata in the rdataset (if any).
156 */
157
158 REQUIRE(rdataset->methods != NULL);
159
160 return ((rdataset->methods->first)(rdataset));
161 }
162
163 isc_result_t
dns_rdataset_next(dns_rdataset_t * rdataset)164 dns_rdataset_next(dns_rdataset_t *rdataset) {
165
166 /*
167 * Move the rdata cursor to the next rdata in the rdataset (if any).
168 */
169
170 REQUIRE(rdataset->methods != NULL);
171
172 return ((rdataset->methods->next)(rdataset));
173 }
174
175 void
dns_rdataset_current(dns_rdataset_t * rdataset,dns_rdata_t * rdata)176 dns_rdataset_current(dns_rdataset_t *rdataset, dns_rdata_t *rdata) {
177
178 /*
179 * Make 'rdata' refer to the current rdata.
180 */
181
182 REQUIRE(rdataset->methods != NULL);
183
184 (rdataset->methods->current)(rdataset, rdata);
185 }
186
187 #define MAX_SHUFFLE 32
188
189 struct towire_sort {
190 int key;
191 dns_rdata_t *rdata;
192 };
193
194 static isc_result_t
towiresorted(dns_rdataset_t * rdataset,const dns_name_t * owner_name,dns_compress_t * cctx,isc_buffer_t * target,unsigned int * countp)195 towiresorted(dns_rdataset_t *rdataset, const dns_name_t *owner_name,
196 dns_compress_t *cctx, isc_buffer_t *target, unsigned int *countp)
197 {
198 dns_rdata_t rdata = DNS_RDATA_INIT;
199 isc_region_t r;
200 isc_result_t result;
201 unsigned int i, count = 0;
202 isc_buffer_t savedbuffer, rdlen;
203 unsigned int headlen;
204 int question = 0;
205 int shuffle = 0;
206 dns_rdata_t *in = NULL, in_fixed[MAX_SHUFFLE];
207 struct towire_sort *out = NULL, out_fixed[MAX_SHUFFLE];
208
209 /*
210 * Convert 'rdataset' to wire format, compressing names as specified
211 * in cctx, and storing the result in 'target'.
212 */
213
214 REQUIRE(rdataset->methods != NULL);
215 REQUIRE(countp != NULL);
216 REQUIRE(cctx != NULL);
217
218 if ((rdataset->attributes & DNS_RDATASETATTR_QUESTION) != 0) {
219 question = 1;
220 count = 1;
221 result = dns_rdataset_first(rdataset);
222 INSIST(result == ISC_R_NOMORE);
223 } else {
224 count = (rdataset->methods->count)(rdataset);
225 result = dns_rdataset_first(rdataset);
226 if (result == ISC_R_NOMORE)
227 return (ISC_R_SUCCESS);
228 if (result != ISC_R_SUCCESS)
229 return (result);
230 }
231
232 /*
233 * Do we want to shuffle this answer?
234 */
235 if (!question && count > 1 && rdataset->type != dns_rdatatype_rrsig)
236 shuffle = 1;
237
238 if (shuffle && count > MAX_SHUFFLE) {
239 in = reallocarray(NULL, count, sizeof(*in));
240 out = reallocarray(NULL, count, sizeof(*out));
241 if (in == NULL || out == NULL)
242 shuffle = 0;
243 } else {
244 in = in_fixed;
245 out = out_fixed;
246 }
247
248 if (shuffle) {
249 uint32_t val;
250 unsigned int j;
251
252 /*
253 * First we get handles to all of the rdata.
254 */
255 i = 0;
256 do {
257 INSIST(i < count);
258 dns_rdata_init(&in[i]);
259 dns_rdataset_current(rdataset, &in[i]);
260 i++;
261 result = dns_rdataset_next(rdataset);
262 } while (result == ISC_R_SUCCESS);
263 if (result != ISC_R_NOMORE)
264 goto cleanup;
265 INSIST(i == count);
266
267 /*
268 * Now we shuffle.
269 */
270
271 /*
272 * "Cyclic" order.
273 */
274
275 val = rdataset->count;
276 if (val == UINT32_MAX)
277 val = arc4random();
278 j = val % count;
279 for (i = 0; i < count; i++) {
280 out[i].key = 0; /* Unused */
281 out[i].rdata = &in[j];
282 j++;
283 if (j == count)
284 j = 0; /* Wrap around. */
285 }
286 }
287
288 savedbuffer = *target;
289 i = 0;
290
291 do {
292 /*
293 * Copy out the name, type, class, ttl.
294 */
295
296 dns_compress_setmethods(cctx, DNS_COMPRESS_GLOBAL14);
297 result = dns_name_towire(owner_name, cctx, target);
298 if (result != ISC_R_SUCCESS)
299 goto rollback;
300 headlen = sizeof(dns_rdataclass_t) + sizeof(dns_rdatatype_t);
301 if (!question)
302 headlen += sizeof(dns_ttl_t)
303 + 2; /* XXX 2 for rdata len */
304 isc_buffer_availableregion(target, &r);
305 if (r.length < headlen) {
306 result = ISC_R_NOSPACE;
307 goto rollback;
308 }
309 isc_buffer_putuint16(target, rdataset->type);
310 isc_buffer_putuint16(target, rdataset->rdclass);
311 if (!question) {
312 isc_buffer_putuint32(target, rdataset->ttl);
313
314 /*
315 * Save space for rdlen.
316 */
317 rdlen = *target;
318 isc_buffer_add(target, 2);
319
320 /*
321 * Copy out the rdata
322 */
323 if (shuffle)
324 rdata = *(out[i].rdata);
325 else {
326 dns_rdata_reset(&rdata);
327 dns_rdataset_current(rdataset, &rdata);
328 }
329 result = dns_rdata_towire(&rdata, cctx, target);
330 if (result != ISC_R_SUCCESS)
331 goto rollback;
332 INSIST((target->used >= rdlen.used + 2) &&
333 (target->used - rdlen.used - 2 < 65536));
334 isc_buffer_putuint16(&rdlen,
335 (uint16_t)(target->used -
336 rdlen.used - 2));
337 }
338
339 if (shuffle) {
340 i++;
341 if (i == count)
342 result = ISC_R_NOMORE;
343 else
344 result = ISC_R_SUCCESS;
345 } else {
346 result = dns_rdataset_next(rdataset);
347 }
348 } while (result == ISC_R_SUCCESS);
349
350 if (result != ISC_R_NOMORE)
351 goto rollback;
352
353 *countp += count;
354
355 result = ISC_R_SUCCESS;
356 goto cleanup;
357
358 rollback:
359 INSIST(savedbuffer.used < 65536);
360 dns_compress_rollback(cctx, (uint16_t)savedbuffer.used);
361 *countp = 0;
362 *target = savedbuffer;
363
364 cleanup:
365 if (out != NULL && out != out_fixed)
366 free(out);
367 if (in != NULL && in != in_fixed)
368 free(in);
369 return (result);
370 }
371
372 isc_result_t
dns_rdataset_towiresorted(dns_rdataset_t * rdataset,const dns_name_t * owner_name,dns_compress_t * cctx,isc_buffer_t * target,unsigned int * countp)373 dns_rdataset_towiresorted(dns_rdataset_t *rdataset,
374 const dns_name_t *owner_name,
375 dns_compress_t *cctx,
376 isc_buffer_t *target,
377 unsigned int *countp)
378 {
379 return (towiresorted(rdataset, owner_name, cctx, target, countp));
380 }
381
382 isc_result_t
dns_rdataset_towire(dns_rdataset_t * rdataset,dns_name_t * owner_name,dns_compress_t * cctx,isc_buffer_t * target,unsigned int * countp)383 dns_rdataset_towire(dns_rdataset_t *rdataset,
384 dns_name_t *owner_name,
385 dns_compress_t *cctx,
386 isc_buffer_t *target,
387 unsigned int *countp)
388 {
389 return (towiresorted(rdataset, owner_name, cctx, target, countp));
390 }
391