towire.c revision 1.1 1 1.1 christos /* towire.c
2 1.1 christos *
3 1.1 christos * Copyright (c) 2018-2021 Apple, Inc. All rights reserved.
4 1.1 christos *
5 1.1 christos * Licensed under the Apache License, Version 2.0 (the "License");
6 1.1 christos * you may not use this file except in compliance with the License.
7 1.1 christos * You may obtain a copy of the License at
8 1.1 christos *
9 1.1 christos * http://www.apache.org/licenses/LICENSE-2.0
10 1.1 christos *
11 1.1 christos * Unless required by applicable law or agreed to in writing, software
12 1.1 christos * distributed under the License is distributed on an "AS IS" BASIS,
13 1.1 christos * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 1.1 christos * See the License for the specific language governing permissions and
15 1.1 christos * limitations under the License.
16 1.1 christos *
17 1.1 christos * DNS to-wire wire-format functions.
18 1.1 christos *
19 1.1 christos * These are really simple functions for constructing DNS messages in wire format.
20 1.1 christos * The flow is that there is a transaction structure which contains pointers to both
21 1.1 christos * a message output buffer and a response input buffer. The structure is initialized,
22 1.1 christos * and then the various wire format functions are called repeatedly to store data.
23 1.1 christos * If an error occurs during this process, it's okay to just keep going, because the
24 1.1 christos * error is recorded in the transaction; once all of the copy-in functions have been
25 1.1 christos * called, the error status can be checked once at the end.
26 1.1 christos */
27 1.1 christos
28 1.1 christos #include <stdio.h>
29 1.1 christos #include <unistd.h>
30 1.1 christos #include <string.h>
31 1.1 christos #include <errno.h>
32 1.1 christos #ifndef THREAD_DEVKIT_ADK
33 1.1 christos #include <arpa/inet.h>
34 1.1 christos #endif
35 1.1 christos #include <stdlib.h>
36 1.1 christos
37 1.1 christos #include "srp.h"
38 1.1 christos #include "dns-msg.h"
39 1.1 christos #include "srp-crypto.h"
40 1.1 christos
41 1.1 christos #ifndef NO_CLOCK
42 1.1 christos #include <sys/time.h>
43 1.1 christos #endif
44 1.1 christos
45 1.1 christos static int
46 1.1 christos dns_parse_label(const char *cur, const char *NONNULL *NONNULL nextp, uint8_t *NONNULL lenp, uint8_t *NONNULL buf,
47 1.1 christos ssize_t max)
48 1.1 christos {
49 1.1 christos const char *end;
50 1.1 christos int tlen;
51 1.1 christos const char *s;
52 1.1 christos uint8_t *t;
53 1.1 christos
54 1.1 christos end = strchr(cur, '.');
55 1.1 christos if (end == NULL) {
56 1.1 christos end = cur + strlen(cur);
57 1.1 christos if (end == cur) {
58 1.1 christos *lenp = 0;
59 1.1 christos *nextp = NULL;
60 1.1 christos return 0;
61 1.1 christos }
62 1.1 christos *nextp = NULL;
63 1.1 christos } else {
64 1.1 christos if (end == cur) {
65 1.1 christos return EINVAL;
66 1.1 christos }
67 1.1 christos *nextp = end + 1;
68 1.1 christos }
69 1.1 christos
70 1.1 christos // Figure out the length of the label after escapes have been converted.
71 1.1 christos tlen = 0;
72 1.1 christos for (s = cur; s < end; s++) {
73 1.1 christos if (*s == '\\') {
74 1.1 christos if (s + 4 <= end) {
75 1.1 christos tlen++;
76 1.1 christos s += 3;
77 1.1 christos } else {
78 1.1 christos tlen++;
79 1.1 christos }
80 1.1 christos } else {
81 1.1 christos tlen++;
82 1.1 christos }
83 1.1 christos }
84 1.1 christos
85 1.1 christos // Is there no space?
86 1.1 christos
87 1.1 christos if (tlen >= max) {
88 1.1 christos return ENOBUFS;
89 1.1 christos }
90 1.1 christos
91 1.1 christos // Is the label too long?
92 1.1 christos if (end - cur > DNS_MAX_LABEL_SIZE) {
93 1.1 christos return ENAMETOOLONG;
94 1.1 christos }
95 1.1 christos
96 1.1 christos // Store the label length
97 1.1 christos *lenp = (uint8_t)(tlen);
98 1.1 christos
99 1.1 christos // Store the label.
100 1.1 christos t = buf;
101 1.1 christos for (s = cur; s < end; s++) {
102 1.1 christos if (*s == '\\') {
103 1.1 christos if (s + 4 <= end) {
104 1.1 christos int v0 = s[1] - '0';
105 1.1 christos int v1 = s[2] - '0';
106 1.1 christos int v2 = s[3] - '0';
107 1.1 christos int val = v0 * 100 + v1 * 10 + v2;
108 1.1 christos if (val < 255) {
109 1.1 christos *t++ = (uint8_t)val;
110 1.1 christos s += 3;
111 1.1 christos } else {
112 1.1 christos return EINVAL;
113 1.1 christos }
114 1.1 christos } else {
115 1.1 christos return EINVAL;
116 1.1 christos }
117 1.1 christos } else {
118 1.1 christos *t++ = (uint8_t)*s;
119 1.1 christos }
120 1.1 christos }
121 1.1 christos return 0;
122 1.1 christos }
123 1.1 christos
124 1.1 christos // Convert a name to wire format. Does not store the root label (0) at the end. Does not support binary labels.
125 1.1 christos void
126 1.1 christos dns_name_to_wire_(dns_name_pointer_t *NULLABLE r_pointer, dns_towire_state_t *NONNULL txn,
127 1.1 christos const char *NONNULL name, int line)
128 1.1 christos {
129 1.1 christos const char *next, *cur;
130 1.1 christos int status;
131 1.1 christos dns_name_pointer_t np;
132 1.1 christos
133 1.1 christos if (!txn->error) {
134 1.1 christos memset(&np, 0, sizeof np);
135 1.1 christos np.message_start = (uint8_t *)txn->message;
136 1.1 christos np.name_start = txn->p;
137 1.1 christos
138 1.1 christos cur = name;
139 1.1 christos do {
140 1.1 christos // Note that nothing is stored through txn->p until dns_name_parse has verified that
141 1.1 christos // there is space in the buffer for the label as well as the length.
142 1.1 christos status = dns_parse_label(cur, &next, txn->p, txn->p + 1, txn->lim - txn->p - 1);
143 1.1 christos if (status) {
144 1.1 christos if (status == ENOBUFS) {
145 1.1 christos txn->truncated = true;
146 1.1 christos }
147 1.1 christos txn->error = (unsigned)status;
148 1.1 christos txn->line = line;
149 1.1 christos return;
150 1.1 christos }
151 1.1 christos
152 1.1 christos // Don't use the root label if it was parsed.
153 1.1 christos if (*txn->p != 0) {
154 1.1 christos np.num_labels++;
155 1.1 christos np.length += 1 + *txn->p;
156 1.1 christos txn->p = txn->p + *txn->p + 1;
157 1.1 christos cur = next;
158 1.1 christos }
159 1.1 christos } while (next != NULL);
160 1.1 christos
161 1.1 christos if (np.length > DNS_MAX_NAME_SIZE) {
162 1.1 christos txn->error = ENAMETOOLONG;
163 1.1 christos txn->line = line;
164 1.1 christos return;
165 1.1 christos }
166 1.1 christos if (r_pointer != NULL) {
167 1.1 christos *r_pointer = np;
168 1.1 christos }
169 1.1 christos }
170 1.1 christos }
171 1.1 christos
172 1.1 christos // Like dns_name_to_wire, but includes the root label at the end.
173 1.1 christos void
174 1.1 christos dns_full_name_to_wire_(dns_name_pointer_t *NULLABLE r_pointer, dns_towire_state_t *NONNULL txn,
175 1.1 christos const char *NONNULL name, int line)
176 1.1 christos {
177 1.1 christos dns_name_pointer_t np;
178 1.1 christos if (!txn->error) {
179 1.1 christos memset(&np, 0, sizeof np);
180 1.1 christos dns_name_to_wire(&np, txn, name);
181 1.1 christos if (!txn->error) {
182 1.1 christos if (txn->p + 1 >= txn->lim) {
183 1.1 christos txn->error = ENOBUFS;
184 1.1 christos txn->truncated = true;
185 1.1 christos txn->line = line;
186 1.1 christos return;
187 1.1 christos }
188 1.1 christos *txn->p++ = 0;
189 1.1 christos np.num_labels++;
190 1.1 christos np.length += 1;
191 1.1 christos if (np.length > DNS_MAX_NAME_SIZE) {
192 1.1 christos txn->error = ENAMETOOLONG;
193 1.1 christos txn->line = line;
194 1.1 christos return;
195 1.1 christos }
196 1.1 christos if (r_pointer) {
197 1.1 christos *r_pointer = np;
198 1.1 christos }
199 1.1 christos }
200 1.1 christos }
201 1.1 christos }
202 1.1 christos
203 1.1 christos // Store a pointer to a name that's already in the message.
204 1.1 christos void
205 1.1 christos dns_pointer_to_wire_(dns_name_pointer_t *NULLABLE r_pointer, dns_towire_state_t *NONNULL txn,
206 1.1 christos dns_name_pointer_t *NONNULL pointer, int line)
207 1.1 christos {
208 1.1 christos if (!txn->error) {
209 1.1 christos uint16_t offset = (uint16_t)(pointer->name_start - pointer->message_start);
210 1.1 christos if (offset > DNS_MAX_POINTER) {
211 1.1 christos txn->error = ETOOMANYREFS;
212 1.1 christos txn->line = line;
213 1.1 christos return;
214 1.1 christos }
215 1.1 christos if (txn->p + 2 >= txn->lim) {
216 1.1 christos txn->error = ENOBUFS;
217 1.1 christos txn->truncated = true;
218 1.1 christos txn->line = line;
219 1.1 christos return;
220 1.1 christos }
221 1.1 christos *txn->p++ = 0xc0 | (offset >> 8);
222 1.1 christos *txn->p++ = offset & 0xff;
223 1.1 christos if (r_pointer) {
224 1.1 christos r_pointer->num_labels += pointer->num_labels;
225 1.1 christos r_pointer->length += pointer->length + 1;
226 1.1 christos if (r_pointer->length > DNS_MAX_NAME_SIZE) {
227 1.1 christos txn->error = ENAMETOOLONG;
228 1.1 christos txn->line = line;
229 1.1 christos return;
230 1.1 christos }
231 1.1 christos }
232 1.1 christos }
233 1.1 christos }
234 1.1 christos
235 1.1 christos void
236 1.1 christos dns_u8_to_wire_(dns_towire_state_t *NONNULL txn, uint8_t val, int line)
237 1.1 christos {
238 1.1 christos if (!txn->error) {
239 1.1 christos if (txn->p + 1 >= txn->lim) {
240 1.1 christos txn->error = ENOBUFS;
241 1.1 christos txn->truncated = true;
242 1.1 christos txn->line = line;
243 1.1 christos return;
244 1.1 christos }
245 1.1 christos *txn->p++ = val;
246 1.1 christos }
247 1.1 christos }
248 1.1 christos
249 1.1 christos // Store a 16-bit integer in network byte order
250 1.1 christos void
251 1.1 christos dns_u16_to_wire_(dns_towire_state_t *NONNULL txn, uint16_t val, int line)
252 1.1 christos {
253 1.1 christos if (!txn->error) {
254 1.1 christos if (txn->p + 2 >= txn->lim) {
255 1.1 christos txn->error = ENOBUFS;
256 1.1 christos txn->truncated = true;
257 1.1 christos txn->line = line;
258 1.1 christos return;
259 1.1 christos }
260 1.1 christos *txn->p++ = val >> 8;
261 1.1 christos *txn->p++ = val & 0xff;
262 1.1 christos }
263 1.1 christos }
264 1.1 christos
265 1.1 christos void
266 1.1 christos dns_u32_to_wire_(dns_towire_state_t *NONNULL txn, uint32_t val, int line)
267 1.1 christos {
268 1.1 christos if (!txn->error) {
269 1.1 christos if (txn->p + 4 >= txn->lim) {
270 1.1 christos txn->error = ENOBUFS;
271 1.1 christos txn->truncated = true;
272 1.1 christos txn->line = line;
273 1.1 christos return;
274 1.1 christos }
275 1.1 christos *txn->p++ = val >> 24;
276 1.1 christos *txn->p++ = (val >> 16) & 0xff;
277 1.1 christos *txn->p++ = (val >> 8) & 0xff;
278 1.1 christos *txn->p++ = val & 0xff;
279 1.1 christos }
280 1.1 christos }
281 1.1 christos
282 1.1 christos void
283 1.1 christos dns_u64_to_wire_(dns_towire_state_t *NONNULL txn, uint64_t val, int line)
284 1.1 christos {
285 1.1 christos if (!txn->error) {
286 1.1 christos if (txn->p + 8 >= txn->lim) {
287 1.1 christos txn->error = ENOBUFS;
288 1.1 christos txn->truncated = true;
289 1.1 christos txn->line = line;
290 1.1 christos return;
291 1.1 christos }
292 1.1 christos *txn->p++ = val >> 56;
293 1.1 christos *txn->p++ = (val >> 48) & 0xff;
294 1.1 christos *txn->p++ = (val >> 40) & 0xff;
295 1.1 christos *txn->p++ = (val >> 32) & 0xff;
296 1.1 christos *txn->p++ = (val >> 24) & 0xff;
297 1.1 christos *txn->p++ = (val >> 16) & 0xff;
298 1.1 christos *txn->p++ = (val >> 8) & 0xff;
299 1.1 christos *txn->p++ = val & 0xff;
300 1.1 christos }
301 1.1 christos }
302 1.1 christos
303 1.1 christos void
304 1.1 christos dns_ttl_to_wire_(dns_towire_state_t *NONNULL txn, int32_t val, int line)
305 1.1 christos {
306 1.1 christos if (!txn->error) {
307 1.1 christos dns_u32_to_wire_(txn, (uint32_t)val, line);
308 1.1 christos }
309 1.1 christos }
310 1.1 christos
311 1.1 christos void
312 1.1 christos dns_rdlength_begin_(dns_towire_state_t *NONNULL txn, int line)
313 1.1 christos {
314 1.1 christos if (!txn->error) {
315 1.1 christos if (txn->p + 2 >= txn->lim) {
316 1.1 christos txn->error = ENOBUFS;
317 1.1 christos txn->truncated = true;
318 1.1 christos txn->line = line;
319 1.1 christos return;
320 1.1 christos }
321 1.1 christos if (txn->p_rdlength != NULL) {
322 1.1 christos txn->error = EINVAL;
323 1.1 christos txn->line = line;
324 1.1 christos return;
325 1.1 christos }
326 1.1 christos txn->p_rdlength = txn->p;
327 1.1 christos txn->p += 2;
328 1.1 christos }
329 1.1 christos }
330 1.1 christos
331 1.1 christos void
332 1.1 christos dns_rdlength_end_(dns_towire_state_t *NONNULL txn, int line)
333 1.1 christos {
334 1.1 christos ssize_t rdlength;
335 1.1 christos if (!txn->error) {
336 1.1 christos if (txn->p_rdlength == NULL) {
337 1.1 christos txn->error = EINVAL;
338 1.1 christos txn->line = line;
339 1.1 christos return;
340 1.1 christos }
341 1.1 christos rdlength = txn->p - txn->p_rdlength - 2;
342 1.1 christos txn->p_rdlength[0] = (uint8_t)(rdlength >> 8);
343 1.1 christos txn->p_rdlength[1] = (uint8_t)(rdlength & 0xff);
344 1.1 christos txn->p_rdlength = NULL;
345 1.1 christos }
346 1.1 christos }
347 1.1 christos
348 1.1 christos #ifndef THREAD_DEVKIT_ADK
349 1.1 christos void
350 1.1 christos dns_rdata_a_to_wire_(dns_towire_state_t *NONNULL txn, const char *NONNULL ip_address, int line)
351 1.1 christos {
352 1.1 christos if (!txn->error) {
353 1.1 christos if (txn->p + 4 >= txn->lim) {
354 1.1 christos txn->error = ENOBUFS;
355 1.1 christos txn->truncated = true;
356 1.1 christos txn->line = line;
357 1.1 christos return;
358 1.1 christos }
359 1.1 christos if (!inet_pton(AF_INET, ip_address, txn->p)) {
360 1.1 christos txn->error = EINVAL;
361 1.1 christos txn->line = line;
362 1.1 christos return;
363 1.1 christos }
364 1.1 christos txn->p += 4;
365 1.1 christos }
366 1.1 christos }
367 1.1 christos
368 1.1 christos void
369 1.1 christos dns_rdata_aaaa_to_wire_(dns_towire_state_t *NONNULL txn, const char *NONNULL ip_address, int line)
370 1.1 christos {
371 1.1 christos if (!txn->error) {
372 1.1 christos if (txn->p + 16 >= txn->lim) {
373 1.1 christos txn->error = ENOBUFS;
374 1.1 christos txn->truncated = true;
375 1.1 christos txn->line = line;
376 1.1 christos return;
377 1.1 christos }
378 1.1 christos if (!inet_pton(AF_INET6, ip_address, txn->p)) {
379 1.1 christos txn->error = EINVAL;
380 1.1 christos txn->line = line;
381 1.1 christos return;
382 1.1 christos }
383 1.1 christos txn->p += 16;
384 1.1 christos }
385 1.1 christos }
386 1.1 christos #endif
387 1.1 christos
388 1.1 christos uint16_t
389 1.1 christos dns_rdata_key_to_wire_(dns_towire_state_t *NONNULL txn, unsigned key_type, unsigned name_type,
390 1.1 christos uint8_t signatory, srp_key_t *key, int line)
391 1.1 christos {
392 1.1 christos size_t key_len = srp_pubkey_length(key), copied_len;
393 1.1 christos uint8_t *rdata = txn->p;
394 1.1 christos uint32_t key_tag;
395 1.1 christos int i;
396 1.1 christos ssize_t rdlen;
397 1.1 christos
398 1.1 christos if (!txn->error) {
399 1.1 christos if (key_type > 3 || name_type > 3 || signatory > 15) {
400 1.1 christos txn->error = EINVAL;
401 1.1 christos txn->line = line;
402 1.1 christos return 0;
403 1.1 christos }
404 1.1 christos if (txn->p + key_len + 4 >= txn->lim) {
405 1.1 christos txn->error = ENOBUFS;
406 1.1 christos txn->truncated = true;
407 1.1 christos txn->line = line;
408 1.1 christos return 0;
409 1.1 christos }
410 1.1 christos *txn->p++ = (uint8_t)((key_type << 6) | name_type);
411 1.1 christos *txn->p++ = signatory;
412 1.1 christos *txn->p++ = 3; // protocol type is always 3
413 1.1 christos *txn->p++ = srp_key_algorithm(key);
414 1.1 christos copied_len = srp_pubkey_copy(txn->p, key_len, key);
415 1.1 christos if (copied_len == 0) {
416 1.1 christos txn->error = EINVAL;
417 1.1 christos txn->line = line;
418 1.1 christos return 0;
419 1.1 christos }
420 1.1 christos txn->p += key_len;
421 1.1 christos }
422 1.1 christos rdlen = txn->p - rdata;
423 1.1 christos
424 1.1 christos // Compute the key tag
425 1.1 christos key_tag = 0;
426 1.1 christos for (i = 0; i < rdlen; i++) {
427 1.1 christos key_tag += (i & 1) ? rdata[i] : (uint16_t)(rdata[i] << 8);
428 1.1 christos }
429 1.1 christos key_tag += (key_tag >> 16) & 0xFFFF;
430 1.1 christos return (uint16_t)(key_tag & 0xFFFF);
431 1.1 christos }
432 1.1 christos
433 1.1 christos void
434 1.1 christos dns_rdata_txt_to_wire_(dns_towire_state_t *NONNULL txn, const char *NONNULL txt_record, int line)
435 1.1 christos {
436 1.1 christos if (!txn->error) {
437 1.1 christos size_t len = strlen(txt_record);
438 1.1 christos if (txn->p + len + 1 >= txn->lim) {
439 1.1 christos txn->error = ENOBUFS;
440 1.1 christos txn->truncated = true;
441 1.1 christos txn->line = line;
442 1.1 christos return;
443 1.1 christos }
444 1.1 christos if (len > 255) {
445 1.1 christos txn->error = ENAMETOOLONG;
446 1.1 christos txn->line = line;
447 1.1 christos return;
448 1.1 christos }
449 1.1 christos *txn->p++ = (uint8_t)len;
450 1.1 christos memcpy(txn->p, txt_record, len);
451 1.1 christos txn->p += len;
452 1.1 christos }
453 1.1 christos }
454 1.1 christos
455 1.1 christos void
456 1.1 christos dns_rdata_raw_data_to_wire_(dns_towire_state_t *NONNULL txn, const void *NONNULL raw_data, size_t length, int line)
457 1.1 christos {
458 1.1 christos if (!txn->error) {
459 1.1 christos if (txn->p + length > txn->lim) {
460 1.1 christos txn->error = ENOBUFS;
461 1.1 christos txn->truncated = true;
462 1.1 christos txn->line = line;
463 1.1 christos return;
464 1.1 christos }
465 1.1 christos memcpy(txn->p, raw_data, length);
466 1.1 christos txn->p += length;
467 1.1 christos }
468 1.1 christos }
469 1.1 christos
470 1.1 christos void
471 1.1 christos dns_edns0_header_to_wire_(dns_towire_state_t *NONNULL txn, uint16_t mtu, uint8_t xrcode, uint8_t version, bool DO, int line)
472 1.1 christos {
473 1.1 christos if (!txn->error) {
474 1.1 christos if (txn->p + 9 >= txn->lim) {
475 1.1 christos txn->error = ENOBUFS;
476 1.1 christos txn->truncated = true;
477 1.1 christos txn->line = line;
478 1.1 christos return;
479 1.1 christos }
480 1.1 christos *txn->p++ = 0; // root label
481 1.1 christos dns_u16_to_wire(txn, dns_rrtype_opt);
482 1.1 christos dns_u16_to_wire(txn, mtu);
483 1.1 christos *txn->p++ = xrcode;
484 1.1 christos *txn->p++ = version;
485 1.1 christos *txn->p++ = DO ? 1 << 7 : 0; // flags (usb)
486 1.1 christos *txn->p++ = 0; // flags (lsb, mbz)
487 1.1 christos }
488 1.1 christos }
489 1.1 christos
490 1.1 christos void
491 1.1 christos dns_edns0_option_begin_(dns_towire_state_t *NONNULL txn, int line)
492 1.1 christos {
493 1.1 christos if (!txn->error) {
494 1.1 christos if (txn->p + 2 >= txn->lim) {
495 1.1 christos txn->error = ENOBUFS;
496 1.1 christos txn->truncated = true;
497 1.1 christos txn->line = line;
498 1.1 christos return;
499 1.1 christos }
500 1.1 christos if (txn->p_opt != NULL) {
501 1.1 christos txn->error = EINVAL;
502 1.1 christos txn->line = line;
503 1.1 christos return;
504 1.1 christos }
505 1.1 christos txn->p_opt = txn->p;
506 1.1 christos txn->p += 2;
507 1.1 christos }
508 1.1 christos }
509 1.1 christos
510 1.1 christos void
511 1.1 christos dns_edns0_option_end_(dns_towire_state_t *NONNULL txn, int line)
512 1.1 christos {
513 1.1 christos ssize_t opt_length;
514 1.1 christos if (!txn->error) {
515 1.1 christos if (txn->p_opt == NULL) {
516 1.1 christos txn->error = EINVAL;
517 1.1 christos txn->line = line;
518 1.1 christos return;
519 1.1 christos }
520 1.1 christos opt_length = txn->p - txn->p_opt - 2;
521 1.1 christos txn->p_opt[0] = (uint8_t)(opt_length >> 8);
522 1.1 christos txn->p_opt[1] = opt_length & 0xff;
523 1.1 christos txn->p_opt = NULL;
524 1.1 christos }
525 1.1 christos }
526 1.1 christos
527 1.1 christos void
528 1.1 christos dns_sig0_signature_to_wire_(dns_towire_state_t *NONNULL txn, srp_key_t *key, uint16_t key_tag,
529 1.1 christos dns_name_pointer_t *NONNULL signer, const char *NONNULL signer_hostname,
530 1.1 christos const char *NONNULL signer_domain, uint32_t timenow, int line)
531 1.1 christos {
532 1.1 christos size_t siglen = srp_signature_length(key);
533 1.1 christos uint8_t *start, *p_signer, *p_signature, *rrstart = txn->p;
534 1.1 christos
535 1.1 christos // 1 name (root)
536 1.1 christos // 2 type (SIG)
537 1.1 christos // 2 class (255) ANY
538 1.1 christos // 4 TTL (0)
539 1.1 christos // 18 SIG RDATA up to signer name
540 1.1 christos // 2 signer name (always a pointer)
541 1.1 christos // 29 bytes so far
542 1.1 christos // signature data (depends on algorithm, e.g. 64 for ECDSASHA256)
543 1.1 christos // so e.g. 93 bytes total
544 1.1 christos
545 1.1 christos if (!txn->error) {
546 1.1 christos dns_u8_to_wire(txn, 0); // root label
547 1.1 christos dns_u16_to_wire(txn, dns_rrtype_sig);
548 1.1 christos dns_u16_to_wire(txn, dns_qclass_any); // class
549 1.1 christos dns_ttl_to_wire(txn, 0); // SIG RR TTL
550 1.1 christos dns_rdlength_begin(txn);
551 1.1 christos start = txn->p;
552 1.1 christos dns_u16_to_wire(txn, 0); // type = 0 for transaction signature
553 1.1 christos dns_u8_to_wire(txn, srp_key_algorithm(key));
554 1.1 christos dns_u8_to_wire(txn, 0); // labels field doesn't apply for transaction signature
555 1.1 christos dns_ttl_to_wire(txn, 0); // original ttl doesn't apply
556 1.1 christos // If timenow is <300, it's either just after the epoch, or the caller doesn't know what time it is.
557 1.1 christos if (timenow < 300) {
558 1.1 christos dns_u32_to_wire(txn, 0); // Indicate that we have no clock: set expiry and inception times to zero
559 1.1 christos dns_u32_to_wire(txn, 0);
560 1.1 christos } else {
561 1.1 christos dns_u32_to_wire(txn, timenow + 300); // signature expiration time is five minutes from now
562 1.1 christos dns_u32_to_wire(txn, timenow - 300); // signature inception time, five minutes in the past
563 1.1 christos }
564 1.1 christos dns_u16_to_wire(txn, key_tag);
565 1.1 christos
566 1.1 christos p_signer = txn->p;
567 1.1 christos // We store the name in uncompressed form because that's what we have to sign
568 1.1 christos if (signer_hostname != NULL) {
569 1.1 christos dns_name_to_wire(NULL, txn, signer_hostname);
570 1.1 christos }
571 1.1 christos dns_full_name_to_wire(NULL, txn, signer_domain);
572 1.1 christos // And that means we're going to have to copy the signature back earlier in the packet.
573 1.1 christos p_signature = txn->p;
574 1.1 christos
575 1.1 christos // Sign the message, signature RRDATA (less signature) first.
576 1.1 christos if (!srp_sign(txn->p, siglen, (uint8_t *)txn->message, (size_t)(rrstart - (uint8_t *)txn->message),
577 1.1 christos start, (size_t)(txn->p - start), key)) {
578 1.1 christos txn->error = true;
579 1.1 christos txn->line = __LINE__;
580 1.1 christos } else {
581 1.1 christos // Now that it's signed, back up and store the pointer to the name, because we're trying
582 1.1 christos // to be as compact as possible.
583 1.1 christos txn->p = p_signer;
584 1.1 christos dns_pointer_to_wire(NULL, txn, signer); // Pointer to the owner name the key is attached to
585 1.1 christos // And move the signature earlier in the packet.
586 1.1 christos memmove(txn->p, p_signature, siglen);
587 1.1 christos
588 1.1 christos txn->p += siglen;
589 1.1 christos dns_rdlength_end(txn);
590 1.1 christos }
591 1.1 christos
592 1.1 christos if (txn->error) {
593 1.1 christos txn->outer_line = line;
594 1.1 christos }
595 1.1 christos }
596 1.1 christos }
597 1.1 christos
598 1.1 christos // Local Variables:
599 1.1 christos // mode: C
600 1.1 christos // tab-width: 4
601 1.1 christos // c-file-style: "bsd"
602 1.1 christos // c-basic-offset: 4
603 1.1 christos // fill-column: 108
604 1.1 christos // indent-tabs-mode: nil
605 1.1 christos // End:
606