aboutsummaryrefslogtreecommitdiff
path: root/sldns
diff options
context:
space:
mode:
Diffstat (limited to 'sldns')
-rw-r--r--sldns/keyraw.c333
-rw-r--r--sldns/keyraw.h20
-rw-r--r--sldns/parse.c3
-rw-r--r--sldns/parse.h3
-rw-r--r--sldns/parseutil.c15
-rw-r--r--sldns/parseutil.h1
-rw-r--r--sldns/rrdef.c15
-rw-r--r--sldns/rrdef.h10
-rw-r--r--sldns/sbuffer.h2
-rw-r--r--sldns/str2wire.c683
-rw-r--r--sldns/str2wire.h31
-rw-r--r--sldns/wire2str.c279
-rw-r--r--sldns/wire2str.h12
13 files changed, 1358 insertions, 49 deletions
diff --git a/sldns/keyraw.c b/sldns/keyraw.c
index 2ec225bc5bd8..b1e60d8b52a8 100644
--- a/sldns/keyraw.c
+++ b/sldns/keyraw.c
@@ -26,11 +26,15 @@
#ifdef HAVE_OPENSSL_BN_H
#include <openssl/bn.h>
#endif
-#ifdef HAVE_OPENSSL_RSA_H
-#include <openssl/rsa.h>
-#endif
-#ifdef HAVE_OPENSSL_DSA_H
-#include <openssl/dsa.h>
+#ifdef HAVE_OPENSSL_PARAM_BUILD_H
+# include <openssl/param_build.h>
+#else
+# ifdef HAVE_OPENSSL_RSA_H
+# include <openssl/rsa.h>
+# endif
+# ifdef HAVE_OPENSSL_DSA_H
+# include <openssl/dsa.h>
+# endif
#endif
#endif /* HAVE_SSL */
@@ -191,45 +195,59 @@ void sldns_key_EVP_unload_gost(void)
}
#endif /* USE_GOST */
-DSA *
-sldns_key_buf2dsa_raw(unsigned char* key, size_t len)
+/* Retrieve params as BIGNUM from raw buffer */
+static int
+sldns_key_dsa_buf_bignum(unsigned char* key, size_t len, BIGNUM** p,
+ BIGNUM** q, BIGNUM** g, BIGNUM** y)
{
uint8_t T;
uint16_t length;
uint16_t offset;
- DSA *dsa;
- BIGNUM *Q; BIGNUM *P;
- BIGNUM *G; BIGNUM *Y;
if(len == 0)
- return NULL;
+ return 0;
T = (uint8_t)key[0];
length = (64 + T * 8);
offset = 1;
if (T > 8) {
- return NULL;
+ return 0;
}
if(len < (size_t)1 + SHA_DIGEST_LENGTH + 3*length)
- return NULL;
+ return 0;
- Q = BN_bin2bn(key+offset, SHA_DIGEST_LENGTH, NULL);
+ *q = BN_bin2bn(key+offset, SHA_DIGEST_LENGTH, NULL);
offset += SHA_DIGEST_LENGTH;
- P = BN_bin2bn(key+offset, (int)length, NULL);
+ *p = BN_bin2bn(key+offset, (int)length, NULL);
offset += length;
- G = BN_bin2bn(key+offset, (int)length, NULL);
+ *g = BN_bin2bn(key+offset, (int)length, NULL);
offset += length;
- Y = BN_bin2bn(key+offset, (int)length, NULL);
+ *y = BN_bin2bn(key+offset, (int)length, NULL);
+
+ if(!*q || !*p || !*g || !*y) {
+ BN_free(*q);
+ BN_free(*p);
+ BN_free(*g);
+ BN_free(*y);
+ return 0;
+ }
+ return 1;
+}
+#ifndef HAVE_OSSL_PARAM_BLD_NEW
+DSA *
+sldns_key_buf2dsa_raw(unsigned char* key, size_t len)
+{
+ DSA *dsa;
+ BIGNUM *Q=NULL, *P=NULL, *G=NULL, *Y=NULL;
+ if(!sldns_key_dsa_buf_bignum(key, len, &P, &Q, &G, &Y)) {
+ return NULL;
+ }
/* create the key and set its properties */
- if(!Q || !P || !G || !Y || !(dsa = DSA_new())) {
- BN_free(Q);
- BN_free(P);
- BN_free(G);
- BN_free(Y);
+ if(!(dsa = DSA_new())) {
return NULL;
}
#if OPENSSL_VERSION_NUMBER < 0x10100000 || defined(HAVE_LIBRESSL)
@@ -261,22 +279,111 @@ sldns_key_buf2dsa_raw(unsigned char* key, size_t len)
return dsa;
}
+#endif /* HAVE_OSSL_PARAM_BLD_NEW */
-RSA *
-sldns_key_buf2rsa_raw(unsigned char* key, size_t len)
+EVP_PKEY *sldns_key_dsa2pkey_raw(unsigned char* key, size_t len)
+{
+#ifdef HAVE_OSSL_PARAM_BLD_NEW
+ EVP_PKEY* evp_key = NULL;
+ EVP_PKEY_CTX* ctx;
+ BIGNUM *p=NULL, *q=NULL, *g=NULL, *y=NULL;
+ OSSL_PARAM_BLD* param_bld;
+ OSSL_PARAM* params = NULL;
+ if(!sldns_key_dsa_buf_bignum(key, len, &p, &q, &g, &y)) {
+ return NULL;
+ }
+
+ param_bld = OSSL_PARAM_BLD_new();
+ if(!param_bld) {
+ BN_free(p);
+ BN_free(q);
+ BN_free(g);
+ BN_free(y);
+ return NULL;
+ }
+ if(!OSSL_PARAM_BLD_push_BN(param_bld, "p", p) ||
+ !OSSL_PARAM_BLD_push_BN(param_bld, "g", g) ||
+ !OSSL_PARAM_BLD_push_BN(param_bld, "q", q) ||
+ !OSSL_PARAM_BLD_push_BN(param_bld, "pub", y)) {
+ OSSL_PARAM_BLD_free(param_bld);
+ BN_free(p);
+ BN_free(q);
+ BN_free(g);
+ BN_free(y);
+ return NULL;
+ }
+ params = OSSL_PARAM_BLD_to_param(param_bld);
+ OSSL_PARAM_BLD_free(param_bld);
+
+ ctx = EVP_PKEY_CTX_new_from_name(NULL, "DSA", NULL);
+ if(!ctx) {
+ OSSL_PARAM_free(params);
+ BN_free(p);
+ BN_free(q);
+ BN_free(g);
+ BN_free(y);
+ return NULL;
+ }
+ if(EVP_PKEY_fromdata_init(ctx) <= 0) {
+ EVP_PKEY_CTX_free(ctx);
+ OSSL_PARAM_free(params);
+ BN_free(p);
+ BN_free(q);
+ BN_free(g);
+ BN_free(y);
+ return NULL;
+ }
+ if(EVP_PKEY_fromdata(ctx, &evp_key, EVP_PKEY_PUBLIC_KEY, params) <= 0) {
+ EVP_PKEY_CTX_free(ctx);
+ OSSL_PARAM_free(params);
+ BN_free(p);
+ BN_free(q);
+ BN_free(g);
+ BN_free(y);
+ return NULL;
+ }
+
+ EVP_PKEY_CTX_free(ctx);
+ OSSL_PARAM_free(params);
+ BN_free(p);
+ BN_free(q);
+ BN_free(g);
+ BN_free(y);
+ return evp_key;
+#else
+ DSA* dsa;
+ EVP_PKEY* evp_key = EVP_PKEY_new();
+ if(!evp_key) {
+ return NULL;
+ }
+ dsa = sldns_key_buf2dsa_raw(key, len);
+ if(!dsa) {
+ EVP_PKEY_free(evp_key);
+ return NULL;
+ }
+ if(EVP_PKEY_assign_DSA(evp_key, dsa) == 0) {
+ DSA_free(dsa);
+ EVP_PKEY_free(evp_key);
+ return NULL;
+ }
+ return evp_key;
+#endif
+}
+
+/* Retrieve params as BIGNUM from raw buffer, n is modulus, e is exponent */
+static int
+sldns_key_rsa_buf_bignum(unsigned char* key, size_t len, BIGNUM** n,
+ BIGNUM** e)
{
uint16_t offset;
uint16_t exp;
uint16_t int16;
- RSA *rsa;
- BIGNUM *modulus;
- BIGNUM *exponent;
if (len == 0)
- return NULL;
+ return 0;
if (key[0] == 0) {
if(len < 3)
- return NULL;
+ return 0;
memmove(&int16, key+1, 2);
exp = ntohs(int16);
offset = 3;
@@ -287,23 +394,34 @@ sldns_key_buf2rsa_raw(unsigned char* key, size_t len)
/* key length at least one */
if(len < (size_t)offset + exp + 1)
- return NULL;
+ return 0;
/* Exponent */
- exponent = BN_new();
- if(!exponent) return NULL;
- (void) BN_bin2bn(key+offset, (int)exp, exponent);
+ *e = BN_new();
+ if(!*e) return 0;
+ (void) BN_bin2bn(key+offset, (int)exp, *e);
offset += exp;
/* Modulus */
- modulus = BN_new();
- if(!modulus) {
- BN_free(exponent);
- return NULL;
+ *n = BN_new();
+ if(!*n) {
+ BN_free(*e);
+ return 0;
}
/* length of the buffer must match the key length! */
- (void) BN_bin2bn(key+offset, (int)(len - offset), modulus);
+ (void) BN_bin2bn(key+offset, (int)(len - offset), *n);
+ return 1;
+}
+#ifndef HAVE_OSSL_PARAM_BLD_NEW
+RSA *
+sldns_key_buf2rsa_raw(unsigned char* key, size_t len)
+{
+ BIGNUM* modulus = NULL;
+ BIGNUM* exponent = NULL;
+ RSA *rsa;
+ if(!sldns_key_rsa_buf_bignum(key, len, &modulus, &exponent))
+ return NULL;
rsa = RSA_new();
if(!rsa) {
BN_free(exponent);
@@ -327,6 +445,88 @@ sldns_key_buf2rsa_raw(unsigned char* key, size_t len)
return rsa;
}
+#endif /* HAVE_OSSL_PARAM_BLD_NEW */
+
+EVP_PKEY* sldns_key_rsa2pkey_raw(unsigned char* key, size_t len)
+{
+#ifdef HAVE_OSSL_PARAM_BLD_NEW
+ EVP_PKEY* evp_key = NULL;
+ EVP_PKEY_CTX* ctx;
+ BIGNUM *n=NULL, *e=NULL;
+ OSSL_PARAM_BLD* param_bld;
+ OSSL_PARAM* params = NULL;
+
+ if(!sldns_key_rsa_buf_bignum(key, len, &n, &e)) {
+ return NULL;
+ }
+
+ param_bld = OSSL_PARAM_BLD_new();
+ if(!param_bld) {
+ BN_free(n);
+ BN_free(e);
+ return NULL;
+ }
+ if(!OSSL_PARAM_BLD_push_BN(param_bld, "n", n)) {
+ OSSL_PARAM_BLD_free(param_bld);
+ BN_free(n);
+ BN_free(e);
+ return NULL;
+ }
+ if(!OSSL_PARAM_BLD_push_BN(param_bld, "e", e)) {
+ OSSL_PARAM_BLD_free(param_bld);
+ BN_free(n);
+ BN_free(e);
+ return NULL;
+ }
+ params = OSSL_PARAM_BLD_to_param(param_bld);
+ OSSL_PARAM_BLD_free(param_bld);
+
+ ctx = EVP_PKEY_CTX_new_from_name(NULL, "RSA", NULL);
+ if(!ctx) {
+ OSSL_PARAM_free(params);
+ BN_free(n);
+ BN_free(e);
+ return NULL;
+ }
+ if(EVP_PKEY_fromdata_init(ctx) <= 0) {
+ EVP_PKEY_CTX_free(ctx);
+ OSSL_PARAM_free(params);
+ BN_free(n);
+ BN_free(e);
+ return NULL;
+ }
+ if(EVP_PKEY_fromdata(ctx, &evp_key, EVP_PKEY_PUBLIC_KEY, params) <= 0) {
+ EVP_PKEY_CTX_free(ctx);
+ OSSL_PARAM_free(params);
+ BN_free(n);
+ BN_free(e);
+ return NULL;
+ }
+
+ EVP_PKEY_CTX_free(ctx);
+ OSSL_PARAM_free(params);
+ BN_free(n);
+ BN_free(e);
+ return evp_key;
+#else
+ RSA* rsa;
+ EVP_PKEY *evp_key = EVP_PKEY_new();
+ if(!evp_key) {
+ return NULL;
+ }
+ rsa = sldns_key_buf2rsa_raw(key, len);
+ if(!rsa) {
+ EVP_PKEY_free(evp_key);
+ return NULL;
+ }
+ if(EVP_PKEY_assign_RSA(evp_key, rsa) == 0) {
+ RSA_free(rsa);
+ EVP_PKEY_free(evp_key);
+ return NULL;
+ }
+ return evp_key;
+#endif
+}
#ifdef USE_GOST
EVP_PKEY*
@@ -357,6 +557,62 @@ sldns_gost2pkey_raw(unsigned char* key, size_t keylen)
EVP_PKEY*
sldns_ecdsa2pkey_raw(unsigned char* key, size_t keylen, uint8_t algo)
{
+#ifdef HAVE_OSSL_PARAM_BLD_NEW
+ unsigned char buf[256+2]; /* sufficient for 2*384/8+1 */
+ EVP_PKEY *evp_key = NULL;
+ EVP_PKEY_CTX* ctx;
+ OSSL_PARAM_BLD* param_bld;
+ OSSL_PARAM* params = NULL;
+ char* group = NULL;
+
+ /* check length, which uncompressed must be 2 bignums */
+ if(algo == LDNS_ECDSAP256SHA256) {
+ if(keylen != 2*256/8) return NULL;
+ group = "prime256v1";
+ } else if(algo == LDNS_ECDSAP384SHA384) {
+ if(keylen != 2*384/8) return NULL;
+ group = "P-384";
+ } else {
+ return NULL;
+ }
+ if(keylen+1 > sizeof(buf)) { /* sanity check */
+ return NULL;
+ }
+ /* prepend the 0x04 for uncompressed format */
+ buf[0] = POINT_CONVERSION_UNCOMPRESSED;
+ memmove(buf+1, key, keylen);
+
+ param_bld = OSSL_PARAM_BLD_new();
+ if(!param_bld) {
+ return NULL;
+ }
+ if(!OSSL_PARAM_BLD_push_utf8_string(param_bld, "group", group, 0) ||
+ !OSSL_PARAM_BLD_push_octet_string(param_bld, "pub", buf, keylen+1)) {
+ OSSL_PARAM_BLD_free(param_bld);
+ return NULL;
+ }
+ params = OSSL_PARAM_BLD_to_param(param_bld);
+ OSSL_PARAM_BLD_free(param_bld);
+
+ ctx = EVP_PKEY_CTX_new_from_name(NULL, "EC", NULL);
+ if(!ctx) {
+ OSSL_PARAM_free(params);
+ return NULL;
+ }
+ if(EVP_PKEY_fromdata_init(ctx) <= 0) {
+ EVP_PKEY_CTX_free(ctx);
+ OSSL_PARAM_free(params);
+ return NULL;
+ }
+ if(EVP_PKEY_fromdata(ctx, &evp_key, EVP_PKEY_PUBLIC_KEY, params) <= 0) {
+ EVP_PKEY_CTX_free(ctx);
+ OSSL_PARAM_free(params);
+ return NULL;
+ }
+ EVP_PKEY_CTX_free(ctx);
+ OSSL_PARAM_free(params);
+ return evp_key;
+#else
unsigned char buf[256+2]; /* sufficient for 2*384/8+1 */
const unsigned char* pp = buf;
EVP_PKEY *evp_key;
@@ -393,6 +649,7 @@ sldns_ecdsa2pkey_raw(unsigned char* key, size_t keylen, uint8_t algo)
return NULL;
}
return evp_key;
+#endif /* HAVE_OSSL_PARAM_BLD_NEW */
}
#endif /* USE_ECDSA */
diff --git a/sldns/keyraw.h b/sldns/keyraw.h
index 989b02ce052e..b1f19740cd6c 100644
--- a/sldns/keyraw.h
+++ b/sldns/keyraw.h
@@ -57,6 +57,7 @@ int sldns_key_EVP_load_gost_id(void);
/** Release the engine reference held for the GOST engine. */
void sldns_key_EVP_unload_gost(void);
+#ifndef HAVE_OSSL_PARAM_BLD_NEW
/**
* Like sldns_key_buf2dsa, but uses raw buffer.
* \param[in] key the uncompressed wireformat of the key.
@@ -64,6 +65,15 @@ void sldns_key_EVP_unload_gost(void);
* \return a DSA * structure with the key material
*/
DSA *sldns_key_buf2dsa_raw(unsigned char* key, size_t len);
+#endif
+
+/**
+ * Converts a holding buffer with DSA key material to EVP PKEY in openssl.
+ * \param[in] key the uncompressed wireformat of the key.
+ * \param[in] len length of key data
+ * \return the key or NULL on error.
+ */
+EVP_PKEY *sldns_key_dsa2pkey_raw(unsigned char* key, size_t len);
/**
* Converts a holding buffer with key material to EVP PKEY in openssl.
@@ -84,6 +94,7 @@ EVP_PKEY* sldns_gost2pkey_raw(unsigned char* key, size_t keylen);
*/
EVP_PKEY* sldns_ecdsa2pkey_raw(unsigned char* key, size_t keylen, uint8_t algo);
+#ifndef HAVE_OSSL_PARAM_BLD_NEW
/**
* Like sldns_key_buf2rsa, but uses raw buffer.
* \param[in] key the uncompressed wireformat of the key.
@@ -91,6 +102,15 @@ EVP_PKEY* sldns_ecdsa2pkey_raw(unsigned char* key, size_t keylen, uint8_t algo);
* \return a RSA * structure with the key material
*/
RSA *sldns_key_buf2rsa_raw(unsigned char* key, size_t len);
+#endif
+
+/**
+ * Converts a holding buffer with RSA key material to EVP PKEY in openssl.
+ * \param[in] key the uncompressed wireformat of the key.
+ * \param[in] len length of key data
+ * \return the key or NULL on error.
+ */
+EVP_PKEY* sldns_key_rsa2pkey_raw(unsigned char* key, size_t len);
/**
* Converts a holding buffer with key material to EVP PKEY in openssl.
diff --git a/sldns/parse.c b/sldns/parse.c
index f4de8602fd69..491c8f51bf10 100644
--- a/sldns/parse.c
+++ b/sldns/parse.c
@@ -149,6 +149,9 @@ sldns_fget_token_l(FILE *f, char *token, const char *delim, size_t limit, int *l
if (c != '\0' && c != '\n') {
*t++ = c;
}
+ if (c == '\n' && line_nr) {
+ *line_nr = *line_nr + 1;
+ }
if (c == '\\' && prev_c == '\\')
prev_c = 0;
else prev_c = c;
diff --git a/sldns/parse.h b/sldns/parse.h
index 44236bfd4951..fa8f51a924c4 100644
--- a/sldns/parse.h
+++ b/sldns/parse.h
@@ -153,7 +153,6 @@ int sldns_bgetc(struct sldns_buffer *buffer);
* the position to the first character that is not in *s.
* \param[in] *buffer buffer to use
* \param[in] *s characters to skip
- * \return void
*/
void sldns_bskipcs(struct sldns_buffer *buffer, const char *s);
@@ -162,7 +161,6 @@ void sldns_bskipcs(struct sldns_buffer *buffer, const char *s);
* the position to the first character that is not in *s.
* \param[in] *fp file to use
* \param[in] *s characters to skip
- * \return void
*/
void sldns_fskipcs(FILE *fp, const char *s);
@@ -173,7 +171,6 @@ void sldns_fskipcs(FILE *fp, const char *s);
* \param[in] *fp file to use
* \param[in] *s characters to skip
* \param[in] line_nr pointer to an integer containing the current line number (for debugging purposes)
- * \return void
*/
void sldns_fskipcs_l(FILE *fp, const char *s, int *line_nr);
diff --git a/sldns/parseutil.c b/sldns/parseutil.c
index 9f289d3596c3..ba71df55d501 100644
--- a/sldns/parseutil.c
+++ b/sldns/parseutil.c
@@ -790,3 +790,18 @@ int sldns_b64url_pton(char const *src, size_t srcsize, uint8_t *target,
}
return sldns_b64_pton_base(src, srcsize, target, targsize, 1);
}
+
+int sldns_b64_contains_nonurl(char const *src, size_t srcsize)
+{
+ const char* s = src;
+ while(*s && srcsize) {
+ char d = *s++;
+ srcsize--;
+ /* the '+' and the '/' and padding '=' is not allowed in b64
+ * url encoding */
+ if(d == '+' || d == '/' || d == '=') {
+ return 1;
+ }
+ }
+ return 0;
+}
diff --git a/sldns/parseutil.h b/sldns/parseutil.h
index 7eb23317f285..74d7c727571a 100644
--- a/sldns/parseutil.h
+++ b/sldns/parseutil.h
@@ -102,6 +102,7 @@ size_t sldns_b64_pton_calculate_size(size_t srcsize);
int sldns_b64_pton(char const *src, uint8_t *target, size_t targsize);
int sldns_b64url_pton(char const *src, size_t srcsize, uint8_t *target,
size_t targsize);
+int sldns_b64_contains_nonurl(char const *src, size_t srcsize);
/**
* calculates the size needed to store the result of b32_ntop
diff --git a/sldns/rrdef.c b/sldns/rrdef.c
index 0af015f4b3b5..fe5c8e104a88 100644
--- a/sldns/rrdef.c
+++ b/sldns/rrdef.c
@@ -150,6 +150,12 @@ static const sldns_rdf_type type_openpgpkey_wireformat[] = {
static const sldns_rdf_type type_csync_wireformat[] = {
LDNS_RDF_TYPE_INT32, LDNS_RDF_TYPE_INT16, LDNS_RDF_TYPE_NSEC
};
+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*/
@@ -372,9 +378,12 @@ static sldns_rr_descriptor rdata_field_descriptors[] = {
{LDNS_RR_TYPE_OPENPGPKEY, "OPENPGPKEY", 1, 1, type_openpgpkey_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
/* 62 */
{LDNS_RR_TYPE_CSYNC, "CSYNC", 3, 3, type_csync_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
-{(enum sldns_enum_rr_type)0, "TYPE63", 1, 1, type_0_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 },
-{(enum sldns_enum_rr_type)0, "TYPE65", 1, 1, type_0_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 },
+ /* 64 */
+ {LDNS_RR_TYPE_SVCB, "SVCB", 2, 2, type_svcb_wireformat, LDNS_RDF_TYPE_SVCPARAM, LDNS_RR_NO_COMPRESS, 0 },
+ /* 65 */
+ {LDNS_RR_TYPE_HTTPS, "HTTPS", 2, 2, type_svcb_wireformat, LDNS_RDF_TYPE_SVCPARAM, 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 },
{(enum sldns_enum_rr_type)0, "TYPE68", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
diff --git a/sldns/rrdef.h b/sldns/rrdef.h
index e084f354a8a1..42d5de064eea 100644
--- a/sldns/rrdef.h
+++ b/sldns/rrdef.h
@@ -195,6 +195,9 @@ enum sldns_enum_rr_type
LDNS_RR_TYPE_CDNSKEY = 60, /** RFC 7344 */
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 */
@@ -352,8 +355,13 @@ enum sldns_enum_rdf_type
/** TSIG extended 16bit error value */
LDNS_RDF_TYPE_TSIGERROR,
+ /* draft-ietf-dnsop-svcb-https-05:
+ * 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;
diff --git a/sldns/sbuffer.h b/sldns/sbuffer.h
index 5dbe988cd1b8..1b7fe370cc4e 100644
--- a/sldns/sbuffer.h
+++ b/sldns/sbuffer.h
@@ -202,7 +202,6 @@ INLINE void sldns_buffer_clear(sldns_buffer *buffer)
* the position is set to 0.
*
* \param[in] buffer the buffer to flip
- * \return void
*/
INLINE void sldns_buffer_flip(sldns_buffer *buffer)
{
@@ -732,7 +731,6 @@ int sldns_buffer_printf(sldns_buffer *buffer, const char *format, ...)
/**
* frees the buffer.
* \param[in] *buffer the buffer to be freed
- * \return void
*/
void sldns_buffer_free(sldns_buffer *buffer);
diff --git a/sldns/str2wire.c b/sldns/str2wire.c
index 977cda28a2da..fbd615cbfd7d 100644
--- a/sldns/str2wire.c
+++ b/sldns/str2wire.c
@@ -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
@@ -615,6 +614,122 @@ sldns_affix_token(sldns_buffer* strbuf, char* token, size_t* token_len,
return 1;
}
+static int sldns_str2wire_svcparam_key_cmp(const void *a, const void *b)
+{
+ return sldns_read_uint16(*(uint8_t**) a)
+ - sldns_read_uint16(*(uint8_t**) b);
+}
+
+/**
+ * Add constraints to the SVCB RRs which involve the whole set
+ */
+static int sldns_str2wire_check_svcbparams(uint8_t* rdata, uint16_t rdata_len)
+{
+ size_t nparams = 0, i;
+ uint8_t new_rdata[LDNS_MAX_RDFLEN];
+ uint8_t* new_rdata_ptr = new_rdata;
+ uint8_t* svcparams[MAX_NUMBER_OF_SVCPARAMS];
+ uint8_t* rdata_ptr = rdata;
+ uint16_t rdata_remaining = rdata_len;
+
+ /* find the SvcParams */
+ while (rdata_remaining) {
+ uint16_t svcbparam_len;
+
+ svcparams[nparams] = rdata_ptr;
+ if (rdata_remaining < 4)
+ return LDNS_WIREPARSE_ERR_SVCPARAM_BROKEN_RDATA;
+ svcbparam_len = sldns_read_uint16(rdata_ptr + 2);
+ rdata_remaining -= 4;
+ rdata_ptr += 4;
+
+ if (rdata_remaining < svcbparam_len)
+ return LDNS_WIREPARSE_ERR_SVCPARAM_BROKEN_RDATA;
+ rdata_remaining -= svcbparam_len;
+ rdata_ptr += svcbparam_len;
+
+ nparams += 1;
+ if (nparams >= MAX_NUMBER_OF_SVCPARAMS)
+ return LDNS_WIREPARSE_ERR_SVCB_TOO_MANY_PARAMS;
+ }
+
+ /* In draft-ietf-dnsop-svcb-https-06 Section 7:
+ *
+ * In wire format, the keys are represented by their numeric
+ * values in network byte order, concatenated in ascending order.
+ */
+ qsort((void *)svcparams
+ ,nparams
+ ,sizeof(uint8_t*)
+ ,sldns_str2wire_svcparam_key_cmp);
+
+
+ /* The code below revolves around sematic errors in the SVCParam set.
+ * So long as we do not distinguish between running Unbound as a primary
+ * or as a secondary, we default to secondary behavior and we ignore the
+ * sematic errors. */
+
+#ifdef SVCB_SEMANTIC_ERRORS
+ {
+ uint8_t* mandatory = NULL;
+ /* In draft-ietf-dnsop-svcb-https-06 Section 7:
+ *
+ * Keys (...) MUST NOT appear more than once.
+ *
+ * If they key has already been seen, we have a duplicate
+ */
+ for(i=0; i < nparams; i++) {
+ uint16_t key = sldns_read_uint16(svcparams[i]);
+ if(i + 1 < nparams && key == sldns_read_uint16(svcparams[i+1]))
+ return LDNS_WIREPARSE_ERR_SVCB_DUPLICATE_KEYS;
+ if(key == SVCB_KEY_MANDATORY)
+ mandatory = svcparams[i];
+ }
+
+ /* 4. verify that all the SvcParamKeys in mandatory are present */
+ if(mandatory) {
+ /* Divide by sizeof(uint16_t)*/
+ uint16_t mandatory_nkeys = sldns_read_uint16(mandatory + 2) / sizeof(uint16_t);
+
+ /* Guaranteed by sldns_str2wire_svcparam_key_value */
+ assert(mandatory_nkeys > 0);
+
+ for(i=0; i < mandatory_nkeys; i++) {
+ uint16_t mandatory_key = sldns_read_uint16(
+ mandatory
+ + 2 * sizeof(uint16_t)
+ + i * sizeof(uint16_t));
+ uint8_t found = 0;
+ size_t j;
+
+ for(j=0; j < nparams; j++) {
+ if(mandatory_key == sldns_read_uint16(svcparams[j])) {
+ found = 1;
+ break;
+ }
+ }
+
+ if(!found)
+ return LDNS_WIREPARSE_ERR_SVCB_MANDATORY_MISSING_PARAM;
+ }
+ }
+ }
+#endif
+ /* Write rdata in correct order */
+ for (i = 0; i < nparams; i++) {
+ uint16_t svcparam_len = sldns_read_uint16(svcparams[i] + 2)
+ + 2 * sizeof(uint16_t);
+
+ if ((unsigned)(new_rdata_ptr - new_rdata) + svcparam_len > sizeof(new_rdata))
+ return LDNS_WIREPARSE_ERR_BUFFER_TOO_SMALL;
+
+ memcpy(new_rdata_ptr, svcparams[i], svcparam_len);
+ new_rdata_ptr += svcparam_len;
+ }
+ memcpy(rdata, new_rdata, rdata_len);
+ return LDNS_WIREPARSE_ERR_OK;
+}
+
/** parse rdata from string into rr buffer(-remainder after dname). */
static int
rrinternal_parse_rdata(sldns_buffer* strbuf, char* token, size_t token_len,
@@ -712,6 +827,42 @@ rrinternal_parse_rdata(sldns_buffer* strbuf, char* token, size_t token_len,
/* write rdata length */
sldns_write_uint16(rr+dname_len+8, (uint16_t)(rr_cur_len-dname_len-10));
*rr_len = rr_cur_len;
+ /* SVCB/HTTPS handling */
+ if (rr_type == LDNS_RR_TYPE_SVCB || rr_type == LDNS_RR_TYPE_HTTPS) {
+ size_t rdata_len = rr_cur_len - dname_len - 10;
+ uint8_t *rdata = rr+dname_len + 10;
+
+ /* skip 1st rdata field SvcPriority (uint16_t) */
+ if (rdata_len < sizeof(uint16_t))
+ return LDNS_WIREPARSE_ERR_OK;
+
+ rdata_len -= sizeof(uint16_t);
+ rdata += sizeof(uint16_t);
+
+ /* skip 2nd rdata field dname */
+ while (rdata_len && *rdata != 0) {
+ uint8_t label_len;
+
+ if (*rdata & 0xC0)
+ return LDNS_WIREPARSE_ERR_OK;
+
+ label_len = *rdata + 1;
+ if (rdata_len < label_len)
+ return LDNS_WIREPARSE_ERR_OK;
+
+ rdata_len -= label_len;
+ rdata += label_len;
+ }
+ /* The root label is one more character, so smaller
+ * than 1 + 1 means no Svcparam Keys */
+ if (rdata_len < 2 || *rdata != 0)
+ return LDNS_WIREPARSE_ERR_OK;
+
+ rdata_len -= 1;
+ rdata += 1;
+ return sldns_str2wire_check_svcbparams(rdata, rdata_len);
+
+ }
return LDNS_WIREPARSE_ERR_OK;
}
@@ -929,11 +1080,533 @@ int sldns_fp2wire_rr_buf(FILE* in, uint8_t* rr, size_t* len, size_t* dname_len,
memmove(parse_state->prev_rr, rr, *dname_len);
parse_state->prev_rr_len = (*dname_len);
}
+ if(r == LDNS_WIREPARSE_ERR_OK && parse_state) {
+ parse_state->default_ttl = sldns_wirerr_get_ttl(
+ rr, *len, *dname_len);
+ }
return r;
}
return LDNS_WIREPARSE_ERR_OK;
}
+static int
+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;
+ }
+
+ /* 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, SVCB_KEY_PORT);
+ sldns_write_uint16(rd + 2, sizeof(uint16_t));
+ sldns_write_uint16(rd + 4, port);
+ *rd_len = 6;
+
+ return LDNS_WIREPARSE_ERR_OK;
+ }
+
+ return LDNS_WIREPARSE_ERR_SVCB_PORT_VALUE_SYNTAX;
+}
+
+static int
+sldns_str2wire_svcbparam_ipv4hint(const char* val, uint8_t* rd, size_t* rd_len)
+{
+ size_t count;
+ char ip_str[INET_ADDRSTRLEN+1];
+ char *next_ip_str;
+ size_t i;
+
+ for (i = 0, count = 1; val[i]; i++) {
+ if (val[i] == ',')
+ count += 1;
+ if (count > SVCB_MAX_COMMA_SEPARATED_VALUES) {
+ return LDNS_WIREPARSE_ERR_SVCB_IPV4_TOO_MANY_ADDRESSES;
+ }
+ }
+
+ 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, SVCB_KEY_IPV4HINT);
+ sldns_write_uint16(rd + 2, LDNS_IP4ADDRLEN * count);
+ *rd_len = 4;
+
+ while (count) {
+ if (!(next_ip_str = strchr(val, ','))) {
+ if (inet_pton(AF_INET, val, rd + *rd_len) != 1)
+ break;
+ *rd_len += LDNS_IP4ADDRLEN;
+
+ 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) {
+ break;
+ }
+ *rd_len += LDNS_IP4ADDRLEN;
+
+ val = next_ip_str + 1;
+ }
+ count--;
+ }
+ if (count) /* verify that we parsed all values */
+ return LDNS_WIREPARSE_ERR_SYNTAX_IP4;
+
+ return LDNS_WIREPARSE_ERR_OK;
+}
+
+static int
+sldns_str2wire_svcbparam_ipv6hint(const char* val, uint8_t* rd, size_t* rd_len)
+{
+ size_t count;
+ char ip_str[INET6_ADDRSTRLEN+1];
+ char *next_ip_str;
+ size_t i;
+
+ for (i = 0, count = 1; val[i]; i++) {
+ if (val[i] == ',')
+ count += 1;
+ if (count > SVCB_MAX_COMMA_SEPARATED_VALUES) {
+ return LDNS_WIREPARSE_ERR_SVCB_IPV6_TOO_MANY_ADDRESSES;
+ }
+ }
+
+ 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, SVCB_KEY_IPV6HINT);
+ sldns_write_uint16(rd + 2, LDNS_IP6ADDRLEN * count);
+ *rd_len = 4;
+
+ while (count) {
+ if (!(next_ip_str = strchr(val, ','))) {
+ if (inet_pton(AF_INET6, val, rd + *rd_len) != 1)
+ break;
+ *rd_len += LDNS_IP6ADDRLEN;
+
+ 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_INET6, ip_str, rd + *rd_len) != 1) {
+ break;
+ }
+ *rd_len += LDNS_IP6ADDRLEN;
+
+ val = next_ip_str + 1;
+ }
+ count--;
+ }
+ if (count) /* verify that we parsed all values */
+ return LDNS_WIREPARSE_ERR_SYNTAX_IP6;
+
+ 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;
+
+ val_len = strlen(val);
+
+ for (i = 0, count = 1; val[i]; i++) {
+ if (val[i] == ',')
+ count += 1;
+ if (count > SVCB_MAX_COMMA_SEPARATED_VALUES) {
+ return LDNS_WIREPARSE_ERR_SVCB_MANDATORY_TOO_MANY_KEYS;
+ }
+ }
+ if (sizeof(uint16_t) * (count + 2) > *rd_len)
+ return LDNS_WIREPARSE_ERR_BUFFER_TOO_SMALL;
+
+ sldns_write_uint16(rd, SVCB_KEY_MANDATORY);
+ sldns_write_uint16(rd + 2, sizeof(uint16_t) * count);
+ *rd_len = 4;
+
+ while (1) {
+ int svcparamkey;
+
+ if (!(next_key = strchr(val, ','))) {
+ svcparamkey = sldns_str2wire_svcparam_key_lookup(val, val_len);
+
+ if (svcparamkey < 0) {
+ return LDNS_WIREPARSE_ERR_SVCB_UNKNOWN_KEY;
+ }
+
+ sldns_write_uint16(rd + *rd_len, svcparamkey);
+ *rd_len += 2;
+ break;
+ } else {
+ svcparamkey = sldns_str2wire_svcparam_key_lookup(val, next_key - val);
+
+ if (svcparamkey < 0) {
+ return LDNS_WIREPARSE_ERR_SVCB_UNKNOWN_KEY;
+ }
+
+ sldns_write_uint16(rd + *rd_len,
+ svcparamkey);
+ *rd_len += 2;
+ }
+
+ val_len -= next_key - val + 1;
+ val = next_key + 1; /* skip the comma */
+ }
+
+ /* In draft-ietf-dnsop-svcb-https-06 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);
+
+ /* The code below revolves around sematic errors in the SVCParam set.
+ * So long as we do not distinguish between running Unbound as a primary
+ * or as a secondary, we default to secondary behavior and we ignore the
+ * semantic errors. */
+#ifdef SVCB_SEMANTIC_ERRORS
+ /* In draft-ietf-dnsop-svcb-https-06 Section 8
+ * automatically mandatory MUST NOT appear in its own value-list
+ */
+ if (sldns_read_uint16(rd + 4) == SVCB_KEY_MANDATORY)
+ return LDNS_WIREPARSE_ERR_SVCB_MANDATORY_IN_MANDATORY;
+
+ /* Guarantee key uniqueness. After the sort we only need to
+ * compare neighbouring keys */
+ if (count > 1) {
+ for (i = 0; i < count - 1; i++) {
+ uint8_t* current_pos = (rd + 4 + (sizeof(uint16_t) * i));
+ uint16_t key = sldns_read_uint16(current_pos);
+
+ if (key == sldns_read_uint16(current_pos + 2)) {
+ return LDNS_WIREPARSE_ERR_SVCB_MANDATORY_DUPLICATE_KEY;
+ }
+ }
+ }
+#endif
+ 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;
+
+ /* single 0 represents empty buffer */
+ if(strcmp(val, "0") == 0) {
+ if (*rd_len < 4)
+ return LDNS_WIREPARSE_ERR_BUFFER_TOO_SMALL;
+ sldns_write_uint16(rd, SVCB_KEY_ECH);
+ sldns_write_uint16(rd + 2, 0);
+
+ return LDNS_WIREPARSE_ERR_OK;
+ }
+
+ wire_len = sldns_b64_pton(val, buffer, LDNS_MAX_RDFLEN);
+
+ if (wire_len <= 0) {
+ return LDNS_WIREPARSE_ERR_SYNTAX_B64;
+ } else if ((unsigned)wire_len + 4 > *rd_len) {
+ return LDNS_WIREPARSE_ERR_BUFFER_TOO_SMALL;
+ } else {
+ sldns_write_uint16(rd, SVCB_KEY_ECH);
+ sldns_write_uint16(rd + 2, wire_len);
+ memcpy(rd + 4, buffer, wire_len);
+ *rd_len = 4 + wire_len;
+
+ return LDNS_WIREPARSE_ERR_OK;
+ }
+}
+
+static const char*
+sldns_str2wire_svcbparam_parse_next_unescaped_comma(const char *val)
+{
+ while (*val) {
+ /* Only return when the comma is not escaped*/
+ if (*val == '\\'){
+ ++val;
+ if (!*val)
+ break;
+ } else if (*val == ',')
+ return val;
+
+ val++;
+ }
+ return NULL;
+}
+
+/* The source is already properly unescaped, this double unescaping is purely to allow for
+ * comma's in comma seperated alpn lists.
+ *
+ * In draft-ietf-dnsop-svcb-https-06 Section 7:
+ * To enable simpler parsing, this SvcParamValue MUST NOT contain escape sequences.
+ */
+static size_t
+sldns_str2wire_svcbparam_parse_copy_unescaped(uint8_t *dst,
+ const char *src, size_t len)
+{
+ uint8_t *orig_dst = dst;
+
+ while (len) {
+ if (*src == '\\') {
+ src++;
+ len--;
+ if (!len)
+ break;
+ }
+ *dst++ = *src++;
+ len--;
+ }
+ return (size_t)(dst - orig_dst);
+}
+
+static int
+sldns_str2wire_svcbparam_alpn_value(const char* val,
+ uint8_t* rd, size_t* rd_len)
+{
+ uint8_t unescaped_dst[LDNS_MAX_RDFLEN];
+ uint8_t *dst = unescaped_dst;
+ const char *next_str;
+ size_t str_len;
+ size_t dst_len;
+ size_t val_len;
+
+ val_len = strlen(val);
+
+ if (val_len > sizeof(unescaped_dst)) {
+ return LDNS_WIREPARSE_ERR_SVCB_ALPN_KEY_TOO_LARGE;
+ }
+ while (val_len) {
+ size_t key_len;
+
+ str_len = (next_str = sldns_str2wire_svcbparam_parse_next_unescaped_comma(val))
+ ? (size_t)(next_str - val) : val_len;
+
+ if (str_len > 255) {
+ return LDNS_WIREPARSE_ERR_SVCB_ALPN_KEY_TOO_LARGE;
+ }
+
+ key_len = sldns_str2wire_svcbparam_parse_copy_unescaped(dst + 1, val, str_len);
+ *dst++ = key_len;
+ dst += key_len;
+
+ if (!next_str)
+ break;
+
+ /* skip the comma in the next iteration */
+ val_len -= next_str - val + 1;
+ val = next_str + 1;
+ }
+ dst_len = dst - unescaped_dst;
+ if (*rd_len < 4 + dst_len)
+ return LDNS_WIREPARSE_ERR_BUFFER_TOO_SMALL;
+ sldns_write_uint16(rd, SVCB_KEY_ALPN);
+ sldns_write_uint16(rd + 2, dst_len);
+ memcpy(rd + 4, unescaped_dst, dst_len);
+ *rd_len = 4 + dst_len;
+
+ return LDNS_WIREPARSE_ERR_OK;
+}
+
+static int
+sldns_str2wire_svcparam_value(const char *key, size_t key_len,
+ const char *val, uint8_t* rd, size_t* rd_len)
+{
+ size_t str_len;
+ int svcparamkey = sldns_str2wire_svcparam_key_lookup(key, key_len);
+
+ if (svcparamkey < 0) {
+ return LDNS_WIREPARSE_ERR_SVCB_UNKNOWN_KEY;
+ }
+
+ /* key without value */
+ if (val == NULL) {
+ switch (svcparamkey) {
+#ifdef SVCB_SEMANTIC_ERRORS
+ case SVCB_KEY_MANDATORY:
+ case SVCB_KEY_ALPN:
+ case SVCB_KEY_PORT:
+ case SVCB_KEY_IPV4HINT:
+ case SVCB_KEY_IPV6HINT:
+ return LDNS_WIREPARSE_ERR_SVCB_MISSING_PARAM;
+#endif
+ default:
+ if (*rd_len < 4)
+ return LDNS_WIREPARSE_ERR_BUFFER_TOO_SMALL;
+ sldns_write_uint16(rd, svcparamkey);
+ sldns_write_uint16(rd + 2, 0);
+ *rd_len = 4;
+
+ return LDNS_WIREPARSE_ERR_OK;
+ }
+ }
+
+ /* value is non-empty */
+ 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);
+#ifdef SVCB_SEMANTIC_ERRORS
+ case SVCB_KEY_NO_DEFAULT_ALPN:
+ return LDNS_WIREPARSE_ERR_SVCB_NO_DEFAULT_ALPN_VALUE;
+#endif
+ 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:
+ str_len = strlen(val);
+ if (*rd_len < 4 + str_len)
+ return LDNS_WIREPARSE_ERR_BUFFER_TOO_SMALL;
+ sldns_write_uint16(rd, svcparamkey);
+ sldns_write_uint16(rd + 2, str_len);
+ memcpy(rd + 4, val, str_len);
+ *rd_len = 4 + str_len;
+
+ return LDNS_WIREPARSE_ERR_OK;
+ }
+
+ return LDNS_WIREPARSE_ERR_GENERAL;
+}
+
+int sldns_str2wire_svcparam_buf(const char* str, uint8_t* rd, size_t* rd_len)
+{
+ const char* eq_pos;
+ char unescaped_val[LDNS_MAX_RDFLEN];
+ char* val_out = unescaped_val;
+ const char* val_in;
+
+ eq_pos = strchr(str, '=');
+
+ /* case: key=value */
+ if (eq_pos != NULL && eq_pos[1]) {
+ val_in = eq_pos + 1;
+
+ /* unescape characters and "" blocks */
+ if (*val_in == '"') {
+ val_in++;
+ while (*val_in != '"'
+ && (unsigned)(val_out - unescaped_val + 1) < sizeof(unescaped_val)
+ && sldns_parse_char( (uint8_t*) val_out, &val_in)) {
+ val_out++;
+ }
+ } else {
+ while ((unsigned)(val_out - unescaped_val + 1) < sizeof(unescaped_val)
+ && sldns_parse_char( (uint8_t*) val_out, &val_in)) {
+ val_out++;
+ }
+ }
+ *val_out = 0;
+
+ return sldns_str2wire_svcparam_value(str, eq_pos - str,
+ unescaped_val[0] ? unescaped_val : NULL, rd, rd_len);
+ }
+ /* case: key= */
+ else if (eq_pos != NULL && !(eq_pos[1])) {
+ return sldns_str2wire_svcparam_value(str, eq_pos - str, NULL, rd, rd_len);
+ }
+ /* case: key */
+ else {
+ return sldns_str2wire_svcparam_value(str, strlen(str), NULL, rd, rd_len);
+ }
+}
+
int sldns_str2wire_rdf_buf(const char* str, uint8_t* rd, size_t* len,
sldns_rdf_type rdftype)
{
@@ -1006,6 +1679,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;
@@ -1491,13 +2166,17 @@ static int
loc_parse_cm(char* my_str, char** endstr, uint8_t* m, uint8_t* e)
{
uint32_t meters = 0, cm = 0, val;
+ char* cm_endstr;
while (isblank((unsigned char)*my_str)) {
my_str++;
}
meters = (uint32_t)strtol(my_str, &my_str, 10);
if (*my_str == '.') {
my_str++;
- cm = (uint32_t)strtol(my_str, &my_str, 10);
+ cm = (uint32_t)strtol(my_str, &cm_endstr, 10);
+ if(cm_endstr == my_str + 1)
+ cm *= 10;
+ my_str = cm_endstr;
}
if (meters >= 1) {
*e = 2;
diff --git a/sldns/str2wire.h b/sldns/str2wire.h
index 70070e4f5752..0c316498956e 100644
--- a/sldns/str2wire.h
+++ b/sldns/str2wire.h
@@ -23,10 +23,27 @@ 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 MAX_NUMBER_OF_SVCPARAMS 64
+
+#define SVCB_MAX_COMMA_SEPARATED_VALUES 1000
+
/*
* To convert class and type to string see
* sldns_get_rr_class_by_name(str)
@@ -204,6 +221,20 @@ uint8_t* sldns_wirerr_get_rdatawl(uint8_t* rr, size_t len, size_t dname_len);
#define LDNS_WIREPARSE_ERR_SYNTAX_INTEGER_OVERFLOW 370
#define LDNS_WIREPARSE_ERR_INCLUDE 371
#define LDNS_WIREPARSE_ERR_PARENTHESIS 372
+#define LDNS_WIREPARSE_ERR_SVCB_UNKNOWN_KEY 373
+#define LDNS_WIREPARSE_ERR_SVCB_MISSING_PARAM 374
+#define LDNS_WIREPARSE_ERR_SVCB_TOO_MANY_PARAMS 375
+#define LDNS_WIREPARSE_ERR_SVCB_DUPLICATE_KEYS 376
+#define LDNS_WIREPARSE_ERR_SVCB_MANDATORY_TOO_MANY_KEYS 377
+#define LDNS_WIREPARSE_ERR_SVCB_MANDATORY_MISSING_PARAM 378
+#define LDNS_WIREPARSE_ERR_SVCB_MANDATORY_DUPLICATE_KEY 379
+#define LDNS_WIREPARSE_ERR_SVCB_MANDATORY_IN_MANDATORY 380
+#define LDNS_WIREPARSE_ERR_SVCB_PORT_VALUE_SYNTAX 381
+#define LDNS_WIREPARSE_ERR_SVCB_IPV4_TOO_MANY_ADDRESSES 382
+#define LDNS_WIREPARSE_ERR_SVCB_IPV6_TOO_MANY_ADDRESSES 383
+#define LDNS_WIREPARSE_ERR_SVCB_ALPN_KEY_TOO_LARGE 384
+#define LDNS_WIREPARSE_ERR_SVCB_NO_DEFAULT_ALPN_VALUE 385
+#define LDNS_WIREPARSE_ERR_SVCPARAM_BROKEN_RDATA 386
/**
* Get reference to a constant string for the (parse) error.
diff --git a/sldns/wire2str.c b/sldns/wire2str.c
index d0d1632d407d..6a177ec0b06c 100644
--- a/sldns/wire2str.c
+++ b/sldns/wire2str.c
@@ -149,6 +149,30 @@ static sldns_lookup_table sldns_wireparse_errors_data[] = {
{ LDNS_WIREPARSE_ERR_SYNTAX_INTEGER_OVERFLOW, "Syntax error, integer overflow" },
{ LDNS_WIREPARSE_ERR_INCLUDE, "$INCLUDE directive was seen in the zone" },
{ LDNS_WIREPARSE_ERR_PARENTHESIS, "Parse error, parenthesis mismatch" },
+ { LDNS_WIREPARSE_ERR_SVCB_UNKNOWN_KEY, "Unknown SvcParamKey"},
+ { LDNS_WIREPARSE_ERR_SVCB_MISSING_PARAM, "SvcParam is missing a SvcParamValue"},
+ { LDNS_WIREPARSE_ERR_SVCB_DUPLICATE_KEYS, "Duplicate SVCB key found"},
+ { LDNS_WIREPARSE_ERR_SVCB_MANDATORY_TOO_MANY_KEYS, "Too many keys in mandatory" },
+ { LDNS_WIREPARSE_ERR_SVCB_TOO_MANY_PARAMS,
+ "Too many SvcParams. Unbound only allows 63 entries" },
+ { LDNS_WIREPARSE_ERR_SVCB_MANDATORY_MISSING_PARAM,
+ "Mandatory SvcParamKey is missing"},
+ { LDNS_WIREPARSE_ERR_SVCB_MANDATORY_DUPLICATE_KEY,
+ "Keys in SvcParam mandatory MUST be unique" },
+ { LDNS_WIREPARSE_ERR_SVCB_MANDATORY_IN_MANDATORY,
+ "mandatory MUST not be included as mandatory parameter" },
+ { LDNS_WIREPARSE_ERR_SVCB_PORT_VALUE_SYNTAX,
+ "Could not parse port SvcParamValue" },
+ { LDNS_WIREPARSE_ERR_SVCB_IPV4_TOO_MANY_ADDRESSES,
+ "Too many IPv4 addresses in ipv4hint" },
+ { LDNS_WIREPARSE_ERR_SVCB_IPV6_TOO_MANY_ADDRESSES,
+ "Too many IPv6 addresses in ipv6hint" },
+ { LDNS_WIREPARSE_ERR_SVCB_ALPN_KEY_TOO_LARGE,
+ "Alpn strings need to be smaller than 255 chars"},
+ { LDNS_WIREPARSE_ERR_SVCB_NO_DEFAULT_ALPN_VALUE,
+ "No-default-alpn should not have a value" },
+ { LDNS_WIREPARSE_ERR_SVCPARAM_BROKEN_RDATA,
+ "General SVCParam error" },
{ 0, NULL }
};
sldns_lookup_table* sldns_wireparse_errors = sldns_wireparse_errors_data;
@@ -196,6 +220,12 @@ static sldns_lookup_table sldns_tsig_errors_data[] = {
};
sldns_lookup_table* sldns_tsig_errors = sldns_tsig_errors_data;
+/* draft-ietf-dnsop-svcb-https-06: 6. Initial SvcParamKeys */
+const char *svcparamkey_strs[] = {
+ "mandatory", "alpn", "no-default-alpn", "port",
+ "ipv4hint", "ech", "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 +970,253 @@ 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) {
+ return sldns_str_print(s, slen, "%s", svcparamkey_strs[svcparamkey]);
+ }
+ else {
+ return sldns_str_print(s, slen, "key%d", (int)svcparamkey);
+ }
+}
+
+static int sldns_wire2str_svcparam_port2str(char** s,
+ size_t* slen, uint16_t data_len, uint8_t* data)
+{
+ int w = 0;
+
+ if (data_len != 2)
+ return -1; /* wireformat error, a short is 2 bytes */
+ w = sldns_str_print(s, slen, "=%d", (int)sldns_read_uint16(data));
+
+ return w;
+}
+
+static int sldns_wire2str_svcparam_ipv4hint2str(char** s,
+ size_t* slen, uint16_t data_len, uint8_t* data)
+{
+ char ip_str[INET_ADDRSTRLEN + 1];
+
+ int w = 0;
+
+ assert(data_len > 0);
+
+ if ((data_len % LDNS_IP4ADDRLEN) == 0) {
+ if (inet_ntop(AF_INET, data, ip_str, sizeof(ip_str)) == NULL)
+ return -1; /* wireformat error, incorrect size or inet family */
+
+ w += sldns_str_print(s, slen, "=%s", ip_str);
+ data += LDNS_IP4ADDRLEN;
+
+ while ((data_len -= LDNS_IP4ADDRLEN) > 0) {
+ if (inet_ntop(AF_INET, data, ip_str, sizeof(ip_str)) == NULL)
+ return -1; /* wireformat error, incorrect size or inet family */
+
+ w += sldns_str_print(s, slen, ",%s", ip_str);
+ data += LDNS_IP4ADDRLEN;
+ }
+ } else
+ return -1;
+
+ return w;
+}
+
+static int sldns_wire2str_svcparam_ipv6hint2str(char** s,
+ size_t* slen, uint16_t data_len, uint8_t* data)
+{
+ char ip_str[INET6_ADDRSTRLEN + 1];
+
+ int w = 0;
+
+ assert(data_len > 0);
+
+ if ((data_len % LDNS_IP6ADDRLEN) == 0) {
+ if (inet_ntop(AF_INET6, data, ip_str, sizeof(ip_str)) == NULL)
+ return -1; /* wireformat error, incorrect size or inet family */
+
+ w += sldns_str_print(s, slen, "=%s", ip_str);
+ data += LDNS_IP6ADDRLEN;
+
+ while ((data_len -= LDNS_IP6ADDRLEN) > 0) {
+ if (inet_ntop(AF_INET6, data, ip_str, sizeof(ip_str)) == NULL)
+ return -1; /* wireformat error, incorrect size or inet family */
+
+ w += sldns_str_print(s, slen, ",%s", ip_str);
+ data += LDNS_IP6ADDRLEN;
+ }
+ } else
+ return -1;
+
+ return w;
+}
+
+static int sldns_wire2str_svcparam_mandatory2str(char** s,
+ size_t* slen, uint16_t data_len, uint8_t* data)
+{
+ int w = 0;
+
+ assert(data_len > 0);
+
+ if (data_len % sizeof(uint16_t))
+ return -1; // wireformat error, data_len must be multiple of shorts
+ w += sldns_str_print(s, slen, "=");
+ w += sldns_print_svcparamkey(s, slen, sldns_read_uint16(data));
+ data += 2;
+
+ while ((data_len -= sizeof(uint16_t))) {
+ w += sldns_str_print(s, slen, ",");
+ w += sldns_print_svcparamkey(s, slen, sldns_read_uint16(data));
+ data += 2;
+ }
+
+ return w;
+}
+
+static int sldns_wire2str_svcparam_alpn2str(char** s,
+ size_t* slen, uint16_t data_len, uint8_t* data)
+{
+ uint8_t *dp = (void *)data;
+ int w = 0;
+
+ assert(data_len > 0); /* Guaranteed by sldns_wire2str_svcparam_scan */
+
+ w += sldns_str_print(s, slen, "=\"");
+ while (data_len) {
+ /* alpn is list of length byte (str_len) followed by a string of that size */
+ uint8_t i, str_len = *dp++;
+
+ if (str_len > --data_len)
+ return -1;
+
+ for (i = 0; i < str_len; i++) {
+ if (dp[i] == '"' || dp[i] == '\\')
+ w += sldns_str_print(s, slen, "\\\\\\%c", dp[i]);
+
+ else if (dp[i] == ',')
+ w += sldns_str_print(s, slen, "\\\\%c", dp[i]);
+
+ else if (!isprint(dp[i]))
+ w += sldns_str_print(s, slen, "\\%03u", (unsigned) dp[i]);
+
+ else
+ w += sldns_str_print(s, slen, "%c", dp[i]);
+ }
+ dp += str_len;
+ if ((data_len -= str_len))
+ w += sldns_str_print(s, slen, "%s", ",");
+ }
+ w += sldns_str_print(s, slen, "\"");
+
+ return w;
+}
+
+static int sldns_wire2str_svcparam_ech2str(char** s,
+ size_t* slen, uint16_t data_len, uint8_t* data)
+{
+ int size;
+ int w = 0;
+
+ assert(data_len > 0); /* Guaranteed by sldns_wire2str_svcparam_scan */
+
+ w += sldns_str_print(s, slen, "=\"");
+
+ if ((size = sldns_b64_ntop(data, data_len, *s, *slen)) < 0)
+ return -1;
+
+ (*s) += size;
+ (*slen) -= size;
+
+ w += sldns_str_print(s, slen, "\"");
+
+ return w + size;
+}
+
+int sldns_wire2str_svcparam_scan(uint8_t** d, size_t* dlen, char** s, size_t* slen)
+{
+ uint8_t ch;
+ uint16_t svcparamkey, data_len;
+ int written_chars = 0;
+ int r, i;
+
+ /* verify that we have enough data to read svcparamkey and data_len */
+ if(*dlen < 4)
+ return -1;
+
+ svcparamkey = sldns_read_uint16(*d);
+ data_len = sldns_read_uint16(*d+2);
+ *d += 4;
+ *dlen -= 4;
+
+ /* verify that we have data_len data */
+ if (data_len > *dlen)
+ return -1;
+
+ written_chars += sldns_print_svcparamkey(s, slen, svcparamkey);
+ if (!data_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 -1;
+ default:
+ return written_chars;
+ }
+ }
+
+ switch (svcparamkey) {
+ case SVCB_KEY_PORT:
+ r = sldns_wire2str_svcparam_port2str(s, slen, data_len, *d);
+ break;
+ case SVCB_KEY_IPV4HINT:
+ r = sldns_wire2str_svcparam_ipv4hint2str(s, slen, data_len, *d);
+ break;
+ case SVCB_KEY_IPV6HINT:
+ r = sldns_wire2str_svcparam_ipv6hint2str(s, slen, data_len, *d);
+ break;
+ case SVCB_KEY_MANDATORY:
+ r = sldns_wire2str_svcparam_mandatory2str(s, slen, data_len, *d);
+ break;
+ case SVCB_KEY_NO_DEFAULT_ALPN:
+ return -1; /* wireformat error, should not have a value */
+ case SVCB_KEY_ALPN:
+ r = sldns_wire2str_svcparam_alpn2str(s, slen, data_len, *d);
+ break;
+ case SVCB_KEY_ECH:
+ r = sldns_wire2str_svcparam_ech2str(s, slen, data_len, *d);
+ break;
+ default:
+ r = sldns_str_print(s, slen, "=\"");
+
+ for (i = 0; i < data_len; i++) {
+ ch = (*d)[i];
+
+ if (ch == '"' || ch == '\\')
+ r += sldns_str_print(s, slen, "\\%c", ch);
+
+ else if (!isprint(ch))
+ r += sldns_str_print(s, slen, "\\%03u", (unsigned) ch);
+
+ else
+ r += sldns_str_print(s, slen, "%c", ch);
+
+ }
+ r += sldns_str_print(s, slen, "\"");
+ break;
+ }
+ if (r <= 0)
+ return -1; /* wireformat error */
+
+ written_chars += r;
+ *d += data_len;
+ *dlen -= data_len;
+ 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 +1294,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);
}
diff --git a/sldns/wire2str.h b/sldns/wire2str.h
index 0167fe7c1e2d..b1ad459e3780 100644
--- a/sldns/wire2str.h
+++ b/sldns/wire2str.h
@@ -495,6 +495,18 @@ int sldns_wire2str_dname_buf(uint8_t* dname, size_t dname_len, char* str,
size_t len);
/**
+ * Convert wire SVCB to a string with user buffer.
+ * @param d: the SVCB data in uncompressed wireformat.
+ * @param dlen: length of the SVCB data.
+ * @param s: the string to write to.
+ * @param slen: length of string.
+ * @return the number of characters for this element, excluding zerobyte.
+ * Is larger or equal than str_len if output was truncated.
+ */
+int sldns_wire2str_svcparam_scan(uint8_t** d, size_t* dlen, char** s,
+ size_t* slen);
+
+/**
* Scan wireformat rdf field to string, with user buffers.
* It shifts the arguments to move along (see sldns_wire2str_pkt_scan).
* @param data: wireformat data.