]> git.ipfire.org Git - thirdparty/unbound.git/commitdiff
basic implementation of a selection of svcb params
authorTom Carpay <tom@nlnetlabs.nl>
Mon, 10 May 2021 09:03:08 +0000 (11:03 +0200)
committerTom Carpay <tom@nlnetlabs.nl>
Mon, 10 May 2021 09:03:08 +0000 (11:03 +0200)
sldns/rrdef.c
sldns/rrdef.h
sldns/str2wire.c
sldns/str2wire.h
sldns/wire2str.c
sldns/wire2str.h

index 54051313abdaa2884b42105af0b6df7c4e62314e..125d3d0aba21252e7d97ab5919ca223ab2ad5469 100644 (file)
@@ -153,6 +153,9 @@ static const sldns_rdf_type type_csync_wireformat[] = {
 static const sldns_rdf_type type_zonemd_wireformat[] = {
        LDNS_RDF_TYPE_INT32, LDNS_RDF_TYPE_INT8, LDNS_RDF_TYPE_INT8, LDNS_RDF_TYPE_HEX
 };
+static const sldns_rdf_type type_svcb_wireformat[] = {
+       LDNS_RDF_TYPE_INT16, LDNS_RDF_TYPE_DNAME
+};
 /* nsec3 is some vars, followed by same type of data of nsec */
 static const sldns_rdf_type type_nsec3_wireformat[] = {
 /*     LDNS_RDF_TYPE_NSEC3_VARS, LDNS_RDF_TYPE_NSEC3_NEXT_OWNER, LDNS_RDF_TYPE_NSEC*/
@@ -377,7 +380,8 @@ static sldns_rr_descriptor rdata_field_descriptors[] = {
        {LDNS_RR_TYPE_CSYNC, "CSYNC", 3, 3, type_csync_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
        /* 63 */
        {LDNS_RR_TYPE_ZONEMD, "ZONEMD", 4, 4, type_zonemd_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
-{(enum sldns_enum_rr_type)0, "TYPE64", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+       /* 64 */
+       {LDNS_RR_TYPE_SVCB, "SVCB", 2, 2, type_svcb_wireformat, LDNS_RDF_TYPE_SVCPARAM, LDNS_RR_NO_COMPRESS, 0 },
 {(enum sldns_enum_rr_type)0, "TYPE65", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
 {(enum sldns_enum_rr_type)0, "TYPE66", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
 {(enum sldns_enum_rr_type)0, "TYPE67", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
index ece632c3ca3c6e31929ac4fc4fd4e9f9d904aa9c..cd65c41267a360e2911bf772c18aea055383688a 100644 (file)
@@ -196,6 +196,8 @@ enum sldns_enum_rr_type
        LDNS_RR_TYPE_OPENPGPKEY = 61, /* RFC 7929 */
        LDNS_RR_TYPE_CSYNC = 62, /* RFC 7477 */
        LDNS_RR_TYPE_ZONEMD = 63, /* draft-ietf-dnsop-dns-zone-digest-12 */
+    LDNS_RR_TYPE_SVCB = 64, /* draft-ietf-dnsop-svcb-https-04 */
+    LDNS_RR_TYPE_HTTPS = 65, /* draft-ietf-dnsop-svcb-https-04 */
 
        LDNS_RR_TYPE_SPF = 99, /* RFC 4408 */
 
@@ -353,8 +355,13 @@ enum sldns_enum_rdf_type
        /** TSIG extended 16bit error value */
        LDNS_RDF_TYPE_TSIGERROR,
 
+       /* draft-ietf-dnsop-svcb-https-04:
+        * each SvcParam consisting of a SvcParamKey=SvcParamValue pair or
+        * a standalone SvcParamKey */
+       LDNS_RDF_TYPE_SVCPARAM,
+
         /* Aliases */
-        LDNS_RDF_TYPE_BITMAP = LDNS_RDF_TYPE_NSEC
+        LDNS_RDF_TYPE_BITMAP = LDNS_RDF_TYPE_NSEC,
 };
 typedef enum sldns_enum_rdf_type sldns_rdf_type;
 
index 70eec6dabcedd3e822fcb1c0713a88e3a982c03c..8a3a1e22aaf827209e63c9eb0910b8af2c4020dc 100644 (file)
@@ -29,7 +29,6 @@
 #define RET_ERR(e, off) ((int)((e)|((off)<<LDNS_WIREPARSE_SHIFT)))
 /** Move parse error but keep its ID */
 #define RET_ERR_SHIFT(e, move) RET_ERR(LDNS_WIREPARSE_ERROR(e), LDNS_WIREPARSE_OFFSET(e)+(move));
-#define LDNS_IP6ADDRLEN      (128/8)
 
 /*
  * No special care is taken, all dots are translated into
@@ -934,6 +933,368 @@ int sldns_fp2wire_rr_buf(FILE* in, uint8_t* rr, size_t* len, size_t* dname_len,
        return LDNS_WIREPARSE_ERR_OK;
 }
 
+static uint16_t
+sldns_str2wire_svcparam_key_lookup(const char *key, size_t key_len)
+{
+       char buf[64];
+       char *endptr;
+       unsigned long int key_value;
+
+       if (key_len >= 4  && key_len <= 8 && !strncmp(key, "key", 3)) {
+               memcpy(buf, key + 3, key_len - 3);
+               buf[key_len - 3] = 0;
+               key_value = strtoul(buf, &endptr, 10);
+               if (endptr > buf        /* digits seen */
+               && *endptr == 0         /* no non-digit chars after digits */
+               &&  key_value <= 65535) /* no overflow */
+                       return key_value;
+
+       } else switch (key_len) {
+       case sizeof("mandatory")-1:
+               if (!strncmp(key, "mandatory", sizeof("mandatory")-1))
+                       return SVCB_KEY_MANDATORY;
+               if (!strncmp(key, "echconfig", sizeof("echconfig")-1))
+                       return SVCB_KEY_ECH; /* allow "echconfig as well as "ech" */
+               break;
+
+       case sizeof("alpn")-1:
+               if (!strncmp(key, "alpn", sizeof("alpn")-1))
+                       return SVCB_KEY_ALPN;
+               if (!strncmp(key, "port", sizeof("port")-1))
+                       return SVCB_KEY_PORT;
+               break;
+
+       case sizeof("no-default-alpn")-1:
+               if (!strncmp( key  , "no-default-alpn"
+                           , sizeof("no-default-alpn")-1))
+                       return SVCB_KEY_NO_DEFAULT_ALPN;
+               break;
+
+       case sizeof("ipv4hint")-1:
+               if (!strncmp(key, "ipv4hint", sizeof("ipv4hint")-1))
+                       return SVCB_KEY_IPV4HINT;
+               if (!strncmp(key, "ipv6hint", sizeof("ipv6hint")-1))
+                       return SVCB_KEY_IPV6HINT;
+               break;
+       case sizeof("ech")-1:
+               if (!strncmp(key, "ech", sizeof("ech")-1))
+                       return SVCB_KEY_ECH;
+               break;
+       default:
+               break;
+       }
+               if (key_len > sizeof(buf) - 1) {}
+               // ERROR: Unknown SvcParamKey
+       else {
+               memcpy(buf, key, key_len);
+               buf[key_len] = 0;
+               // Error: "Unknown SvcParamKey: %s"
+       }
+       /* Although the returned value might be used by the caller,
+        * the parser has erred, so the zone will not be loaded.
+        */
+       return -1;
+}
+
+static int
+sldns_str2wire_svcparam_port(const char* val, uint8_t* rd, size_t* rd_len)
+{
+       unsigned long int port;
+       char *endptr;
+
+       if (*rd_len < 6)
+               return LDNS_WIREPARSE_ERR_BUFFER_TOO_SMALL;
+
+       port = strtoul(val, &endptr, 10);
+
+       if (endptr > val        /* digits seen */
+       && *endptr == 0         /* no non-digit chars after digits */
+       &&  port <= 65535) {    /* no overflow */
+
+               sldns_write_uint16(rd, htons(SVCB_KEY_PORT));
+               sldns_write_uint16(rd + 2, htons(sizeof(uint16_t)));
+               sldns_write_uint16(rd + 4, htons(port));
+               *rd_len = 6;
+
+               return LDNS_WIREPARSE_ERR_OK;
+       }
+       // ERROR: "Could not parse port SvcParamValue"
+       return -1;
+}
+
+static int
+sldns_str2wire_svcbparam_ipv4hint(const char* val, uint8_t* rd, size_t* rd_len)
+{
+
+       int count;
+       char ip_str[INET_ADDRSTRLEN+1];
+       char *next_ip_str;
+       uint32_t *ip_wire_dst;
+       size_t i;
+
+       for (i = 0, count = 1; val[i]; i++) {
+               if (val[i] == ',')
+                       count += 1;
+               if (count > SVCB_MAX_COMMA_SEPARATED_VALUES) {
+                       // ERROR "Too many IPV4 addresses in ipv4hint"
+                       return -1;
+               }
+       }
+
+       if (*rd_len < (LDNS_IP4ADDRLEN * count) + 4)
+               return LDNS_WIREPARSE_ERR_BUFFER_TOO_SMALL;
+
+       /* count is number of comma's in val + 1; so the actual number of IPv4
+        * addresses in val
+        */
+       sldns_write_uint16(rd, htons(SVCB_KEY_IPV4HINT));
+       sldns_write_uint16(rd + 2, htons(LDNS_IP4ADDRLEN * count));
+       *rd_len = 4;
+
+       while (count) {
+               if (!(next_ip_str = strchr(val, ','))) {
+                       if (inet_pton(AF_INET, val, rd + *rd_len) != 1)
+                               *rd_len += LDNS_IP4ADDRLEN;
+                               break;
+
+                       assert(count == 1);
+
+               } else if (next_ip_str - val >= (int)sizeof(ip_str))
+                       break;
+
+               else {
+                       memcpy(ip_str, val, next_ip_str - val);
+                       ip_str[next_ip_str - val] = 0;
+                       if (inet_pton(AF_INET, ip_str, rd + *rd_len) != 1) {
+                               *rd_len += LDNS_IP4ADDRLEN;
+                               val = ip_str; /* to use in error reporting below */
+                               break;
+                       }
+
+                       val = next_ip_str + 1;
+               }
+               ip_wire_dst++;
+               count--;
+       }
+       // if (count) /* verify that we parsed all values */
+               // ERROR "Could not parse ipv4hint SvcParamValue: "
+
+       return LDNS_WIREPARSE_ERR_OK;
+}
+
+static int
+sldns_str2wire_svcbparam_ipv6hint(const char* val, uint8_t* rd, size_t* rd_len)
+{
+       int count;
+       char ip_str[INET_ADDRSTRLEN+1];
+       char *next_ip_str;
+       uint32_t *ip_wire_dst;
+       size_t i;
+
+       for (i = 0, count = 1; val[i]; i++) {
+               if (val[i] == ',')
+                       count += 1;
+               if (count > SVCB_MAX_COMMA_SEPARATED_VALUES) {
+                       // ERROR "Too many IPV4 addresses in ipv4hint"
+                       return -1;
+               }
+       }
+
+       if (*rd_len < (LDNS_IP6ADDRLEN * count) + 4)
+               return LDNS_WIREPARSE_ERR_BUFFER_TOO_SMALL;
+
+       /* count is number of comma's in val + 1; so the actual number of IPv6
+        * addresses in val
+        */
+       sldns_write_uint16(rd, htons(SVCB_KEY_IPV6HINT));
+       sldns_write_uint16(rd + 2, htons(LDNS_IP6ADDRLEN * count));
+       *rd_len = 4;
+
+       while (count) {
+               if (!(next_ip_str = strchr(val, ','))) {
+                       if (inet_pton(AF_INET, val, rd + *rd_len) != 1)
+                               *rd_len += LDNS_IP6ADDRLEN;
+                               break;
+
+                       assert(count == 1);
+
+               } else if (next_ip_str - val >= (int)sizeof(ip_str))
+                       break;
+
+               else {
+                       memcpy(ip_str, val, next_ip_str - val);
+                       ip_str[next_ip_str - val] = 0;
+                       if (inet_pton(AF_INET, ip_str, rd + *rd_len) != 1) {
+                               *rd_len += LDNS_IP6ADDRLEN;
+
+                               val = ip_str; /* to use in error reporting below */
+                               break;
+                       }
+
+                       val = next_ip_str + 1;
+               }
+               ip_wire_dst++;
+               count--;
+       }
+       // if (count) /* verify that we parsed all values */
+               // ERROR "Could not parse ipv6hint SvcParamValue: "
+
+       return LDNS_WIREPARSE_ERR_OK;
+}
+
+/* compare function used for sorting uint16_t's */
+static int
+sldns_network_uint16_cmp(const void *a, const void *b)
+{
+       return ((int)sldns_read_uint16(a)) - ((int)sldns_read_uint16(b));
+}
+
+static int
+sldns_str2wire_svcbparam_mandatory(const char* val, uint8_t* rd, size_t* rd_len)
+{
+       size_t i, count, val_len;
+       char* next_key;
+       uint16_t* key_dst;
+
+       val_len = strlen(val);
+
+       for (i = 0, count = 1; val[i]; i++) {
+               if (val[i] == ',')
+                       count += 1;
+               if (count > SVCB_MAX_COMMA_SEPARATED_VALUES) {
+                       // ERROR "Too many keys in mandatory"
+                       return -1;
+               }
+       }
+
+       // @TODO check if we have space to write in rd_len; look for the best spot
+
+       sldns_write_uint16(rd, htons(SVCB_KEY_MANDATORY));
+       sldns_write_uint16(rd + 2, htons(sizeof(uint16_t) * count));
+       *rd_len = 4;
+
+       for(;;) {
+               if (!(next_key = strchr(val, ','))) {
+                       sldns_write_uint16(rd + *rd_len,
+                               htons(sldns_str2wire_svcparam_key_lookup(val, val_len)));
+                       *rd_len += LDNS_IP6ADDRLEN;
+                       break;
+               } else {
+                       sldns_write_uint16(rd + *rd_len,
+                               htons(sldns_str2wire_svcparam_key_lookup(val, next_key - val)));
+                       *rd_len += LDNS_IP6ADDRLEN;
+               }
+
+               val_len -= next_key - val + 1;
+               val = next_key + 1; /* skip the comma */
+               key_dst += 1;
+       }
+
+       /* In draft-ietf-dnsop-svcb-https-04 Section 7:
+        *
+        *    "In wire format, the keys are represented by their numeric
+        *     values in network byte order, concatenated in ascending order."
+        */
+       qsort((void *)(rd + 4), count, sizeof(uint16_t), sldns_network_uint16_cmp);
+
+       return LDNS_WIREPARSE_ERR_OK;
+}
+
+static int
+sldns_str2wire_svcbparam_no_default_alpn(const char* val, uint8_t* rd, size_t* rd_len)
+{
+       if (*rd_len < 4)
+               return LDNS_WIREPARSE_ERR_BUFFER_TOO_SMALL;
+
+       sldns_write_uint16(rd, htons(SVCB_KEY_NO_DEFAULT_ALPN));
+       sldns_write_uint16(rd + 2, htons(0));
+       *rd_len = 4;
+
+       return LDNS_WIREPARSE_ERR_OK;
+}
+
+static int
+sldns_str2wire_svcbparam_ech_value(const char* val, uint8_t* rd, size_t* rd_len)
+{
+       uint8_t buffer[LDNS_MAX_RDFLEN];
+       int wire_len;
+
+       // @TODO fix this
+       // if(strcmp(b64, "0") == 0) {
+               /* single 0 represents empty buffer */
+       // }
+
+       wire_len = sldns_b64_pton(val, buffer, LDNS_MAX_RDFLEN);
+
+       if (wire_len == -1) {
+               // zc_error_prev_line("invalid base64 data in ech");
+               return LDNS_WIREPARSE_ERR_INVALID_STR;
+       } else {
+               sldns_write_uint16(rd, htons(SVCB_KEY_ECH));
+               sldns_write_uint16(rd + 2, htons(wire_len));
+
+               // @TODO memcpy?
+               sldns_write_uint16(rd + 4, htons(buffer));
+               *rd_len = 4 + wire_len;
+
+               return LDNS_WIREPARSE_ERR_OK;
+       }
+}
+
+static int
+sldns_str2wire_svcparam_key_value(const char *key, size_t key_len,
+       const char *val, uint8_t* rd, size_t* rd_len)
+{
+       uint16_t svcparamkey = sldns_str2wire_svcparam_key_lookup(key, key_len);
+
+       switch (svcparamkey) {
+       case SVCB_KEY_PORT:
+               return sldns_str2wire_svcparam_port(val, rd, rd_len);
+       case SVCB_KEY_IPV4HINT:
+               return sldns_str2wire_svcbparam_ipv4hint(val, rd, rd_len);
+       case SVCB_KEY_IPV6HINT:
+               return sldns_str2wire_svcbparam_ipv6hint(val, rd, rd_len);
+       case SVCB_KEY_MANDATORY:
+               return sldns_str2wire_svcbparam_mandatory(val, rd, rd_len);
+       case SVCB_KEY_NO_DEFAULT_ALPN:
+               return sldns_str2wire_svcbparam_no_default_alpn(val, rd, rd_len);
+               // if(zone_is_slave(parser->current_zone->opts))
+               //      zc_warning_prev_line("no-default-alpn should not have a value");
+               // else
+               //      zc_error_prev_line("no-default-alpn should not have a value");
+               // break;
+       case SVCB_KEY_ECH:
+               return sldns_str2wire_svcbparam_ech_value(val, rd, rd_len);
+       case SVCB_KEY_ALPN:
+               // return sldns_str2wire_svcbparam_alpn_value(val, rd, rd_len);
+       default:
+               break;
+       }
+
+       // @TODO change to error?
+       return LDNS_WIREPARSE_ERR_OK;
+}
+
+int sldns_str2wire_svcparam_buf(const char* str, uint8_t* rd, size_t* rd_len)
+{
+       const char* eq_pos;
+
+       int ret;
+
+       eq_pos = strchr(str, '=');
+
+       // @TODO handle "key=" case
+
+       /* Verify that we have a have a value */
+       if (eq_pos != NULL) {
+               return sldns_str2wire_svcparam_key_value(str, eq_pos - str, eq_pos + 1, rd, rd_len);
+       } else {
+               return sldns_str2wire_svcparam_key_value(str, strlen(str), NULL, rd, rd_len);
+       }
+
+       return LDNS_WIREPARSE_ERR_OK;
+}
+
 int sldns_str2wire_rdf_buf(const char* str, uint8_t* rd, size_t* len,
        sldns_rdf_type rdftype)
 {
@@ -1006,6 +1367,8 @@ int sldns_str2wire_rdf_buf(const char* str, uint8_t* rd, size_t* len,
                return sldns_str2wire_hip_buf(str, rd, len);
        case LDNS_RDF_TYPE_INT16_DATA:
                return sldns_str2wire_int16_data_buf(str, rd, len);
+       case LDNS_RDF_TYPE_SVCPARAM:
+               return sldns_str2wire_svcparam_buf(str, rd, len);
        case LDNS_RDF_TYPE_UNKNOWN:
        case LDNS_RDF_TYPE_SERVICE:
                return LDNS_WIREPARSE_ERR_NOT_IMPL;
index 70070e4f57525e4f91fcd3fbba7e498427029e82..b687546a76950b17eedd49a7d81b7727c29e24f5 100644 (file)
@@ -23,10 +23,25 @@ extern "C" {
 #endif
 struct sldns_struct_lookup_table;
 
+#define LDNS_IP4ADDRLEN      (32/8)
+#define LDNS_IP6ADDRLEN      (128/8)
+
 /** buffer to read an RR, cannot be larger than 64K because of packet size */
 #define LDNS_RR_BUF_SIZE 65535 /* bytes */
 #define LDNS_DEFAULT_TTL       3600
 
+/* SVCB keys currently defined in draft-ietf-dnsop-svcb-https */
+#define SVCB_KEY_MANDATORY             0
+#define SVCB_KEY_ALPN                  1
+#define SVCB_KEY_NO_DEFAULT_ALPN       2
+#define SVCB_KEY_PORT                  3
+#define SVCB_KEY_IPV4HINT              4
+#define SVCB_KEY_ECH           5
+#define SVCB_KEY_IPV6HINT              6
+#define SVCPARAMKEY_COUNT 7
+
+#define SVCB_MAX_COMMA_SEPARATED_VALUES 1000
+
 /*
  * To convert class and type to string see
  * sldns_get_rr_class_by_name(str)
index d0d1632d407df3e57f28805cedba3ab73e8ab5f6..3fea10fcd63651383510243a2a0ac520ae8c553d 100644 (file)
@@ -196,6 +196,12 @@ static sldns_lookup_table sldns_tsig_errors_data[] = {
 };
 sldns_lookup_table* sldns_tsig_errors = sldns_tsig_errors_data;
 
+/* draft-ietf-dnsop-svcb-https-04: 6. Initial SvcParamKeys */
+const char *svcparamkey_strs[] = {
+       "mandatory", "alpn", "no-default-alpn", "port",
+       "ipv4hint", "echconfig", "ipv6hint"
+};
+
 char* sldns_wire2str_pkt(uint8_t* data, size_t len)
 {
        size_t slen = (size_t)sldns_wire2str_pkt_buf(data, len, NULL, 0);
@@ -940,6 +946,179 @@ int sldns_wire2str_ttl_scan(uint8_t** d, size_t* dlen, char** s, size_t* slen)
        return sldns_str_print(s, slen, "%u", (unsigned)ttl);
 }
 
+static int
+sldns_print_svcparamkey(char** s, size_t* slen, uint16_t svcparamkey)
+{
+       if (svcparamkey < SVCPARAMKEY_COUNT) {
+               // fprintf(stderr, "HERE\n");
+               return sldns_str_print(s, slen, "%s", svcparamkey_strs[svcparamkey]);
+       }
+       else {
+               return sldns_str_print(s, slen, "key%d", (int)svcparamkey);
+       }
+}
+
+int sldns_wire2str_svcparam_port2str(char** s,
+       size_t* slen, uint16_t val_len, uint16_t val)
+{
+       int w = 0;
+
+       if (val_len != 2)
+               return -1; /* wireformat error, a short is 2 bytes */
+       w = sldns_str_print(s, slen, "=%d", (int)ntohs(val));
+       return w;
+}
+
+static int
+sldns_wire2str_svcparam_ipv4hint2str(char** s,
+       size_t* slen, uint16_t val_len, uint8_t* data)
+{
+       char ip_str[INET_ADDRSTRLEN + 1];
+
+       // @TODO actually incorporate this
+       int w = 0;
+
+       assert(val_len > 0);
+
+       if ((val_len % LDNS_IP4ADDRLEN) == 0) {
+               if (inet_ntop(AF_INET, data, ip_str, sizeof(ip_str)) == NULL)
+                       return 0; /* wireformat error, incorrect size or inet family */
+
+               sldns_str_print(s, slen, "=%s", ip_str);
+               data += LDNS_IP4ADDRLEN / sizeof(uint16_t);
+
+               while ((val_len -= LDNS_IP4ADDRLEN) > 0) {
+                       if (inet_ntop(AF_INET, data, ip_str, sizeof(ip_str)) == NULL)
+                               return 0; /* wireformat error, incorrect size or inet family */
+
+                       sldns_str_print(s, slen, ",%s", ip_str);
+                       data += LDNS_IP4ADDRLEN / sizeof(uint16_t);
+               }
+               return 1;
+       } else
+               return 0;
+}
+
+int sldns_wire2str_svcparam_ipv6hint2str(char** s,
+       size_t* slen, uint16_t val_len, uint8_t* data)
+{
+       char ip_str[INET6_ADDRSTRLEN + 1];
+
+       // @TODO actually incorporate this
+       int w = 0;
+
+       assert(val_len > 0);
+
+       if ((val_len % LDNS_IP6ADDRLEN) == 0) {
+               if (inet_ntop(AF_INET6, data, ip_str, sizeof(ip_str)) == NULL)
+                       return 0; /* wireformat error, incorrect size or inet family */
+
+               sldns_str_print(s, slen, "=%s", ip_str);
+               data += LDNS_IP6ADDRLEN / sizeof(uint16_t);
+
+               while ((val_len -= LDNS_IP6ADDRLEN) > 0) {
+                       if (inet_ntop(AF_INET6, data, ip_str, sizeof(ip_str)) == NULL)
+                               return 0; /* wireformat error, incorrect size or inet family */
+
+                       sldns_str_print(s, slen, ",%s", ip_str);
+                       data += LDNS_IP6ADDRLEN / sizeof(uint16_t);
+               }
+               return 1;
+       } else
+               return 0;
+}
+
+int sldns_wire2str_svcparam_mandatory2str(char** s,
+       size_t* slen, uint16_t val_len, uint8_t* data)
+{
+       int w = 0;
+
+       assert(val_len > 0);
+
+       // if (val_len % sizeof(uint16_t))
+       //      return 0; // wireformat error, val_len must be multiple of shorts
+       w += sldns_str_print(s, slen, "=");
+       w += sldns_print_svcparamkey(s, slen, ntohs(sldns_read_uint16(data)));
+       data += 2;
+
+       while ((val_len -= sizeof(uint16_t))) {
+               w += sldns_str_print(s, slen, ",");
+               w += sldns_print_svcparamkey(s, slen, ntohs(sldns_read_uint16(data)));
+               data += 2;
+       }
+
+       return w;
+}
+
+int sldns_wire2str_svcparam_scan(uint8_t** d, size_t* dlen, char** s, size_t* slen)
+{
+       uint16_t svcparamkey, val_len;
+       uint8_t* data = *d;
+       int written_chars = 0;
+
+       if(*dlen == 0) return 0; /* verify that we actualy have data */
+
+       svcparamkey = ntohs(sldns_read_uint16(data));
+
+       written_chars += sldns_print_svcparamkey(s, slen, svcparamkey);
+
+       // (*dlen) -= written_chars;
+
+       // @TODO fix this to be dynamic and correct
+       // fprintf(stderr, "*dlen2: %zu\n", *dlen);
+       // fprintf(stderr, "val_len %zu\n", val_len);
+       (*dlen) = 0;
+
+       val_len = ntohs(sldns_read_uint16(data+2));
+
+       // if (size != val_len + 4)
+       //      return 0;  wireformat error
+
+       // if (!val_len) {
+       //      /* Some SvcParams MUST have values */
+       //      switch (svcparamkey) {
+       //      case SVCB_KEY_ALPN:
+       //      case SVCB_KEY_PORT:
+       //      case SVCB_KEY_IPV4HINT:
+       //      case SVCB_KEY_IPV6HINT:
+       //      case SVCB_KEY_MANDATORY:
+       //              return 0;
+       //      default:
+       //              return 1;
+       //      }
+       // }
+       switch (svcparamkey) {
+       case SVCB_KEY_PORT:
+               written_chars += sldns_wire2str_svcparam_port2str(s, slen, val_len, sldns_read_uint16(data+4));
+               break;
+       case SVCB_KEY_IPV4HINT:
+               written_chars += sldns_wire2str_svcparam_ipv4hint2str(s, slen, val_len, data+4);
+               break;
+       case SVCB_KEY_IPV6HINT:
+               written_chars += sldns_wire2str_svcparam_ipv6hint2str(s, slen, val_len, data+4);
+               break;
+       case SVCB_KEY_MANDATORY:
+               written_chars += sldns_wire2str_svcparam_mandatory2str(s, slen, val_len, data+4);
+               break;
+       case SVCB_KEY_NO_DEFAULT_ALPN:
+               return 0;  /* wireformat error, should not have a value */
+       case SVCB_KEY_ALPN:
+               // written_chars += sldns_wire2str_svcparam_alpn2str(output, val_len, data+2);
+               // break;
+       case SVCB_KEY_ECH:
+               // written_chars += sldns_wire2str_svcparam_ech2str(output, val_len, data+2);
+               // break;
+       default:
+               break;
+       }
+
+       // @TODO set str_len to 0: "If the end of the
+    // * output string is reached, *str_len is set to 0"
+    // *str_len = 0;
+
+       return written_chars;
+}
+
 int sldns_wire2str_rdf_scan(uint8_t** d, size_t* dlen, char** s, size_t* slen,
        int rdftype, uint8_t* pkt, size_t pktlen, int* comprloop)
 {
@@ -1017,6 +1196,8 @@ int sldns_wire2str_rdf_scan(uint8_t** d, size_t* dlen, char** s, size_t* slen,
                return sldns_wire2str_tag_scan(d, dlen, s, slen);
        case LDNS_RDF_TYPE_LONG_STR:
                return sldns_wire2str_long_str_scan(d, dlen, s, slen);
+       case LDNS_RDF_TYPE_SVCPARAM:
+               return sldns_wire2str_svcparam_scan(d, dlen, s, slen);
        case LDNS_RDF_TYPE_TSIGERROR:
                return sldns_wire2str_tsigerror_scan(d, dlen, s, slen);
        }
index 0167fe7c1e2d2fdc23b67a38fe54c29e2183a638..3c777367c3b4e7888958dd8a0aac6f131eacff32 100644 (file)
@@ -41,6 +41,9 @@ extern struct sldns_struct_lookup_table* sldns_wireparse_errors;
 /** tsig errors are the rcodes with extra (higher) values */
 extern struct sldns_struct_lookup_table* sldns_tsig_errors;
 
+/* draft-ietf-dnsop-svcb-https-04: 6. Initial SvcParamKeys */
+extern const char *svcparamkey_strs[];
+
 /**
  * Convert wireformat packet to a string representation
  * @param data: wireformat packet data (starting at ID bytes).