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
27 #include "ares_private.h"
28 #include "ares_data.h"
29
ares_parse_txt_reply_int(const unsigned char * abuf,size_t alen,ares_bool_t ex,void ** txt_out)30 static int ares_parse_txt_reply_int(const unsigned char *abuf, size_t alen,
31 ares_bool_t ex, void **txt_out)
32 {
33 ares_status_t status;
34 struct ares_txt_ext *txt_head = NULL;
35 struct ares_txt_ext *txt_last = NULL;
36 struct ares_txt_ext *txt_curr;
37 ares_dns_record_t *dnsrec = NULL;
38 size_t i;
39
40 *txt_out = NULL;
41
42 status = ares_dns_parse(abuf, alen, 0, &dnsrec);
43 if (status != ARES_SUCCESS) {
44 goto done;
45 }
46
47 if (ares_dns_record_rr_cnt(dnsrec, ARES_SECTION_ANSWER) == 0) {
48 status = ARES_ENODATA;
49 goto done;
50 }
51
52 for (i = 0; i < ares_dns_record_rr_cnt(dnsrec, ARES_SECTION_ANSWER); i++) {
53 const ares_dns_rr_t *rr =
54 ares_dns_record_rr_get(dnsrec, ARES_SECTION_ANSWER, i);
55 size_t j;
56 size_t cnt;
57
58
59 if (rr == NULL) {
60 /* Shouldn't be possible */
61 status = ARES_EBADRESP; /* LCOV_EXCL_LINE: DefensiveCoding */
62 goto done; /* LCOV_EXCL_LINE: DefensiveCoding */
63 }
64
65 /* XXX: Why Chaos? */
66 if ((ares_dns_rr_get_class(rr) != ARES_CLASS_IN &&
67 ares_dns_rr_get_class(rr) != ARES_CLASS_CHAOS) ||
68 ares_dns_rr_get_type(rr) != ARES_REC_TYPE_TXT) {
69 continue;
70 }
71
72 cnt = ares_dns_rr_get_abin_cnt(rr, ARES_RR_TXT_DATA);
73
74 for (j = 0; j < cnt; j++) {
75 const unsigned char *ptr;
76 size_t ptr_len;
77
78 /* Allocate storage for this TXT answer appending it to the list */
79 txt_curr =
80 ares_malloc_data(ex ? ARES_DATATYPE_TXT_EXT : ARES_DATATYPE_TXT_REPLY);
81 if (txt_curr == NULL) {
82 status = ARES_ENOMEM; /* LCOV_EXCL_LINE: OutOfMemory */
83 goto done; /* LCOV_EXCL_LINE: OutOfMemory */
84 }
85
86 /* Link in the record */
87 if (txt_last) {
88 txt_last->next = txt_curr;
89 } else {
90 txt_head = txt_curr;
91 }
92 txt_last = txt_curr;
93
94 /* Tag start on first for each TXT record */
95 if (ex && j == 0) {
96 txt_curr->record_start = 1;
97 }
98
99 ptr = ares_dns_rr_get_abin(rr, ARES_RR_TXT_DATA, j, &ptr_len);
100
101 txt_curr->txt = ares_malloc(ptr_len + 1);
102 if (txt_curr->txt == NULL) {
103 status = ARES_ENOMEM; /* LCOV_EXCL_LINE: OutOfMemory */
104 goto done; /* LCOV_EXCL_LINE: OutOfMemory */
105 }
106 memcpy(txt_curr->txt, ptr, ptr_len);
107 txt_curr->txt[ptr_len] = 0;
108 txt_curr->length = ptr_len;
109 }
110 }
111
112 done:
113 /* clean up on error */
114 if (status != ARES_SUCCESS) {
115 if (txt_head) {
116 ares_free_data(txt_head);
117 }
118 } else {
119 /* everything looks fine, return the data */
120 *txt_out = txt_head;
121 }
122 ares_dns_record_destroy(dnsrec);
123 return (int)status;
124 }
125
ares_parse_txt_reply(const unsigned char * abuf,int alen,struct ares_txt_reply ** txt_out)126 int ares_parse_txt_reply(const unsigned char *abuf, int alen,
127 struct ares_txt_reply **txt_out)
128 {
129 if (alen < 0) {
130 return ARES_EBADRESP;
131 }
132 return ares_parse_txt_reply_int(abuf, (size_t)alen, ARES_FALSE,
133 (void **)txt_out);
134 }
135
ares_parse_txt_reply_ext(const unsigned char * abuf,int alen,struct ares_txt_ext ** txt_out)136 int ares_parse_txt_reply_ext(const unsigned char *abuf, int alen,
137 struct ares_txt_ext **txt_out)
138 {
139 if (alen < 0) {
140 return ARES_EBADRESP;
141 }
142 return ares_parse_txt_reply_int(abuf, (size_t)alen, ARES_TRUE,
143 (void **)txt_out);
144 }
145