Home | History | Annotate | Line # | Download | only in ServiceRegistration
      1 /* wireutils.c
      2  *
      3  * Copyright (c) 2019-2021 Apple 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  *     https://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 #ifndef STANDALONE
     48 #define STANDALONE
     49 #endif
     50 #include "../mDNSCore/DNSCommon.c"
     51 
     52 void dns_name_free(dns_label_t *name)
     53 {
     54     dns_label_t *next;
     55     if (name == NULL) {
     56         return;
     57     }
     58     next = name->next;
     59     free(name);
     60     if (next != NULL) {
     61         return dns_name_free(next);
     62     }
     63 }
     64 
     65 dns_name_t *
     66 dns_name_copy(dns_name_t *original)
     67 {
     68     dns_name_t *ret = NULL, **cur = &ret;
     69     dns_name_t *next;
     70 
     71     for (next = original; next; next = next->next) {
     72         *cur = calloc(1, 1 + next->len + (sizeof (dns_name_t)) - DNS_MAX_LABEL_SIZE);
     73         if (*cur == NULL) {
     74             if (ret != NULL) {
     75                 dns_name_free(ret);
     76             }
     77             return NULL;
     78         }
     79         if (next->len) {
     80             memcpy((*cur)->data, next->data, next->len + 1);
     81         }
     82         (*cur)->len = next->len;
     83         cur = &((*cur)->next);
     84     }
     85     return ret;
     86 }
     87 
     88 // Needed for TSIG (RFC2845).
     89 void
     90 dns_u48_to_wire_(dns_towire_state_t *NONNULL txn,
     91                  uint64_t val, int line)
     92 {
     93     if (!txn->error) {
     94         if (txn->p + 6 >= txn->lim) {
     95             txn->error = ENOBUFS;
     96             txn->truncated = true;
     97             txn->line = line;
     98             return;
     99         }
    100         *txn->p++ = (val >> 40) & 0xff;
    101         *txn->p++ = (val >> 32) & 0xff;
    102         *txn->p++ = (val >> 24) & 0xff;
    103         *txn->p++ = (val >> 16) & 0xff;
    104         *txn->p++ = (val >> 8) & 0xff;
    105         *txn->p++ = val & 0xff;
    106     }
    107 }
    108 
    109 void
    110 dns_concatenate_name_to_wire_(dns_towire_state_t *towire, dns_name_t *labels_prefix, const char *prefix,
    111                               const char *suffix, int line)
    112 {
    113     dns_wire_t namebuf;
    114     dns_towire_state_t namewire;
    115     mDNSu8 *ret;
    116     namebuf.data[0] = 0;
    117 
    118     // Don't do all this work if we're already past an error.
    119     if (towire->error) {
    120         return;
    121     }
    122     memset(&namewire, 0, sizeof namewire);
    123     namewire.message = &namebuf;
    124     namewire.lim = &namebuf.data[DNS_DATA_SIZE];
    125     namewire.p = namebuf.data;
    126     if (prefix != NULL) {
    127         dns_name_to_wire(NULL, &namewire, prefix);
    128     } else if (labels_prefix != NULL) {
    129         size_t bytes_written;
    130 
    131         if (!namewire.error) {
    132             bytes_written = (size_t)(namewire.lim - namewire.p);
    133             if (bytes_written > INT16_MAX) {
    134                 towire->error = true;
    135                 towire->line = __LINE__;
    136                 return;
    137             }
    138             bytes_written = dns_name_to_wire_canonical(namewire.p, bytes_written, labels_prefix);
    139             // This can never occur with a valid name.
    140             if (bytes_written == 0) {
    141                 namewire.truncated = true;
    142             } else {
    143                 namewire.p += bytes_written;
    144             }
    145         }
    146     }
    147     if (suffix != NULL) {
    148         dns_full_name_to_wire(NULL, &namewire, suffix);
    149     }
    150     if (namewire.error) {
    151         towire->truncated = namewire.truncated;
    152         towire->error = namewire.error;
    153         towire->line = line;
    154     }
    155 
    156     ret = putDomainNameAsLabels((DNSMessage *)towire->message, towire->p, towire->lim, (domainname *)namebuf.data);
    157     if (ret == NULL) {
    158         towire->error = ENOBUFS;
    159         towire->truncated = true;
    160         towire->line = line;
    161         return;
    162     }
    163 
    164     // Shouldn't happen
    165     if (ret > towire->lim) {
    166         towire->error = ENOBUFS;
    167         towire->truncated = true;
    168         towire->line = line;
    169     } else {
    170         towire->p = ret;
    171     }
    172 }
    173 
    174 // Convert a dns_name_t to presentation format.   Stop conversion at the specified limit.
    175 // A trailing dot is only written if a null label is present.
    176 const char *NONNULL
    177 dns_name_print_to_limit(dns_name_t *NONNULL name, dns_name_t *NULLABLE limit, char *buf, size_t bufmax)
    178 {
    179     dns_label_t *lp;
    180     size_t ix = 0;
    181     size_t i;
    182 
    183     // Copy the labels in one at a time, putting a dot between each one; if there isn't room
    184     // in the buffer (shouldn't be the case), copy as much as will fit, leaving room for a NUL
    185     // termination.
    186     for (lp = name; lp != limit && lp != NULL; lp = lp->next) {
    187         if (ix != 0) {
    188             if (ix + 2 >= bufmax) {
    189                 break;
    190             }
    191             buf[ix++] = '.';
    192         }
    193         for (i = 0; i < lp->len; i++) {
    194             if (isascii(lp->data[i]) && (lp->data[i] == ' ' || isprint(lp->data[i]))) {
    195                 if (ix + 2 >= bufmax) {
    196                     break;
    197                 }
    198                 buf[ix++] = lp->data[i];
    199             } else {
    200                 if (ix + 5 >= bufmax) {
    201                     break;
    202                 }
    203                 uint8_t unsigned_char = lp->data[i];
    204                 buf[ix++] = '\\';
    205                 buf[ix++] = '0' + (unsigned_char / 100);
    206                 buf[ix++] = '0' + (unsigned_char /  10) % 10;
    207                 buf[ix++] = '0' + unsigned_char         % 10;
    208             }
    209         }
    210         if (i != lp->len) {
    211             break;
    212         }
    213     }
    214     buf[ix++] = 0;
    215     return buf;
    216 }
    217 
    218 const char *NONNULL
    219 dns_name_print(dns_name_t *NONNULL name, char *buf, size_t bufmax)
    220 {
    221     return dns_name_print_to_limit(name, NULL, buf, bufmax);
    222 }
    223 
    224 bool
    225 dns_labels_equal(const char *label1, const char *label2, size_t len)
    226 {
    227     unsigned i;
    228     for (i = 0; i < len; i++) {
    229         if (isascii(label1[i]) && isascii(label2[i])) {
    230             if (tolower(label1[i]) != tolower(label2[i])) {
    231                 return false;
    232             }
    233         }
    234         else {
    235             if (label1[i] != label2[i]) {
    236                 return false;
    237             }
    238         }
    239     }
    240     return true;
    241 }
    242 
    243 bool
    244 dns_names_equal(dns_label_t *NONNULL name1, dns_label_t *NONNULL name2)
    245 {
    246     if (name1->len != name2->len) {
    247         return false;
    248     }
    249     if (name1->len != 0 && !dns_labels_equal(name1->data, name2->data, name1->len) != 0) {
    250         return false;
    251     }
    252     if (name1->next != NULL && name2->next != NULL) {
    253         return dns_names_equal(name1->next, name2->next);
    254     }
    255     if (name1->next == NULL && name2->next == NULL) {
    256         return true;
    257     }
    258     return false;
    259 }
    260 
    261 // Note that "foo.arpa" is not the same as "foo.arpa."
    262 bool
    263 dns_names_equal_text(dns_label_t *NONNULL name1, const char *NONNULL name2)
    264 {
    265     const char *ndot;
    266     const char *s, *t;
    267     int tlen = 0;
    268     ndot = strchr(name2, '.');
    269     if (ndot == NULL) {
    270         ndot = name2 + strlen(name2);
    271     }
    272     for (s = name2; s < ndot; s++) {
    273         if (*s == '\\') {
    274             if (s + 4 <= ndot) {
    275                 tlen++;
    276                 s += 3;
    277             } else {
    278                 return false;  // An invalid name can't be equal to anything.
    279             }
    280         } else {
    281             tlen++;
    282         }
    283     }
    284     if (name1->len != tlen) {
    285         return false;
    286     }
    287     if (name1->len != 0) {
    288         t = name1->data;
    289         for (s = name2; s < ndot; s++, t++) {
    290             if (*s == '\\') { // already bounds checked
    291                 int v0 = s[1] - '0';
    292                 int v1 = s[2] - '0';
    293                 int v2 = s[3] - '0';
    294                 int val = v0 * 100 + v1 * 10 + v2;
    295                 if (val > 255) {
    296                     return false;
    297                 } else if (isascii(*s) && isascii(*t)) {
    298                     if (tolower(*s) != tolower(*t)) {
    299                         return false;
    300                     }
    301                 } else if (val != *t) {
    302                     return false;
    303                 }
    304                 s += 3;
    305             } else {
    306                 if (*s != *t) {
    307                     return false;
    308                 }
    309             }
    310         }
    311     }
    312     if (name1->next != NULL && *ndot == '.') {
    313         return dns_names_equal_text(name1->next, ndot + 1);
    314     }
    315     if (name1->next == NULL && *ndot == 0) {
    316         return true;
    317     }
    318     return false;
    319 }
    320 
    321 // Find the length of a name in uncompressed wire format.
    322 static size_t
    323 dns_name_wire_length_in(dns_label_t *NONNULL name, size_t ret)
    324 {
    325     // Root label.
    326     if (name == NULL)
    327         return ret;
    328     return dns_name_wire_length_in(name->next, ret + name->len + 1);
    329 }
    330 
    331 size_t
    332 dns_name_wire_length(dns_label_t *NONNULL name)
    333 {
    334     return dns_name_wire_length_in(name, 0);
    335 }
    336 
    337 // Copy a name we've parsed from a message out in canonical wire format so that we can
    338 // use it to verify a signature.   As above, not actually needed for copying to a message
    339 // we're going to send, since in that case we want to try to compress.
    340 static size_t
    341 dns_name_to_wire_canonical_in(uint8_t *NONNULL buf, size_t max, size_t ret, dns_label_t *NONNULL name)
    342 {
    343     if (name == NULL) {
    344         return ret;
    345     }
    346     if (max < name->len + 1) {
    347         return 0;
    348     }
    349     *buf = name->len;
    350     memcpy(buf + 1, name->data, name->len);
    351     return dns_name_to_wire_canonical_in(buf + name->len + 1,
    352                                          max - name->len - 1, ret + name->len + 1, name->next);
    353 }
    354 
    355 size_t
    356 dns_name_to_wire_canonical(uint8_t *NONNULL buf, size_t max, dns_label_t *NONNULL name)
    357 {
    358     return dns_name_to_wire_canonical_in(buf, max, 0, name);
    359 }
    360 
    361 // Parse a NUL-terminated text string into a sequence of labels.
    362 dns_name_t *
    363 dns_pres_name_parse(const char *pname)
    364 {
    365     const char *dot, *s, *label;
    366     dns_label_t *next, *ret, **prev = &ret;
    367     size_t len;
    368     char *t;
    369     char buf[DNS_MAX_LABEL_SIZE];
    370     ret = NULL;
    371 
    372     label = pname;
    373     dot = strchr(label, '.');
    374     while (true) {
    375         if (dot == NULL) {
    376             dot = label + strlen(label);
    377         }
    378         len = (size_t)(dot - label);
    379         if (len > 0) {
    380             t = buf;
    381             for (s = label; s < dot; s++) {
    382                 if (*s == '\\') { // already bounds checked
    383                     int v0 = s[1] - '0';
    384                     int v1 = s[2] - '0';
    385                     int v2 = s[3] - '0';
    386                     int val = v0 * 100 + v1 * 10 + v2;
    387                     if (val > 255) {
    388                         goto fail;
    389                     }
    390                     s += 3;
    391                     *t++ = (char)val;
    392                 } else {
    393                     *t++ = *s;
    394                 }
    395                 if ((size_t)(t - buf) >= sizeof(buf)) {
    396                     goto fail;
    397                 }
    398             }
    399             len = (size_t)(t - buf);
    400         }
    401         next = calloc(1, len + 1 + (sizeof *next) - DNS_MAX_LABEL_SIZE);
    402         if (next == NULL) {
    403             goto fail;
    404         }
    405         *prev = next;
    406         prev = &next->next;
    407         next->len = (uint8_t)len;
    408         if (next->len > 0) {
    409             memcpy(next->data, buf, next->len);
    410         }
    411         next->data[next->len] = 0;
    412         if (dot[0] == '.' && len > 0) {
    413             dot = dot + 1;
    414         }
    415         if (*dot == '\0') {
    416             if (len > 0) {
    417                 label = dot;
    418             } else {
    419                 break;
    420             }
    421         } else {
    422             label = dot;
    423             dot = strchr(label, '.');
    424         }
    425     }
    426     return ret;
    427 
    428 fail:
    429     if (ret) {
    430         dns_name_free(ret);
    431     }
    432     return NULL;
    433 }
    434 
    435 // See if name is a subdomain of domain.   If so, return a pointer to the label in name
    436 // where the match to domain begins.
    437 dns_name_t *
    438 dns_name_subdomain_of(dns_name_t *name, dns_name_t *domain)
    439 {
    440     int dnum = 0, nnum = 0;
    441     dns_name_t *np, *dp;
    442 
    443     for (dp = domain; dp; dp = dp->next) {
    444         dnum++;
    445     }
    446     for (np = name; np; np = np->next) {
    447         nnum++;
    448     }
    449     if (nnum < dnum) {
    450         return NULL;
    451     }
    452     for (np = name; np; np = np->next) {
    453         if (nnum-- == dnum) {
    454             break;
    455         }
    456     }
    457     if (np != NULL && dns_names_equal(np, domain)) {
    458         return np;
    459     }
    460     return NULL;
    461 }
    462 
    463 const char *
    464 dns_rcode_name(int rcode)
    465 {
    466     switch(rcode) {
    467     case dns_rcode_noerror:
    468         return "No Error";
    469     case dns_rcode_formerr:
    470         return "Format Error";
    471     case dns_rcode_servfail:
    472         return "Server Failure";
    473     case dns_rcode_nxdomain:
    474         return "Non-Existent Domain";
    475     case dns_rcode_notimp:
    476         return "Not Implemented";
    477     case dns_rcode_refused:
    478         return "Query Refused";
    479     case dns_rcode_yxdomain:
    480         return "Name Exists when it should not";
    481     case dns_rcode_yxrrset:
    482         return "RR Set Exists when it should not";
    483     case dns_rcode_nxrrset:
    484         return "RR Set that should exist does not";
    485     case dns_rcode_notauth:
    486         return "Not Authorized";
    487     case dns_rcode_notzone:
    488         return "Name not contained in zone";
    489     case dns_rcode_dsotypeni:
    490         return "DSO-Type Not Implemented";
    491     case dns_rcode_badvers:
    492         return "TSIG Signature Failure";
    493     case dns_rcode_badkey:
    494         return "Key not recognized";
    495     case dns_rcode_badtime:
    496         return "Signature out of time window";
    497     case dns_rcode_badmode:
    498         return "Bad TKEY Mode";
    499     case dns_rcode_badname:
    500         return "Duplicate key name";
    501     case dns_rcode_badalg:
    502         return "Algorithm not supported";
    503     case dns_rcode_badtrunc:
    504         return "Bad Truncation";
    505     case dns_rcode_badcookie:
    506         return "Bad/missing Server Cookie";
    507     default:
    508         return "Unknown rcode.";
    509     }
    510 }
    511 
    512 bool
    513 dns_keys_rdata_equal(dns_rr_t *key1, dns_rr_t *key2)
    514 {
    515     if ((key1->type == dns_rrtype_key && key2->type == dns_rrtype_key) &&
    516         key1->data.key.flags == key2->data.key.flags &&
    517         key1->data.key.protocol == key2->data.key.protocol &&
    518         key1->data.key.algorithm == key2->data.key.algorithm &&
    519         key1->data.key.len == key2->data.key.len &&
    520         !memcmp(key1->data.key.key, key2->data.key.key, key1->data.key.len))
    521     {
    522         return true;
    523     }
    524     return false;
    525 }
    526 
    527 void
    528 dns_txt_data_print(char *txt_buf, size_t buf_size, uint16_t txt_length, uint8_t *txt_data)
    529 {
    530     uint16_t index = 0;
    531     char *sp = txt_buf;
    532     char *lim = sp + buf_size;
    533     const char *continuation_string = "";
    534     const char *commasp = ", ";
    535     size_t continuation_length = 0;
    536     txt_buf[0] = 0;
    537     while (sp < lim && index < txt_length) {
    538         uint16_t hunk_len = txt_data[index];
    539         uint16_t next_index = index + hunk_len + 1;
    540 
    541         // Overflow or past the end of data?
    542         if (next_index > txt_length || next_index < index) {
    543             break;
    544         }
    545 
    546         // Out of space, shouldn't be possible.
    547         if (sp + continuation_length + 1 >= lim) {
    548             break;
    549         }
    550         if (hunk_len != 0) {
    551             if (continuation_length != 0 && sp + continuation_length + 1 < lim) {
    552                 memcpy(sp, continuation_string, continuation_length);
    553                 sp += continuation_length;
    554                 *sp = 0;
    555             }
    556             continuation_string = commasp;
    557             continuation_length = 2;
    558 
    559             for (int i = index + 1; i < index + 1 + hunk_len; i++) {
    560                 if (isascii(txt_data[i]) && isprint(txt_data[i])) {
    561                     if (sp + 1 < lim) {
    562                         *sp++ = txt_data[i];
    563                         *sp = 0;
    564                     }
    565                 } else {
    566                     if (sp + 5 < lim) {
    567                         size_t ret = snprintf(sp, 5, "\%o", txt_data[i]);
    568                         sp += ret; // Note that this might push sp past lim, which is fine because we'll then exit the loops.
    569                     }
    570                 }
    571             }
    572         }
    573         index = next_index;
    574     }
    575 }
    576 
    577 bool
    578 dns_rrs_equal(dns_rr_t *a, dns_rr_t *b, bool rdata_present)
    579 {
    580     // Obvious stuff first...  We do not compare TTL.
    581     if (a->type != b->type || a->qclass != b->qclass) {
    582         return false;
    583     }
    584     if (!dns_names_equal(a->name, b->name)) {
    585         return false;
    586     }
    587     if (!rdata_present) {
    588         return true;
    589     }
    590 
    591     switch(a->type) {
    592         // There's no reason to compare invalid RRs, but if we do, they are all equally invalid.
    593     case dns_invalid_rr:
    594         return true;
    595 
    596         // Anything we don't have a specific format for we store as binary data.
    597     default:
    598         if (a->data.unparsed.len == b->data.unparsed.len) {
    599             return memcmp(a->data.unparsed.data, b->data.unparsed.data, a->data.unparsed.len) == 0;
    600         }
    601         break;
    602 
    603         // All have a single name as the data
    604     case dns_rrtype_ptr:
    605     case dns_rrtype_ns:
    606     case dns_rrtype_cname:
    607         return dns_names_equal(a->data.ptr.name, b->data.ptr.name);
    608 
    609     case dns_rrtype_srv:
    610         if (a->data.srv.priority == b->data.srv.priority &&
    611             a->data.srv.weight == b->data.srv.weight && a->data.srv.port == b->data.srv.port)
    612         {
    613             return dns_names_equal(a->data.srv.name, b->data.srv.name);
    614         }
    615         break;
    616 
    617     case dns_rrtype_a:
    618         return a->data.a.s_addr == b->data.a.s_addr;
    619 
    620     case dns_rrtype_aaaa:
    621         return in6addr_compare(&a->data.aaaa, &b->data.aaaa) == 0;
    622 
    623         // We could compare signatures, but it doesn't really make sense.
    624     case dns_rrtype_sig:
    625         break;
    626 
    627     case dns_rrtype_key:
    628         return dns_keys_rdata_equal(a, b);
    629 
    630     case dns_rrtype_txt:
    631         if (a->data.txt.len == b->data.txt.len) {
    632             return memcmp(a->data.txt.data, b->data.txt.data, a->data.txt.len) == 0;
    633         }
    634     }
    635     return false;
    636 }
    637 
    638 bool
    639 dns_rr_to_wire(dns_towire_state_t *towire, dns_rr_t *rr, bool question)
    640 {
    641     uint8_t *revert = towire->p;
    642 
    643     if (towire->truncated) {
    644         return false;
    645     }
    646 
    647     // Copy out the invariants.
    648     dns_concatenate_name_to_wire(towire, rr->name, NULL, NULL);
    649     dns_u16_to_wire(towire, rr->type);
    650     dns_u16_to_wire(towire, rr->qclass);
    651 
    652     // Questions don't have RDATA.
    653     if (!question) {
    654         dns_ttl_to_wire(towire, rr->ttl);
    655         dns_rdlength_begin(towire);
    656         switch(rr->type) {
    657             // There's no reason to compare invalid RRs, but if we do, they are all equally invalid.
    658         case dns_invalid_rr:
    659             ERROR("invalid rr!");
    660             towire->error = EINVAL;
    661             break;
    662 
    663             // Anything we don't have a specific format for we store as binary data.
    664         default:
    665             dns_rdata_raw_data_to_wire(towire, rr->data.unparsed.data, rr->data.unparsed.len);
    666             break;
    667 
    668         case dns_rrtype_soa:
    669             dns_concatenate_name_to_wire(towire, rr->data.soa.mname, NULL, NULL);
    670             dns_concatenate_name_to_wire(towire, rr->data.soa.rname, NULL, NULL);
    671             dns_u32_to_wire(towire, rr->data.soa.serial);
    672             dns_u32_to_wire(towire, rr->data.soa.refresh);
    673             dns_u32_to_wire(towire, rr->data.soa.retry);
    674             dns_u32_to_wire(towire, rr->data.soa.expire);
    675             dns_u32_to_wire(towire, rr->data.soa.minimum);
    676             break;
    677 
    678             // All have a single name as the data
    679         case dns_rrtype_ptr:
    680         case dns_rrtype_ns:
    681         case dns_rrtype_cname:
    682             dns_concatenate_name_to_wire(towire, rr->data.ptr.name, NULL, NULL);
    683             break;
    684 
    685         case dns_rrtype_srv:
    686             dns_u16_to_wire(towire, rr->data.srv.priority);
    687             dns_u16_to_wire(towire, rr->data.srv.weight);
    688             dns_u16_to_wire(towire, rr->data.srv.port);
    689             dns_concatenate_name_to_wire(towire, rr->data.ptr.name, NULL, NULL);
    690             break;
    691 
    692         case dns_rrtype_a:
    693             dns_rdata_raw_data_to_wire(towire, &rr->data.a, sizeof(rr->data.a));
    694             break;
    695 
    696         case dns_rrtype_aaaa:
    697             dns_rdata_raw_data_to_wire(towire, &rr->data.aaaa, sizeof(rr->data.aaaa));
    698             break;
    699 
    700             // We could compare signatures, but it doesn't really make sense.
    701         case dns_rrtype_sig:
    702             ERROR("signature not valid here!");
    703             towire->error = EINVAL;
    704             break;
    705 
    706         case dns_rrtype_key:
    707             ERROR("key not valid here!");
    708             towire->error = EINVAL;
    709             break;
    710 
    711         case dns_rrtype_txt:
    712             dns_rdata_raw_data_to_wire(towire, rr->data.txt.data, rr->data.txt.len);
    713             break;
    714         }
    715         dns_rdlength_end(towire);
    716     }
    717 
    718     if (towire->truncated || towire->error) {
    719         towire->p = revert;
    720         return false;
    721     }
    722     return true;
    723 }
    724 
    725 void
    726 dns_message_rrs_to_wire(dns_towire_state_t *towire, dns_message_t *message)
    727 {
    728     bool question = true;
    729     for (int i = 0; i < 4; i++) {
    730         int count = 0;
    731         dns_rr_t *rrs = NULL;
    732         switch(i) {
    733         case 0: count = message->qdcount; rrs = message->questions; break;
    734         case 1: count = message->ancount; rrs = message->answers; break;
    735         case 2: count = message->nscount; rrs = message->authority; break;
    736         case 3: count = message->arcount; rrs = message->additional; break;
    737         }
    738 
    739         for (int j = 0; j < count; j++) {
    740             dns_rr_t *rr = &rrs[j];
    741             if (!dns_rr_to_wire(towire, rr, question)) {
    742                 // XXX if it's TCP we really need to embiggen here.
    743                 ERROR("no space in message for rr %d/%d %d", i, j, rr->type);
    744             }
    745         }
    746         question = false;
    747     }
    748 }
    749 
    750 // Local Variables:
    751 // mode: C
    752 // tab-width: 4
    753 // c-file-style: "bsd"
    754 // c-basic-offset: 4
    755 // fill-column: 108
    756 // indent-tabs-mode: nil
    757 // End:
    758