Home | History | Annotate | Line # | Download | only in ServiceRegistration
      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