1 /* MIT License
2 *
3 * Copyright (c) 2023 Brad House
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining a copy
6 * of this software and associated documentation files (the "Software"), to deal
7 * in the Software without restriction, including without limitation the rights
8 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 * copies of the Software, and to permit persons to whom the Software is
10 * furnished to do so, subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice (including the next
13 * paragraph) shall be included in all copies or substantial portions of the
14 * Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 * SOFTWARE.
23 *
24 * SPDX-License-Identifier: MIT
25 */
26 #include "ares_private.h"
27 #include <limits.h>
28 #ifdef HAVE_STDINT_H
29 # include <stdint.h>
30 #endif
31
32
ares_dns_write_header(const ares_dns_record_t * dnsrec,ares_buf_t * buf)33 static ares_status_t ares_dns_write_header(const ares_dns_record_t *dnsrec,
34 ares_buf_t *buf)
35 {
36 unsigned short u16;
37 unsigned short opcode;
38 unsigned short rcode;
39
40 ares_status_t status;
41
42 /* ID */
43 status = ares_buf_append_be16(buf, dnsrec->id);
44 if (status != ARES_SUCCESS) {
45 return status; /* LCOV_EXCL_LINE: OutOfMemory */
46 }
47
48 /* Flags */
49 u16 = 0;
50
51 /* QR */
52 if (dnsrec->flags & ARES_FLAG_QR) {
53 u16 |= 0x8000;
54 }
55
56 /* OPCODE */
57 opcode = (unsigned short)(dnsrec->opcode & 0xF);
58 opcode <<= 11;
59 u16 |= opcode;
60
61 /* AA */
62 if (dnsrec->flags & ARES_FLAG_AA) {
63 u16 |= 0x400;
64 }
65
66 /* TC */
67 if (dnsrec->flags & ARES_FLAG_TC) {
68 u16 |= 0x200;
69 }
70
71 /* RD */
72 if (dnsrec->flags & ARES_FLAG_RD) {
73 u16 |= 0x100;
74 }
75
76 /* RA */
77 if (dnsrec->flags & ARES_FLAG_RA) {
78 u16 |= 0x80;
79 }
80
81 /* Z -- unused */
82
83 /* AD */
84 if (dnsrec->flags & ARES_FLAG_AD) {
85 u16 |= 0x20;
86 }
87
88 /* CD */
89 if (dnsrec->flags & ARES_FLAG_CD) {
90 u16 |= 0x10;
91 }
92
93 /* RCODE */
94 if (dnsrec->rcode > 15 && ares_dns_get_opt_rr_const(dnsrec) == NULL) {
95 /* Must have OPT RR in order to write extended error codes */
96 rcode = ARES_RCODE_SERVFAIL;
97 } else {
98 rcode = (unsigned short)(dnsrec->rcode & 0xF);
99 }
100 u16 |= rcode;
101
102 status = ares_buf_append_be16(buf, u16);
103 if (status != ARES_SUCCESS) {
104 return status; /* LCOV_EXCL_LINE: OutOfMemory */
105 }
106
107 /* QDCOUNT */
108 status = ares_buf_append_be16(
109 buf, (unsigned short)ares_dns_record_query_cnt(dnsrec));
110 if (status != ARES_SUCCESS) {
111 return status; /* LCOV_EXCL_LINE: OutOfMemory */
112 }
113
114 /* ANCOUNT */
115 status = ares_buf_append_be16(
116 buf, (unsigned short)ares_dns_record_rr_cnt(dnsrec, ARES_SECTION_ANSWER));
117 if (status != ARES_SUCCESS) {
118 return status; /* LCOV_EXCL_LINE: OutOfMemory */
119 }
120
121 /* NSCOUNT */
122 status = ares_buf_append_be16(buf, (unsigned short)ares_dns_record_rr_cnt(
123 dnsrec, ARES_SECTION_AUTHORITY));
124 if (status != ARES_SUCCESS) {
125 return status; /* LCOV_EXCL_LINE: OutOfMemory */
126 }
127
128 /* ARCOUNT */
129 status = ares_buf_append_be16(buf, (unsigned short)ares_dns_record_rr_cnt(
130 dnsrec, ARES_SECTION_ADDITIONAL));
131 if (status != ARES_SUCCESS) {
132 return status; /* LCOV_EXCL_LINE: OutOfMemory */
133 }
134
135 return ARES_SUCCESS;
136 }
137
ares_dns_write_questions(const ares_dns_record_t * dnsrec,ares_llist_t ** namelist,ares_buf_t * buf)138 static ares_status_t ares_dns_write_questions(const ares_dns_record_t *dnsrec,
139 ares_llist_t **namelist,
140 ares_buf_t *buf)
141 {
142 size_t i;
143
144 for (i = 0; i < ares_dns_record_query_cnt(dnsrec); i++) {
145 ares_status_t status;
146 const char *name = NULL;
147 ares_dns_rec_type_t qtype;
148 ares_dns_class_t qclass;
149
150 status = ares_dns_record_query_get(dnsrec, i, &name, &qtype, &qclass);
151 if (status != ARES_SUCCESS) {
152 return status;
153 }
154
155 /* Name */
156 status = ares_dns_name_write(buf, namelist, ARES_TRUE, name);
157 if (status != ARES_SUCCESS) {
158 return status;
159 }
160
161 /* Type */
162 status = ares_buf_append_be16(buf, (unsigned short)qtype);
163 if (status != ARES_SUCCESS) {
164 return status; /* LCOV_EXCL_LINE: OutOfMemory */
165 }
166
167 /* Class */
168 status = ares_buf_append_be16(buf, (unsigned short)qclass);
169 if (status != ARES_SUCCESS) {
170 return status; /* LCOV_EXCL_LINE: OutOfMemory */
171 }
172 }
173
174 return ARES_SUCCESS;
175 }
176
ares_dns_write_rr_name(ares_buf_t * buf,const ares_dns_rr_t * rr,ares_llist_t ** namelist,ares_bool_t validate_hostname,ares_dns_rr_key_t key)177 static ares_status_t ares_dns_write_rr_name(ares_buf_t *buf,
178 const ares_dns_rr_t *rr,
179 ares_llist_t **namelist,
180 ares_bool_t validate_hostname,
181 ares_dns_rr_key_t key)
182 {
183 const char *name;
184
185 name = ares_dns_rr_get_str(rr, key);
186 if (name == NULL) {
187 return ARES_EFORMERR; /* LCOV_EXCL_LINE: DefensiveCoding */
188 }
189
190 return ares_dns_name_write(buf, namelist, validate_hostname, name);
191 }
192
ares_dns_write_rr_str(ares_buf_t * buf,const ares_dns_rr_t * rr,ares_dns_rr_key_t key)193 static ares_status_t ares_dns_write_rr_str(ares_buf_t *buf,
194 const ares_dns_rr_t *rr,
195 ares_dns_rr_key_t key)
196 {
197 const char *str;
198 size_t len;
199 ares_status_t status;
200
201 str = ares_dns_rr_get_str(rr, key);
202 if (str == NULL) {
203 return ARES_EFORMERR; /* LCOV_EXCL_LINE: DefensiveCoding */
204 }
205
206 len = ares_strlen(str);
207 if (len > 255) {
208 return ARES_EFORMERR;
209 }
210
211 /* Write 1 byte length */
212 status = ares_buf_append_byte(buf, (unsigned char)(len & 0xFF));
213 if (status != ARES_SUCCESS) {
214 return status; /* LCOV_EXCL_LINE: OutOfMemory */
215 }
216
217 if (len == 0) {
218 return ARES_SUCCESS;
219 }
220
221 /* Write string */
222 return ares_buf_append(buf, (const unsigned char *)str, len);
223 }
224
ares_dns_write_binstr(ares_buf_t * buf,const unsigned char * bin,size_t bin_len)225 static ares_status_t ares_dns_write_binstr(ares_buf_t *buf,
226 const unsigned char *bin,
227 size_t bin_len)
228 {
229 const unsigned char *ptr;
230 size_t ptr_len;
231 ares_status_t status;
232
233 /* split into possible multiple 255-byte or less length strings */
234 ptr = bin;
235 ptr_len = bin_len;
236 do {
237 size_t len = ptr_len;
238 if (len > 255) {
239 len = 255;
240 }
241
242 /* Length */
243 status = ares_buf_append_byte(buf, (unsigned char)(len & 0xFF));
244 if (status != ARES_SUCCESS) {
245 return status; /* LCOV_EXCL_LINE: OutOfMemory */
246 }
247
248 /* String */
249 if (len) {
250 status = ares_buf_append(buf, ptr, len);
251 if (status != ARES_SUCCESS) {
252 return status; /* LCOV_EXCL_LINE: OutOfMemory */
253 }
254 }
255
256 ptr += len;
257 ptr_len -= len;
258 } while (ptr_len > 0);
259
260 return ARES_SUCCESS;
261 }
262
ares_dns_write_rr_abin(ares_buf_t * buf,const ares_dns_rr_t * rr,ares_dns_rr_key_t key)263 static ares_status_t ares_dns_write_rr_abin(ares_buf_t *buf,
264 const ares_dns_rr_t *rr,
265 ares_dns_rr_key_t key)
266 {
267 ares_status_t status = ARES_EFORMERR;
268 size_t i;
269 size_t cnt = ares_dns_rr_get_abin_cnt(rr, key);
270
271 if (cnt == 0) {
272 return ARES_EFORMERR;
273 }
274
275 for (i = 0; i < cnt; i++) {
276 const unsigned char *bin;
277 size_t bin_len;
278
279 bin = ares_dns_rr_get_abin(rr, key, i, &bin_len);
280
281 status = ares_dns_write_binstr(buf, bin, bin_len);
282 if (status != ARES_SUCCESS) {
283 break;
284 }
285 }
286
287 return status;
288 }
289
ares_dns_write_rr_be32(ares_buf_t * buf,const ares_dns_rr_t * rr,ares_dns_rr_key_t key)290 static ares_status_t ares_dns_write_rr_be32(ares_buf_t *buf,
291 const ares_dns_rr_t *rr,
292 ares_dns_rr_key_t key)
293 {
294 if (ares_dns_rr_key_datatype(key) != ARES_DATATYPE_U32) {
295 return ARES_EFORMERR; /* LCOV_EXCL_LINE: DefensiveCoding */
296 }
297 return ares_buf_append_be32(buf, ares_dns_rr_get_u32(rr, key));
298 }
299
ares_dns_write_rr_be16(ares_buf_t * buf,const ares_dns_rr_t * rr,ares_dns_rr_key_t key)300 static ares_status_t ares_dns_write_rr_be16(ares_buf_t *buf,
301 const ares_dns_rr_t *rr,
302 ares_dns_rr_key_t key)
303 {
304 if (ares_dns_rr_key_datatype(key) != ARES_DATATYPE_U16) {
305 return ARES_EFORMERR; /* LCOV_EXCL_LINE: DefensiveCoding */
306 }
307 return ares_buf_append_be16(buf, ares_dns_rr_get_u16(rr, key));
308 }
309
ares_dns_write_rr_u8(ares_buf_t * buf,const ares_dns_rr_t * rr,ares_dns_rr_key_t key)310 static ares_status_t ares_dns_write_rr_u8(ares_buf_t *buf,
311 const ares_dns_rr_t *rr,
312 ares_dns_rr_key_t key)
313 {
314 if (ares_dns_rr_key_datatype(key) != ARES_DATATYPE_U8) {
315 return ARES_EFORMERR; /* LCOV_EXCL_LINE: DefensiveCoding */
316 }
317 return ares_buf_append_byte(buf, ares_dns_rr_get_u8(rr, key));
318 }
319
ares_dns_write_rr_a(ares_buf_t * buf,const ares_dns_rr_t * rr,ares_llist_t ** namelist)320 static ares_status_t ares_dns_write_rr_a(ares_buf_t *buf,
321 const ares_dns_rr_t *rr,
322 ares_llist_t **namelist)
323 {
324 const struct in_addr *addr;
325 (void)namelist;
326
327 addr = ares_dns_rr_get_addr(rr, ARES_RR_A_ADDR);
328 if (addr == NULL) {
329 return ARES_EFORMERR; /* LCOV_EXCL_LINE: DefensiveCoding */
330 }
331
332 return ares_buf_append(buf, (const unsigned char *)addr, sizeof(*addr));
333 }
334
ares_dns_write_rr_ns(ares_buf_t * buf,const ares_dns_rr_t * rr,ares_llist_t ** namelist)335 static ares_status_t ares_dns_write_rr_ns(ares_buf_t *buf,
336 const ares_dns_rr_t *rr,
337 ares_llist_t **namelist)
338 {
339 return ares_dns_write_rr_name(buf, rr, namelist, ARES_FALSE,
340 ARES_RR_NS_NSDNAME);
341 }
342
ares_dns_write_rr_cname(ares_buf_t * buf,const ares_dns_rr_t * rr,ares_llist_t ** namelist)343 static ares_status_t ares_dns_write_rr_cname(ares_buf_t *buf,
344 const ares_dns_rr_t *rr,
345 ares_llist_t **namelist)
346 {
347 return ares_dns_write_rr_name(buf, rr, namelist, ARES_FALSE,
348 ARES_RR_CNAME_CNAME);
349 }
350
ares_dns_write_rr_soa(ares_buf_t * buf,const ares_dns_rr_t * rr,ares_llist_t ** namelist)351 static ares_status_t ares_dns_write_rr_soa(ares_buf_t *buf,
352 const ares_dns_rr_t *rr,
353 ares_llist_t **namelist)
354 {
355 ares_status_t status;
356
357 /* MNAME */
358 status =
359 ares_dns_write_rr_name(buf, rr, namelist, ARES_FALSE, ARES_RR_SOA_MNAME);
360 if (status != ARES_SUCCESS) {
361 return status;
362 }
363
364 /* RNAME */
365 status =
366 ares_dns_write_rr_name(buf, rr, namelist, ARES_FALSE, ARES_RR_SOA_RNAME);
367 if (status != ARES_SUCCESS) {
368 return status;
369 }
370
371 /* SERIAL */
372 status = ares_dns_write_rr_be32(buf, rr, ARES_RR_SOA_SERIAL);
373 if (status != ARES_SUCCESS) {
374 return status; /* LCOV_EXCL_LINE: OutOfMemory */
375 }
376
377 /* REFRESH */
378 status = ares_dns_write_rr_be32(buf, rr, ARES_RR_SOA_REFRESH);
379 if (status != ARES_SUCCESS) {
380 return status; /* LCOV_EXCL_LINE: OutOfMemory */
381 }
382
383 /* RETRY */
384 status = ares_dns_write_rr_be32(buf, rr, ARES_RR_SOA_RETRY);
385 if (status != ARES_SUCCESS) {
386 return status; /* LCOV_EXCL_LINE: OutOfMemory */
387 }
388
389 /* EXPIRE */
390 status = ares_dns_write_rr_be32(buf, rr, ARES_RR_SOA_EXPIRE);
391 if (status != ARES_SUCCESS) {
392 return status; /* LCOV_EXCL_LINE: OutOfMemory */
393 }
394
395 /* MINIMUM */
396 return ares_dns_write_rr_be32(buf, rr, ARES_RR_SOA_MINIMUM);
397 }
398
ares_dns_write_rr_ptr(ares_buf_t * buf,const ares_dns_rr_t * rr,ares_llist_t ** namelist)399 static ares_status_t ares_dns_write_rr_ptr(ares_buf_t *buf,
400 const ares_dns_rr_t *rr,
401 ares_llist_t **namelist)
402 {
403 return ares_dns_write_rr_name(buf, rr, namelist, ARES_FALSE,
404 ARES_RR_PTR_DNAME);
405 }
406
ares_dns_write_rr_hinfo(ares_buf_t * buf,const ares_dns_rr_t * rr,ares_llist_t ** namelist)407 static ares_status_t ares_dns_write_rr_hinfo(ares_buf_t *buf,
408 const ares_dns_rr_t *rr,
409 ares_llist_t **namelist)
410 {
411 ares_status_t status;
412
413 (void)namelist;
414
415 /* CPU */
416 status = ares_dns_write_rr_str(buf, rr, ARES_RR_HINFO_CPU);
417 if (status != ARES_SUCCESS) {
418 return status;
419 }
420
421 /* OS */
422 return ares_dns_write_rr_str(buf, rr, ARES_RR_HINFO_OS);
423 }
424
ares_dns_write_rr_mx(ares_buf_t * buf,const ares_dns_rr_t * rr,ares_llist_t ** namelist)425 static ares_status_t ares_dns_write_rr_mx(ares_buf_t *buf,
426 const ares_dns_rr_t *rr,
427 ares_llist_t **namelist)
428 {
429 ares_status_t status;
430
431 /* PREFERENCE */
432 status = ares_dns_write_rr_be16(buf, rr, ARES_RR_MX_PREFERENCE);
433 if (status != ARES_SUCCESS) {
434 return status; /* LCOV_EXCL_LINE: OutOfMemory */
435 }
436
437 /* EXCHANGE */
438 return ares_dns_write_rr_name(buf, rr, namelist, ARES_FALSE,
439 ARES_RR_MX_EXCHANGE);
440 }
441
ares_dns_write_rr_txt(ares_buf_t * buf,const ares_dns_rr_t * rr,ares_llist_t ** namelist)442 static ares_status_t ares_dns_write_rr_txt(ares_buf_t *buf,
443 const ares_dns_rr_t *rr,
444 ares_llist_t **namelist)
445 {
446 (void)namelist;
447 return ares_dns_write_rr_abin(buf, rr, ARES_RR_TXT_DATA);
448 }
449
ares_dns_write_rr_sig(ares_buf_t * buf,const ares_dns_rr_t * rr,ares_llist_t ** namelist)450 static ares_status_t ares_dns_write_rr_sig(ares_buf_t *buf,
451 const ares_dns_rr_t *rr,
452 ares_llist_t **namelist)
453 {
454 ares_status_t status;
455 const unsigned char *data;
456 size_t len = 0;
457
458 (void)namelist;
459
460 /* TYPE COVERED */
461 status = ares_dns_write_rr_be16(buf, rr, ARES_RR_SIG_TYPE_COVERED);
462 if (status != ARES_SUCCESS) {
463 return status; /* LCOV_EXCL_LINE: OutOfMemory */
464 }
465
466 /* ALGORITHM */
467 status = ares_dns_write_rr_u8(buf, rr, ARES_RR_SIG_ALGORITHM);
468 if (status != ARES_SUCCESS) {
469 return status; /* LCOV_EXCL_LINE: OutOfMemory */
470 }
471
472 /* LABELS */
473 status = ares_dns_write_rr_u8(buf, rr, ARES_RR_SIG_LABELS);
474 if (status != ARES_SUCCESS) {
475 return status; /* LCOV_EXCL_LINE: OutOfMemory */
476 }
477
478 /* ORIGINAL TTL */
479 status = ares_dns_write_rr_be32(buf, rr, ARES_RR_SIG_ORIGINAL_TTL);
480 if (status != ARES_SUCCESS) {
481 return status; /* LCOV_EXCL_LINE: OutOfMemory */
482 }
483
484 /* EXPIRATION */
485 status = ares_dns_write_rr_be32(buf, rr, ARES_RR_SIG_EXPIRATION);
486 if (status != ARES_SUCCESS) {
487 return status; /* LCOV_EXCL_LINE: OutOfMemory */
488 }
489
490 /* INCEPTION */
491 status = ares_dns_write_rr_be32(buf, rr, ARES_RR_SIG_INCEPTION);
492 if (status != ARES_SUCCESS) {
493 return status; /* LCOV_EXCL_LINE: OutOfMemory */
494 }
495
496 /* KEY TAG */
497 status = ares_dns_write_rr_be16(buf, rr, ARES_RR_SIG_KEY_TAG);
498 if (status != ARES_SUCCESS) {
499 return status; /* LCOV_EXCL_LINE: OutOfMemory */
500 }
501
502 /* SIGNERS NAME */
503 status = ares_dns_write_rr_name(buf, rr, namelist, ARES_FALSE,
504 ARES_RR_SIG_SIGNERS_NAME);
505 if (status != ARES_SUCCESS) {
506 return status;
507 }
508
509 /* SIGNATURE -- binary, rest of buffer, required to be non-zero length */
510 data = ares_dns_rr_get_bin(rr, ARES_RR_SIG_SIGNATURE, &len);
511 if (data == NULL || len == 0) {
512 return ARES_EFORMERR;
513 }
514
515 return ares_buf_append(buf, data, len);
516 }
517
ares_dns_write_rr_aaaa(ares_buf_t * buf,const ares_dns_rr_t * rr,ares_llist_t ** namelist)518 static ares_status_t ares_dns_write_rr_aaaa(ares_buf_t *buf,
519 const ares_dns_rr_t *rr,
520 ares_llist_t **namelist)
521 {
522 const struct ares_in6_addr *addr;
523 (void)namelist;
524
525 addr = ares_dns_rr_get_addr6(rr, ARES_RR_AAAA_ADDR);
526 if (addr == NULL) {
527 return ARES_EFORMERR; /* LCOV_EXCL_LINE: DefensiveCoding */
528 }
529
530 return ares_buf_append(buf, (const unsigned char *)addr, sizeof(*addr));
531 }
532
ares_dns_write_rr_srv(ares_buf_t * buf,const ares_dns_rr_t * rr,ares_llist_t ** namelist)533 static ares_status_t ares_dns_write_rr_srv(ares_buf_t *buf,
534 const ares_dns_rr_t *rr,
535 ares_llist_t **namelist)
536 {
537 ares_status_t status;
538
539 /* PRIORITY */
540 status = ares_dns_write_rr_be16(buf, rr, ARES_RR_SRV_PRIORITY);
541 if (status != ARES_SUCCESS) {
542 return status; /* LCOV_EXCL_LINE: OutOfMemory */
543 }
544
545 /* WEIGHT */
546 status = ares_dns_write_rr_be16(buf, rr, ARES_RR_SRV_WEIGHT);
547 if (status != ARES_SUCCESS) {
548 return status; /* LCOV_EXCL_LINE: OutOfMemory */
549 }
550
551 /* PORT */
552 status = ares_dns_write_rr_be16(buf, rr, ARES_RR_SRV_PORT);
553 if (status != ARES_SUCCESS) {
554 return status; /* LCOV_EXCL_LINE: OutOfMemory */
555 }
556
557 /* TARGET */
558 return ares_dns_write_rr_name(buf, rr, namelist, ARES_FALSE,
559 ARES_RR_SRV_TARGET);
560 }
561
ares_dns_write_rr_naptr(ares_buf_t * buf,const ares_dns_rr_t * rr,ares_llist_t ** namelist)562 static ares_status_t ares_dns_write_rr_naptr(ares_buf_t *buf,
563 const ares_dns_rr_t *rr,
564 ares_llist_t **namelist)
565 {
566 ares_status_t status;
567
568 /* ORDER */
569 status = ares_dns_write_rr_be16(buf, rr, ARES_RR_NAPTR_ORDER);
570 if (status != ARES_SUCCESS) {
571 return status; /* LCOV_EXCL_LINE: OutOfMemory */
572 }
573
574 /* PREFERENCE */
575 status = ares_dns_write_rr_be16(buf, rr, ARES_RR_NAPTR_PREFERENCE);
576 if (status != ARES_SUCCESS) {
577 return status; /* LCOV_EXCL_LINE: OutOfMemory */
578 }
579
580 /* FLAGS */
581 status = ares_dns_write_rr_str(buf, rr, ARES_RR_NAPTR_FLAGS);
582 if (status != ARES_SUCCESS) {
583 return status; /* LCOV_EXCL_LINE: OutOfMemory */
584 }
585
586 /* SERVICES */
587 status = ares_dns_write_rr_str(buf, rr, ARES_RR_NAPTR_SERVICES);
588 if (status != ARES_SUCCESS) {
589 return status; /* LCOV_EXCL_LINE: OutOfMemory */
590 }
591
592 /* REGEXP */
593 status = ares_dns_write_rr_str(buf, rr, ARES_RR_NAPTR_REGEXP);
594 if (status != ARES_SUCCESS) {
595 return status; /* LCOV_EXCL_LINE: OutOfMemory */
596 }
597
598 /* REPLACEMENT */
599 return ares_dns_write_rr_name(buf, rr, namelist, ARES_FALSE,
600 ARES_RR_NAPTR_REPLACEMENT);
601 }
602
ares_dns_write_rr_opt(ares_buf_t * buf,const ares_dns_rr_t * rr,ares_llist_t ** namelist)603 static ares_status_t ares_dns_write_rr_opt(ares_buf_t *buf,
604 const ares_dns_rr_t *rr,
605 ares_llist_t **namelist)
606 {
607 size_t len = ares_buf_len(buf);
608 ares_status_t status;
609 unsigned int ttl = 0;
610 size_t i;
611 unsigned short rcode = (unsigned short)((rr->parent->rcode >> 4) & 0xFF);
612
613 (void)namelist;
614
615 /* Coverity reports on this even though its not possible when taken
616 * into context */
617 if (len == 0) {
618 return ARES_EFORMERR; /* LCOV_EXCL_LINE: DefensiveCoding */
619 }
620
621 /* We need to go back and overwrite the class and ttl that were emitted as
622 * the OPT record overloads them for its own use (yes, very strange!) */
623 status = ares_buf_set_length(buf, len - 2 /* RDLENGTH */
624 - 4 /* TTL */
625 - 2 /* CLASS */);
626 if (status != ARES_SUCCESS) {
627 return status;
628 }
629
630 /* Class -> UDP Size */
631 status = ares_dns_write_rr_be16(buf, rr, ARES_RR_OPT_UDP_SIZE);
632 if (status != ARES_SUCCESS) {
633 return status; /* LCOV_EXCL_LINE: OutOfMemory */
634 }
635
636 /* TTL -> rcode (u8) << 24 | version (u8) << 16 | flags (u16) */
637 ttl |= (unsigned int)rcode << 24;
638 ttl |= (unsigned int)ares_dns_rr_get_u8(rr, ARES_RR_OPT_VERSION) << 16;
639 ttl |= (unsigned int)ares_dns_rr_get_u16(rr, ARES_RR_OPT_FLAGS);
640
641 status = ares_buf_append_be32(buf, ttl);
642 if (status != ARES_SUCCESS) {
643 return status; /* LCOV_EXCL_LINE: OutOfMemory */
644 }
645
646 /* Now go back to real end */
647 status = ares_buf_set_length(buf, len);
648 if (status != ARES_SUCCESS) {
649 return status;
650 }
651
652 /* Append Options */
653 for (i = 0; i < ares_dns_rr_get_opt_cnt(rr, ARES_RR_OPT_OPTIONS); i++) {
654 unsigned short opt;
655 size_t val_len;
656 const unsigned char *val;
657
658 opt = ares_dns_rr_get_opt(rr, ARES_RR_OPT_OPTIONS, i, &val, &val_len);
659
660 /* BE16 option */
661 status = ares_buf_append_be16(buf, opt);
662 if (status != ARES_SUCCESS) {
663 return status; /* LCOV_EXCL_LINE: OutOfMemory */
664 }
665
666 /* BE16 length */
667 status = ares_buf_append_be16(buf, (unsigned short)(val_len & 0xFFFF));
668 if (status != ARES_SUCCESS) {
669 return status; /* LCOV_EXCL_LINE: OutOfMemory */
670 }
671
672 /* Value */
673 if (val && val_len) {
674 status = ares_buf_append(buf, val, val_len);
675 if (status != ARES_SUCCESS) {
676 return status; /* LCOV_EXCL_LINE: OutOfMemory */
677 }
678 }
679 }
680
681 return ARES_SUCCESS;
682 }
683
ares_dns_write_rr_tlsa(ares_buf_t * buf,const ares_dns_rr_t * rr,ares_llist_t ** namelist)684 static ares_status_t ares_dns_write_rr_tlsa(ares_buf_t *buf,
685 const ares_dns_rr_t *rr,
686 ares_llist_t **namelist)
687 {
688 ares_status_t status;
689 const unsigned char *data;
690 size_t len = 0;
691
692 (void)namelist;
693
694 /* CERT_USAGE */
695 status = ares_dns_write_rr_u8(buf, rr, ARES_RR_TLSA_CERT_USAGE);
696 if (status != ARES_SUCCESS) {
697 return status; /* LCOV_EXCL_LINE: OutOfMemory */
698 }
699
700 /* SELECTOR */
701 status = ares_dns_write_rr_u8(buf, rr, ARES_RR_TLSA_SELECTOR);
702 if (status != ARES_SUCCESS) {
703 return status; /* LCOV_EXCL_LINE: OutOfMemory */
704 }
705
706 /* MATCH */
707 status = ares_dns_write_rr_u8(buf, rr, ARES_RR_TLSA_MATCH);
708 if (status != ARES_SUCCESS) {
709 return status; /* LCOV_EXCL_LINE: OutOfMemory */
710 }
711
712 /* DATA -- binary, rest of buffer, required to be non-zero length */
713 data = ares_dns_rr_get_bin(rr, ARES_RR_TLSA_DATA, &len);
714 if (data == NULL || len == 0) {
715 return ARES_EFORMERR;
716 }
717
718 return ares_buf_append(buf, data, len);
719 }
720
ares_dns_write_rr_svcb(ares_buf_t * buf,const ares_dns_rr_t * rr,ares_llist_t ** namelist)721 static ares_status_t ares_dns_write_rr_svcb(ares_buf_t *buf,
722 const ares_dns_rr_t *rr,
723 ares_llist_t **namelist)
724 {
725 ares_status_t status;
726 size_t i;
727
728 /* PRIORITY */
729 status = ares_dns_write_rr_be16(buf, rr, ARES_RR_SVCB_PRIORITY);
730 if (status != ARES_SUCCESS) {
731 return status; /* LCOV_EXCL_LINE: OutOfMemory */
732 }
733
734 /* TARGET */
735 status =
736 ares_dns_write_rr_name(buf, rr, namelist, ARES_FALSE, ARES_RR_SVCB_TARGET);
737 if (status != ARES_SUCCESS) {
738 return status;
739 }
740
741 /* Append Params */
742 for (i = 0; i < ares_dns_rr_get_opt_cnt(rr, ARES_RR_SVCB_PARAMS); i++) {
743 unsigned short opt;
744 size_t val_len;
745 const unsigned char *val;
746
747 opt = ares_dns_rr_get_opt(rr, ARES_RR_SVCB_PARAMS, i, &val, &val_len);
748
749 /* BE16 option */
750 status = ares_buf_append_be16(buf, opt);
751 if (status != ARES_SUCCESS) {
752 return status; /* LCOV_EXCL_LINE: OutOfMemory */
753 }
754
755 /* BE16 length */
756 status = ares_buf_append_be16(buf, (unsigned short)(val_len & 0xFFFF));
757 if (status != ARES_SUCCESS) {
758 return status; /* LCOV_EXCL_LINE: OutOfMemory */
759 }
760
761 /* Value */
762 if (val && val_len) {
763 status = ares_buf_append(buf, val, val_len);
764 if (status != ARES_SUCCESS) {
765 return status; /* LCOV_EXCL_LINE: OutOfMemory */
766 }
767 }
768 }
769 return ARES_SUCCESS;
770 }
771
ares_dns_write_rr_https(ares_buf_t * buf,const ares_dns_rr_t * rr,ares_llist_t ** namelist)772 static ares_status_t ares_dns_write_rr_https(ares_buf_t *buf,
773 const ares_dns_rr_t *rr,
774 ares_llist_t **namelist)
775 {
776 ares_status_t status;
777 size_t i;
778
779 /* PRIORITY */
780 status = ares_dns_write_rr_be16(buf, rr, ARES_RR_HTTPS_PRIORITY);
781 if (status != ARES_SUCCESS) {
782 return status; /* LCOV_EXCL_LINE: OutOfMemory */
783 }
784
785 /* TARGET */
786 status =
787 ares_dns_write_rr_name(buf, rr, namelist, ARES_FALSE, ARES_RR_HTTPS_TARGET);
788 if (status != ARES_SUCCESS) {
789 return status;
790 }
791
792 /* Append Params */
793 for (i = 0; i < ares_dns_rr_get_opt_cnt(rr, ARES_RR_HTTPS_PARAMS); i++) {
794 unsigned short opt;
795 size_t val_len;
796 const unsigned char *val;
797
798 opt = ares_dns_rr_get_opt(rr, ARES_RR_HTTPS_PARAMS, i, &val, &val_len);
799
800 /* BE16 option */
801 status = ares_buf_append_be16(buf, opt);
802 if (status != ARES_SUCCESS) {
803 return status; /* LCOV_EXCL_LINE: OutOfMemory */
804 }
805
806 /* BE16 length */
807 status = ares_buf_append_be16(buf, (unsigned short)(val_len & 0xFFFF));
808 if (status != ARES_SUCCESS) {
809 return status; /* LCOV_EXCL_LINE: OutOfMemory */
810 }
811
812 /* Value */
813 if (val && val_len) {
814 status = ares_buf_append(buf, val, val_len);
815 if (status != ARES_SUCCESS) {
816 return status; /* LCOV_EXCL_LINE: OutOfMemory */
817 }
818 }
819 }
820 return ARES_SUCCESS;
821 }
822
ares_dns_write_rr_uri(ares_buf_t * buf,const ares_dns_rr_t * rr,ares_llist_t ** namelist)823 static ares_status_t ares_dns_write_rr_uri(ares_buf_t *buf,
824 const ares_dns_rr_t *rr,
825 ares_llist_t **namelist)
826 {
827 ares_status_t status;
828 const char *target;
829
830 (void)namelist;
831
832 /* PRIORITY */
833 status = ares_dns_write_rr_be16(buf, rr, ARES_RR_URI_PRIORITY);
834 if (status != ARES_SUCCESS) {
835 return status; /* LCOV_EXCL_LINE: OutOfMemory */
836 }
837
838 /* WEIGHT */
839 status = ares_dns_write_rr_be16(buf, rr, ARES_RR_URI_WEIGHT);
840 if (status != ARES_SUCCESS) {
841 return status; /* LCOV_EXCL_LINE: OutOfMemory */
842 }
843
844 /* TARGET -- not in DNS string format, rest of buffer, required to be
845 * non-zero length */
846 target = ares_dns_rr_get_str(rr, ARES_RR_URI_TARGET);
847 if (target == NULL || ares_strlen(target) == 0) {
848 return ARES_EFORMERR;
849 }
850
851 return ares_buf_append(buf, (const unsigned char *)target,
852 ares_strlen(target));
853 }
854
ares_dns_write_rr_caa(ares_buf_t * buf,const ares_dns_rr_t * rr,ares_llist_t ** namelist)855 static ares_status_t ares_dns_write_rr_caa(ares_buf_t *buf,
856 const ares_dns_rr_t *rr,
857 ares_llist_t **namelist)
858 {
859 const unsigned char *data = NULL;
860 size_t data_len = 0;
861 ares_status_t status;
862
863 (void)namelist;
864
865 /* CRITICAL */
866 status = ares_dns_write_rr_u8(buf, rr, ARES_RR_CAA_CRITICAL);
867 if (status != ARES_SUCCESS) {
868 return status; /* LCOV_EXCL_LINE: OutOfMemory */
869 }
870
871 /* Tag */
872 status = ares_dns_write_rr_str(buf, rr, ARES_RR_CAA_TAG);
873 if (status != ARES_SUCCESS) {
874 return status; /* LCOV_EXCL_LINE: OutOfMemory */
875 }
876
877 /* Value - binary! (remaining buffer */
878 data = ares_dns_rr_get_bin(rr, ARES_RR_CAA_VALUE, &data_len);
879 if (data == NULL || data_len == 0) {
880 return ARES_EFORMERR;
881 }
882
883 return ares_buf_append(buf, data, data_len);
884 }
885
ares_dns_write_rr_raw_rr(ares_buf_t * buf,const ares_dns_rr_t * rr,ares_llist_t ** namelist)886 static ares_status_t ares_dns_write_rr_raw_rr(ares_buf_t *buf,
887 const ares_dns_rr_t *rr,
888 ares_llist_t **namelist)
889 {
890 size_t len = ares_buf_len(buf);
891 ares_status_t status;
892 const unsigned char *data = NULL;
893 size_t data_len = 0;
894
895 (void)namelist;
896
897 /* Coverity reports on this even though its not possible when taken
898 * into context */
899 if (len == 0) {
900 return ARES_EFORMERR; /* LCOV_EXCL_LINE: DefensiveCoding */
901 }
902
903 /* We need to go back and overwrite the type that was emitted by the parent
904 * function */
905 status = ares_buf_set_length(buf, len - 2 /* RDLENGTH */
906 - 4 /* TTL */
907 - 2 /* CLASS */
908 - 2 /* TYPE */);
909 if (status != ARES_SUCCESS) {
910 return status;
911 }
912
913 status = ares_dns_write_rr_be16(buf, rr, ARES_RR_RAW_RR_TYPE);
914 if (status != ARES_SUCCESS) {
915 return status; /* LCOV_EXCL_LINE: OutOfMemory */
916 }
917
918 /* Now go back to real end */
919 status = ares_buf_set_length(buf, len);
920 if (status != ARES_SUCCESS) {
921 return status;
922 }
923
924 /* Output raw data */
925 data = ares_dns_rr_get_bin(rr, ARES_RR_RAW_RR_DATA, &data_len);
926 if (data == NULL) {
927 return ARES_EFORMERR;
928 }
929
930 if (data_len == 0) {
931 return ARES_SUCCESS;
932 }
933
934 return ares_buf_append(buf, data, data_len);
935 }
936
ares_dns_write_rr(const ares_dns_record_t * dnsrec,ares_llist_t ** namelist,ares_dns_section_t section,ares_buf_t * buf)937 static ares_status_t ares_dns_write_rr(const ares_dns_record_t *dnsrec,
938 ares_llist_t **namelist,
939 ares_dns_section_t section,
940 ares_buf_t *buf)
941 {
942 size_t i;
943
944 for (i = 0; i < ares_dns_record_rr_cnt(dnsrec, section); i++) {
945 const ares_dns_rr_t *rr;
946 ares_dns_rec_type_t type;
947 ares_bool_t allow_compress;
948 ares_llist_t **namelistptr = NULL;
949 size_t pos_len;
950 ares_status_t status;
951 size_t rdlength;
952 size_t end_length;
953 unsigned int ttl;
954
955 rr = ares_dns_record_rr_get_const(dnsrec, section, i);
956 if (rr == NULL) {
957 return ARES_EFORMERR; /* LCOV_EXCL_LINE: DefensiveCoding */
958 }
959
960 type = ares_dns_rr_get_type(rr);
961 allow_compress = ares_dns_rec_allow_name_comp(type);
962 if (allow_compress) {
963 namelistptr = namelist;
964 }
965
966 /* Name */
967 status =
968 ares_dns_name_write(buf, namelist, ARES_TRUE, ares_dns_rr_get_name(rr));
969 if (status != ARES_SUCCESS) {
970 return status;
971 }
972
973 /* Type */
974 status = ares_buf_append_be16(buf, (unsigned short)type);
975 if (status != ARES_SUCCESS) {
976 return status; /* LCOV_EXCL_LINE: OutOfMemory */
977 }
978
979 /* Class */
980 status =
981 ares_buf_append_be16(buf, (unsigned short)ares_dns_rr_get_class(rr));
982 if (status != ARES_SUCCESS) {
983 return status; /* LCOV_EXCL_LINE: OutOfMemory */
984 }
985
986 /* TTL */
987 ttl = ares_dns_rr_get_ttl(rr);
988 if (rr->parent->ttl_decrement > ttl) {
989 ttl = 0;
990 } else {
991 ttl -= rr->parent->ttl_decrement;
992 }
993 status = ares_buf_append_be32(buf, ttl);
994 if (status != ARES_SUCCESS) {
995 return status; /* LCOV_EXCL_LINE: OutOfMemory */
996 }
997
998 /* Length */
999 pos_len = ares_buf_len(buf); /* Save to write real length later */
1000 status = ares_buf_append_be16(buf, 0);
1001 if (status != ARES_SUCCESS) {
1002 return status; /* LCOV_EXCL_LINE: OutOfMemory */
1003 }
1004
1005 /* Data */
1006 switch (type) {
1007 case ARES_REC_TYPE_A:
1008 status = ares_dns_write_rr_a(buf, rr, namelistptr);
1009 break;
1010 case ARES_REC_TYPE_NS:
1011 status = ares_dns_write_rr_ns(buf, rr, namelistptr);
1012 break;
1013 case ARES_REC_TYPE_CNAME:
1014 status = ares_dns_write_rr_cname(buf, rr, namelistptr);
1015 break;
1016 case ARES_REC_TYPE_SOA:
1017 status = ares_dns_write_rr_soa(buf, rr, namelistptr);
1018 break;
1019 case ARES_REC_TYPE_PTR:
1020 status = ares_dns_write_rr_ptr(buf, rr, namelistptr);
1021 break;
1022 case ARES_REC_TYPE_HINFO:
1023 status = ares_dns_write_rr_hinfo(buf, rr, namelistptr);
1024 break;
1025 case ARES_REC_TYPE_MX:
1026 status = ares_dns_write_rr_mx(buf, rr, namelistptr);
1027 break;
1028 case ARES_REC_TYPE_TXT:
1029 status = ares_dns_write_rr_txt(buf, rr, namelistptr);
1030 break;
1031 case ARES_REC_TYPE_SIG:
1032 status = ares_dns_write_rr_sig(buf, rr, namelistptr);
1033 break;
1034 case ARES_REC_TYPE_AAAA:
1035 status = ares_dns_write_rr_aaaa(buf, rr, namelistptr);
1036 break;
1037 case ARES_REC_TYPE_SRV:
1038 status = ares_dns_write_rr_srv(buf, rr, namelistptr);
1039 break;
1040 case ARES_REC_TYPE_NAPTR:
1041 status = ares_dns_write_rr_naptr(buf, rr, namelistptr);
1042 break;
1043 case ARES_REC_TYPE_ANY:
1044 status = ARES_EFORMERR;
1045 break;
1046 case ARES_REC_TYPE_OPT:
1047 status = ares_dns_write_rr_opt(buf, rr, namelistptr);
1048 break;
1049 case ARES_REC_TYPE_TLSA:
1050 status = ares_dns_write_rr_tlsa(buf, rr, namelistptr);
1051 break;
1052 case ARES_REC_TYPE_SVCB:
1053 status = ares_dns_write_rr_svcb(buf, rr, namelistptr);
1054 break;
1055 case ARES_REC_TYPE_HTTPS:
1056 status = ares_dns_write_rr_https(buf, rr, namelistptr);
1057 break;
1058 case ARES_REC_TYPE_URI:
1059 status = ares_dns_write_rr_uri(buf, rr, namelistptr);
1060 break;
1061 case ARES_REC_TYPE_CAA:
1062 status = ares_dns_write_rr_caa(buf, rr, namelistptr);
1063 break;
1064 case ARES_REC_TYPE_RAW_RR:
1065 status = ares_dns_write_rr_raw_rr(buf, rr, namelistptr);
1066 break;
1067 }
1068
1069 if (status != ARES_SUCCESS) {
1070 return status;
1071 }
1072
1073 /* Back off write pointer, write real length, then go back to proper
1074 * position */
1075 end_length = ares_buf_len(buf);
1076 rdlength = end_length - pos_len - 2;
1077
1078 status = ares_buf_set_length(buf, pos_len);
1079 if (status != ARES_SUCCESS) {
1080 return status;
1081 }
1082
1083 status = ares_buf_append_be16(buf, (unsigned short)(rdlength & 0xFFFF));
1084 if (status != ARES_SUCCESS) {
1085 return status; /* LCOV_EXCL_LINE: OutOfMemory */
1086 }
1087
1088 status = ares_buf_set_length(buf, end_length);
1089 if (status != ARES_SUCCESS) {
1090 return status;
1091 }
1092 }
1093
1094 return ARES_SUCCESS;
1095 }
1096
ares_dns_write_buf(const ares_dns_record_t * dnsrec,ares_buf_t * buf)1097 ares_status_t ares_dns_write_buf(const ares_dns_record_t *dnsrec,
1098 ares_buf_t *buf)
1099 {
1100 ares_llist_t *namelist = NULL;
1101 size_t orig_len;
1102 ares_status_t status;
1103
1104 if (dnsrec == NULL || buf == NULL) {
1105 return ARES_EFORMERR;
1106 }
1107
1108 orig_len = ares_buf_len(buf);
1109
1110 status = ares_dns_write_header(dnsrec, buf);
1111 if (status != ARES_SUCCESS) {
1112 goto done;
1113 }
1114
1115 status = ares_dns_write_questions(dnsrec, &namelist, buf);
1116 if (status != ARES_SUCCESS) {
1117 goto done;
1118 }
1119
1120 status = ares_dns_write_rr(dnsrec, &namelist, ARES_SECTION_ANSWER, buf);
1121 if (status != ARES_SUCCESS) {
1122 goto done;
1123 }
1124
1125 status = ares_dns_write_rr(dnsrec, &namelist, ARES_SECTION_AUTHORITY, buf);
1126 if (status != ARES_SUCCESS) {
1127 goto done;
1128 }
1129
1130 status = ares_dns_write_rr(dnsrec, &namelist, ARES_SECTION_ADDITIONAL, buf);
1131 if (status != ARES_SUCCESS) {
1132 goto done;
1133 }
1134
1135 done:
1136 ares_llist_destroy(namelist);
1137 if (status != ARES_SUCCESS) {
1138 ares_buf_set_length(buf, orig_len);
1139 }
1140
1141 return status;
1142 }
1143
ares_dns_write_buf_tcp(const ares_dns_record_t * dnsrec,ares_buf_t * buf)1144 ares_status_t ares_dns_write_buf_tcp(const ares_dns_record_t *dnsrec,
1145 ares_buf_t *buf)
1146 {
1147 ares_status_t status;
1148 size_t orig_len;
1149 size_t msg_len;
1150 size_t len;
1151
1152 if (dnsrec == NULL || buf == NULL) {
1153 return ARES_EFORMERR;
1154 }
1155
1156 orig_len = ares_buf_len(buf);
1157
1158 /* Write placeholder for length */
1159 status = ares_buf_append_be16(buf, 0);
1160 if (status != ARES_SUCCESS) {
1161 goto done; /* LCOV_EXCL_LINE: OutOfMemory */
1162 }
1163
1164 /* Write message */
1165 status = ares_dns_write_buf(dnsrec, buf);
1166 if (status != ARES_SUCCESS) {
1167 goto done; /* LCOV_EXCL_LINE: OutOfMemory */
1168 }
1169
1170 len = ares_buf_len(buf);
1171 msg_len = len - orig_len - 2;
1172 if (msg_len > 65535) {
1173 status = ARES_EBADQUERY;
1174 goto done;
1175 }
1176
1177 /* Now we need to overwrite the length, so we jump back to the original
1178 * message length, overwrite the section and jump back */
1179 ares_buf_set_length(buf, orig_len);
1180 status = ares_buf_append_be16(buf, (unsigned short)(msg_len & 0xFFFF));
1181 if (status != ARES_SUCCESS) {
1182 goto done; /* LCOV_EXCL_LINE: UntestablePath */
1183 }
1184 ares_buf_set_length(buf, len);
1185
1186 done:
1187 if (status != ARES_SUCCESS) {
1188 ares_buf_set_length(buf, orig_len);
1189 }
1190 return status;
1191 }
1192
ares_dns_write(const ares_dns_record_t * dnsrec,unsigned char ** buf,size_t * buf_len)1193 ares_status_t ares_dns_write(const ares_dns_record_t *dnsrec,
1194 unsigned char **buf, size_t *buf_len)
1195 {
1196 ares_buf_t *b = NULL;
1197 ares_status_t status;
1198
1199 if (buf == NULL || buf_len == NULL || dnsrec == NULL) {
1200 return ARES_EFORMERR;
1201 }
1202
1203 *buf = NULL;
1204 *buf_len = 0;
1205
1206 b = ares_buf_create();
1207 if (b == NULL) {
1208 return ARES_ENOMEM; /* LCOV_EXCL_LINE: OutOfMemory */
1209 }
1210
1211 status = ares_dns_write_buf(dnsrec, b);
1212
1213 if (status != ARES_SUCCESS) {
1214 ares_buf_destroy(b);
1215 return status;
1216 }
1217
1218 *buf = ares_buf_finish_bin(b, buf_len);
1219 return status;
1220 }
1221
ares_dns_record_ttl_decrement(ares_dns_record_t * dnsrec,unsigned int ttl_decrement)1222 void ares_dns_record_ttl_decrement(ares_dns_record_t *dnsrec,
1223 unsigned int ttl_decrement)
1224 {
1225 if (dnsrec == NULL) {
1226 return;
1227 }
1228 dnsrec->ttl_decrement = ttl_decrement;
1229 }
1230