1 /**
2 * Parse the rdata component of a resource record.
3 *
4 * \param[out] rr resource record object whose ->rdata field will be populated
5 * \param[in] p pointer to start of message
6 * \param[in] eop end of message buffer
7 * \param[in] rdata pointer to rdata
8 * \param[in] rdlen
9 */
10
11 wdns_res
_wdns_parse_rdata(wdns_rr_t * rr,const uint8_t * p,const uint8_t * eop,const uint8_t * rdata,uint16_t rdlen)12 _wdns_parse_rdata(wdns_rr_t *rr, const uint8_t *p, const uint8_t *eop,
13 const uint8_t *rdata, uint16_t rdlen)
14 {
15
16 #define advance_bytes(x) do { \
17 if (src_bytes < ((signed) (x))) { \
18 res = wdns_res_parse_error; \
19 goto parse_error; \
20 } \
21 src += (x); \
22 src_bytes -= (x); \
23 } while (0)
24
25 #define copy_bytes(x) do { \
26 if (src_bytes < (x)) {\
27 res = wdns_res_parse_error; \
28 goto parse_error; \
29 } \
30 ubuf_append(u, src, x); \
31 src += (x); \
32 src_bytes -= (x); \
33 } while (0)
34
35 ubuf *u;
36 const record_descr *descr = NULL;
37 const uint8_t *src;
38 const uint8_t *t;
39 ssize_t src_bytes;
40 size_t len;
41 uint8_t domain_name[WDNS_MAXLEN_NAME];
42 uint8_t oclen;
43 wdns_res res;
44
45 u = ubuf_new();
46 src = rdata;
47 src_bytes = (ssize_t) rdlen;
48
49 if (rr->rrtype < record_descr_len)
50 descr = &record_descr_array[rr->rrtype];
51
52 if (rr->rrtype >= record_descr_len ||
53 (descr != NULL && descr->types[0] == rdf_unknown))
54 {
55 /* unknown rrtype, treat generically */
56 copy_bytes(src_bytes);
57 } else if (descr != NULL &&
58 (descr->record_class == class_un ||
59 descr->record_class == rr->rrclass))
60 {
61 for (t = &descr->types[0]; *t != rdf_end; t++) {
62 if (src_bytes == 0)
63 break;
64
65 switch (*t) {
66 case rdf_name:
67 case rdf_uname:
68 res = wdns_unpack_name(p, eop, src, domain_name, &len);
69 if (res != wdns_res_success)
70 goto parse_error;
71 src_bytes -= wdns_skip_name(&src, eop);
72 if (src_bytes < 0) {
73 res = wdns_res_out_of_bounds;
74 goto parse_error;
75 }
76 ubuf_append(u, domain_name, len);
77 break;
78
79 case rdf_bytes:
80 case rdf_bytes_b64:
81 case rdf_bytes_str:
82 copy_bytes(src_bytes);
83 break;
84
85 case rdf_int8:
86 copy_bytes(1);
87 break;
88
89 case rdf_int16:
90 case rdf_rrtype:
91 copy_bytes(2);
92 break;
93
94 case rdf_int32:
95 case rdf_ipv4:
96 copy_bytes(4);
97 break;
98
99 case rdf_ipv6:
100 copy_bytes(16);
101 break;
102
103 case rdf_eui48:
104 copy_bytes(6);
105 break;
106
107 case rdf_eui64:
108 copy_bytes(8);
109 break;
110
111 case rdf_string:
112 case rdf_salt:
113 case rdf_hash:
114 oclen = *src;
115 copy_bytes(oclen + 1);
116 break;
117
118 case rdf_repstring:
119 while (src_bytes > 0) {
120 oclen = *src;
121 copy_bytes(oclen + 1);
122 }
123 break;
124
125 case rdf_ipv6prefix:
126 oclen = *src;
127 if (oclen > 16U) {
128 res = wdns_res_out_of_bounds;
129 goto parse_error;
130 }
131 copy_bytes(oclen + 1);
132 break;
133
134 case rdf_type_bitmap: {
135 uint8_t bitmap_len;
136
137 while (src_bytes >= 2) {
138 bitmap_len = *(src + 1);
139
140 if (!(bitmap_len >= 1 && bitmap_len <= 32)) {
141 res = wdns_res_out_of_bounds;
142 goto parse_error;
143 }
144
145 if (bitmap_len <= (src_bytes - 2)) {
146 copy_bytes(2 + bitmap_len);
147 } else {
148 res = wdns_res_out_of_bounds;
149 goto parse_error;
150 }
151 }
152 break;
153 }
154
155 default:
156 fprintf(stderr, "%s: unhandled rdf type %u\n", __func__, *t);
157 abort();
158 }
159
160 }
161 if (src_bytes != 0) {
162 res = wdns_res_out_of_bounds;
163 goto parse_error;
164 }
165 } else {
166 /* unknown rrtype, treat generically */
167 copy_bytes(src_bytes);
168 }
169
170 /* load rr->rdata */
171 len = ubuf_size(u);
172 rr->rdata = my_malloc(sizeof(wdns_rdata_t) + len);
173 rr->rdata->len = len;
174 memcpy(rr->rdata->data, ubuf_cstr(u), len);
175 ubuf_destroy(&u);
176
177 return (wdns_res_success);
178
179 parse_error:
180 ubuf_destroy(&u);
181 return (res);
182
183 #undef advance_bytes
184 #undef copy_bytes
185 }
186