1 /* wireutils.c
2  *
3  * Copyright (c) 2019 Apple Computer, Inc. All rights reserved.
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  *     http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  *
17  * DNS wire-format utility functions.
18  *
19  * Functions that are neither necessary for very simple DNS packet generation, nor required for parsing
20  * a message, e.g. compression, name printing, etc.
21  */
22 
23 #include <stdio.h>
24 #include <unistd.h>
25 #include <string.h>
26 #include <errno.h>
27 #include <sys/socket.h>
28 #include <arpa/inet.h>
29 #include <stdlib.h>
30 #include <ctype.h>
31 #include "srp.h"
32 #include "dns-msg.h"
33 
34 #include "mDNSEmbeddedAPI.h"
35 #include "DNSCommon.h"
36 
37 #undef LogMsg
38 #define LogMsg(...)
39 
40 // We need the compression routines from DNSCommon.c, but we can't link to it because that
41 // pulls in a _lot_ of stuff we don't want.   The solution?   Define STANDALONE (this is done
42 // in the Makefile, and include DNSCommon.c.
43 //
44 // The only functions that aren't excluded by STANDALONE are FindCompressionPointer and
45 // putDomainNameAsLabels.
46 
47 #define STANDALONE
48 #include "../mDNSCore/DNSCommon.c"
49 
dns_name_free(dns_label_t * name)50 void dns_name_free(dns_label_t *name)
51 {
52     dns_label_t *next;
53     if (name == NULL) {
54         return;
55     }
56     next = name->next;
57     free(name);
58     if (next != NULL) {
59         return dns_name_free(next);
60     }
61 }
62 
63 dns_name_t *
dns_name_copy(dns_name_t * original)64 dns_name_copy(dns_name_t *original)
65 {
66     dns_name_t *ret = NULL, **cur = &ret;
67     dns_name_t *next;
68 
69     for (next = original; next; next = next->next) {
70         *cur = calloc(1, 1 + next->len + (sizeof (dns_name_t)) - DNS_MAX_LABEL_SIZE);
71         if (*cur == NULL) {
72             if (ret != NULL) {
73                 dns_name_free(ret);
74             }
75             return NULL;
76         }
77         if (next->len) {
78             memcpy((*cur)->data, next->data, next->len + 1);
79         }
80         (*cur)->len = next->len;
81         cur = &((*cur)->next);
82     }
83     return ret;
84 }
85 
86 // Needed for TSIG (RFC2845).
87 void
dns_u48_to_wire_(dns_towire_state_t * NONNULL txn,uint64_t val,int line)88 dns_u48_to_wire_(dns_towire_state_t *NONNULL txn,
89                  uint64_t val, int line)
90 {
91     if (!txn->error) {
92         if (txn->p + 6 >= txn->lim) {
93             txn->error = ENOBUFS;
94             txn->truncated = true;
95             txn->line = line;
96             return;
97         }
98         *txn->p++ = (val >> 40) & 0xff;
99         *txn->p++ = (val >> 32) & 0xff;
100         *txn->p++ = (val >> 24) & 0xff;
101         *txn->p++ = (val >> 16) & 0xff;
102         *txn->p++ = (val >> 8) & 0xff;
103         *txn->p++ = val & 0xff;
104     }
105 }
106 
107 void
dns_concatenate_name_to_wire_(dns_towire_state_t * towire,dns_name_t * labels_prefix,const char * prefix,const char * suffix,int line)108 dns_concatenate_name_to_wire_(dns_towire_state_t *towire, dns_name_t *labels_prefix, const char *prefix,
109                               const char *suffix, int line)
110 {
111     dns_wire_t namebuf;
112     dns_towire_state_t namewire;
113     mDNSu8 *ret;
114     namebuf.data[0] = 0;
115 
116     // Don't do all this work if we're already past an error.
117     if (towire->error) {
118         return;
119     }
120     memset(&namewire, 0, sizeof namewire);
121     namewire.message = &namebuf;
122     namewire.lim = &namebuf.data[DNS_DATA_SIZE];
123     namewire.p = namebuf.data;
124     if (prefix != NULL) {
125         dns_name_to_wire(NULL, &namewire, prefix);
126     } else if (labels_prefix != NULL) {
127         size_t bytes_written;
128 
129         if (!namewire.error) {
130             bytes_written = namewire.lim - namewire.p;
131             if (bytes_written > INT16_MAX) {
132                 towire->error = true;
133                 towire->line = __LINE__;
134                 return;
135             }
136             bytes_written = dns_name_to_wire_canonical(namewire.p,  (int)bytes_written, labels_prefix);
137             // This can never occur with a valid name.
138             if (bytes_written == 0) {
139                 namewire.truncated = true;
140             } else {
141                 namewire.p += bytes_written;
142             }
143         }
144     }
145     if (suffix != NULL) {
146         dns_full_name_to_wire(NULL, &namewire, suffix);
147     }
148     if (namewire.error) {
149         towire->truncated = namewire.truncated;
150         towire->error = namewire.error;
151         towire->line = line;
152     }
153 
154     ret = putDomainNameAsLabels((DNSMessage *)towire->message, towire->p, towire->lim, (domainname *)namebuf.data);
155     if (ret == NULL) {
156         towire->error = ENOBUFS;
157         towire->truncated = true;
158         towire->line = line;
159         return;
160     }
161 
162     // Shouldn't happen
163     if (ret > towire->lim) {
164         towire->error = ENOBUFS;
165         towire->truncated = true;
166         towire->line = line;
167     } else {
168         towire->p = ret;
169     }
170 }
171 
172 // Convert a dns_name_t to presentation format.   Stop conversion at the specified limit.
173 // A trailing dot is only written if a null label is present.
174 const char *NONNULL
dns_name_print_to_limit(dns_name_t * NONNULL name,dns_name_t * NULLABLE limit,char * buf,int bufmax)175 dns_name_print_to_limit(dns_name_t *NONNULL name, dns_name_t *NULLABLE limit, char *buf, int bufmax)
176 {
177     dns_label_t *lp;
178     int ix = 0;
179     int i;
180 
181     // Copy the labels in one at a time, putting a dot between each one; if there isn't room
182     // in the buffer (shouldn't be the case), copy as much as will fit, leaving room for a NUL
183     // termination.
184     for (lp = name; lp != limit && lp != NULL; lp = lp->next) {
185         if (ix != 0) {
186             if (ix + 2 >= bufmax) {
187                 break;
188             }
189             buf[ix++] = '.';
190         }
191         for (i = 0; i < lp->len; i++) {
192             if (isascii(lp->data[i]) && (lp->data[i] == ' ' || isprint(lp->data[i]))) {
193                 if (ix + 2 >= bufmax) {
194                     break;
195                 }
196                 buf[ix++] = lp->data[i];
197             } else {
198                 if (ix + 5 >= bufmax) {
199                     break;
200                 }
201                 buf[ix++] = '\\';
202                 buf[ix++] = '0' + (lp->data[i] / 100);
203                 buf[ix++] = '0' + (lp->data[i] /  10) % 10;
204                 buf[ix++] = '0' + lp->data[i]         % 10;
205             }
206         }
207         if (i != lp->len) {
208             break;
209         }
210     }
211     buf[ix++] = 0;
212     return buf;
213 }
214 
215 const char *NONNULL
dns_name_print(dns_name_t * NONNULL name,char * buf,int bufmax)216 dns_name_print(dns_name_t *NONNULL name, char *buf, int bufmax)
217 {
218     return dns_name_print_to_limit(name, NULL, buf, bufmax);
219 }
220 
221 bool
dns_labels_equal(const char * label1,const char * label2,size_t len)222 dns_labels_equal(const char *label1, const char *label2, size_t len)
223 {
224     unsigned i;
225     for (i = 0; i < len; i++) {
226         if (isascii(label1[i]) && isascii(label2[i])) {
227             if (tolower(label1[i]) != tolower(label2[i])) {
228                 return false;
229             }
230         }
231         else {
232             if (label1[i] != label2[i]) {
233                 return false;
234             }
235         }
236     }
237     return true;
238 }
239 
240 bool
dns_names_equal(dns_label_t * NONNULL name1,dns_label_t * NONNULL name2)241 dns_names_equal(dns_label_t *NONNULL name1, dns_label_t *NONNULL name2)
242 {
243     if (name1->len != name2->len) {
244         return false;
245     }
246     if (name1->len != 0 && !dns_labels_equal(name1->data, name2->data, name1->len) != 0) {
247         return false;
248     }
249     if (name1->next != NULL && name2->next != NULL) {
250         return dns_names_equal(name1->next, name2->next);
251     }
252     if (name1->next == NULL && name2->next == NULL) {
253         return true;
254     }
255     return false;
256 }
257 
258 // Note that "foo.arpa" is not the same as "foo.arpa."
259 bool
dns_names_equal_text(dns_label_t * NONNULL name1,const char * NONNULL name2)260 dns_names_equal_text(dns_label_t *NONNULL name1, const char *NONNULL name2)
261 {
262     const char *ndot;
263     const char *s, *t;
264     int tlen = 0;
265     ndot = strchr(name2, '.');
266     if (ndot == NULL) {
267         ndot = name2 + strlen(name2);
268     }
269     for (s = name2; s < ndot; s++) {
270         if (*s == '\\') {
271             if (s + 4 <= ndot) {
272                 tlen++;
273                 s += 3;
274             } else {
275                 return false;  // An invalid name can't be equal to anything.
276             }
277         } else {
278             tlen++;
279         }
280     }
281     if (name1->len != tlen) {
282         return false;
283     }
284     if (name1->len != 0) {
285         t = name1->data;
286         for (s = name2; s < ndot; s++, t++) {
287             if (*s == '\\') { // already bounds checked
288                 int v0 = s[1] - '0';
289                 int v1 = s[2] - '0';
290                 int v2 = s[3] - '0';
291                 int val = v0 * 100 + v1 * 10 + v2;
292                 if (val > 255) {
293                     return false;
294                 } else if (isascii(*s) && isascii(*t)) {
295                     if (tolower(*s) != tolower(*t)) {
296                         return false;
297                     }
298                 } else if (val != *t) {
299                     return false;
300                 }
301                 s += 3;
302             } else {
303                 if (*s != *t) {
304                     return false;
305                 }
306             }
307         }
308     }
309     if (name1->next != NULL && *ndot == '.') {
310         return dns_names_equal_text(name1->next, ndot + 1);
311     }
312     if (name1->next == NULL && *ndot == 0) {
313         return true;
314     }
315     return false;
316 }
317 
318 // Find the length of a name in uncompressed wire format.
319 static size_t
dns_name_wire_length_in(dns_label_t * NONNULL name,size_t ret)320 dns_name_wire_length_in(dns_label_t *NONNULL name, size_t ret)
321 {
322     // Root label.
323     if (name == NULL)
324         return ret;
325     return dns_name_wire_length_in(name->next, ret + name->len + 1);
326 }
327 
328 size_t
dns_name_wire_length(dns_label_t * NONNULL name)329 dns_name_wire_length(dns_label_t *NONNULL name)
330 {
331     return dns_name_wire_length_in(name, 0);
332 }
333 
334 // Copy a name we've parsed from a message out in canonical wire format so that we can
335 // use it to verify a signature.   As above, not actually needed for copying to a message
336 // we're going to send, since in that case we want to try to compress.
337 static size_t
dns_name_to_wire_canonical_in(uint8_t * NONNULL buf,size_t max,size_t ret,dns_label_t * NONNULL name)338 dns_name_to_wire_canonical_in(uint8_t *NONNULL buf, size_t max, size_t ret, dns_label_t *NONNULL name)
339 {
340     if (name == NULL) {
341         return ret;
342     }
343     if (max < name->len + 1) {
344         return 0;
345     }
346     *buf = name->len;
347     memcpy(buf + 1, name->data, name->len);
348     return dns_name_to_wire_canonical_in(buf + name->len + 1,
349                                          max - name->len - 1, ret + name->len + 1, name->next);
350 }
351 
352 size_t
dns_name_to_wire_canonical(uint8_t * NONNULL buf,size_t max,dns_label_t * NONNULL name)353 dns_name_to_wire_canonical(uint8_t *NONNULL buf, size_t max, dns_label_t *NONNULL name)
354 {
355     return dns_name_to_wire_canonical_in(buf, max, 0, name);
356 }
357 
358 // Parse a NUL-terminated text string into a sequence of labels.
359 dns_name_t *
dns_pres_name_parse(const char * pname)360 dns_pres_name_parse(const char *pname)
361 {
362     const char *dot, *s, *label;
363     dns_label_t *next, *ret, **prev = &ret;
364     size_t len;
365     char *t;
366     char buf[DNS_MAX_LABEL_SIZE];
367     ret = NULL;
368 
369     label = pname;
370     dot = strchr(label, '.');
371     while (true) {
372         if (dot == NULL) {
373             dot = label + strlen(label);
374         }
375         len = dot - label;
376         if (len > 0) {
377             t = buf;
378             for (s = label; s < dot; s++) {
379                 if (*s == '\\') { // already bounds checked
380                     int v0 = s[1] - '0';
381                     int v1 = s[2] - '0';
382                     int v2 = s[3] - '0';
383                     int val = v0 * 100 + v1 * 10 + v2;
384                     if (val > 255) {
385                         goto fail;
386                     }
387                     s += 3;
388                     *t++ = val;
389                 } else {
390                     *t++ = *s;
391                 }
392             }
393             len = t - buf;
394         }
395         next = calloc(1, len + 1 + (sizeof *next) - DNS_MAX_LABEL_SIZE);
396         if (next == NULL) {
397             goto fail;
398         }
399         *prev = next;
400         prev = &next->next;
401         next->len = len;
402         if (next->len > 0) {
403             memcpy(next->data, buf, next->len);
404         }
405         next->data[next->len] = 0;
406         if (dot[0] == '.' && len > 0) {
407             dot = dot + 1;
408         }
409         if (*dot == '\0') {
410             if (len > 0) {
411                 label = dot;
412             } else {
413                 break;
414             }
415         } else {
416             label = dot;
417             dot = strchr(label, '.');
418         }
419     }
420     return ret;
421 
422 fail:
423     if (ret) {
424         dns_name_free(ret);
425     }
426     return NULL;
427 }
428 
429 // See if name is a subdomain of domain.   If so, return a pointer to the label in name
430 // where the match to domain begins.
431 dns_name_t *
dns_name_subdomain_of(dns_name_t * name,dns_name_t * domain)432 dns_name_subdomain_of(dns_name_t *name, dns_name_t *domain)
433 {
434     int dnum = 0, nnum = 0;
435     dns_name_t *np, *dp;
436 
437     for (dp = domain; dp; dp = dp->next) {
438         dnum++;
439     }
440     for (np = name; np; np = np->next) {
441         nnum++;
442     }
443     if (nnum < dnum) {
444         return NULL;
445     }
446     for (np = name; np; np = np->next) {
447         if (nnum-- == dnum) {
448             break;
449         }
450     }
451     if (np != NULL && dns_names_equal(np, domain)) {
452         return np;
453     }
454     return NULL;
455 }
456 
457 const char *
dns_rcode_name(int rcode)458 dns_rcode_name(int rcode)
459 {
460     switch(rcode) {
461     case dns_rcode_noerror:
462         return "No Error";
463     case dns_rcode_formerr:
464         return "Format Error";
465     case dns_rcode_servfail:
466         return "Server Failure";
467     case dns_rcode_nxdomain:
468         return "Non-Existent Domain";
469     case dns_rcode_notimp:
470         return "Not Implemented";
471     case dns_rcode_refused:
472         return "Query Refused";
473     case dns_rcode_yxdomain:
474         return "RFC6672] Name Exists when it should not";
475     case dns_rcode_yxrrset:
476         return "RR Set Exists when it should not";
477     case dns_rcode_nxrrset:
478         return "RR Set that should exist does not";
479     case dns_rcode_notauth:
480         return "Not Authorized";
481     case dns_rcode_notzone:
482         return "Name not contained in zone";
483     case dns_rcode_dsotypeni:
484         return "DSO-Type Not Implemented";
485     case dns_rcode_badvers:
486         return "TSIG Signature Failure";
487     case dns_rcode_badkey:
488         return "Key not recognized";
489     case dns_rcode_badtime:
490         return "Signature out of time window";
491     case dns_rcode_badmode:
492         return "Bad TKEY Mode";
493     case dns_rcode_badname:
494         return "Duplicate key name";
495     case dns_rcode_badalg:
496         return "Algorithm not supported";
497     case dns_rcode_badtrunc:
498         return "Bad Truncation";
499     case dns_rcode_badcookie:
500         return "Bad/missing Server Cookie";
501     default:
502         return "Unknown rcode.";
503     }
504 }
505 
506 bool
dns_keys_rdata_equal(dns_rr_t * key1,dns_rr_t * key2)507 dns_keys_rdata_equal(dns_rr_t *key1, dns_rr_t *key2)
508 {
509     if ((key1->type == dns_rrtype_key && key2->type == dns_rrtype_key) &&
510         key1->data.key.flags == key2->data.key.flags &&
511         key1->data.key.protocol == key2->data.key.protocol &&
512         key1->data.key.algorithm == key2->data.key.algorithm &&
513         key1->data.key.len == key2->data.key.len &&
514         !memcmp(key1->data.key.key, key2->data.key.key, key1->data.key.len))
515     {
516         return true;
517     }
518     return false;
519 }
520 
521 // Local Variables:
522 // mode: C
523 // tab-width: 4
524 // c-file-style: "bsd"
525 // c-basic-offset: 4
526 // fill-column: 108
527 // indent-tabs-mode: nil
528 // End:
529