+/*
+ * Hex decoder that tolerates optional whitespace. Returns number of bytes
+ * produced, advances inptr to end of input string.
+ */
+static ossl_ssize_t hexdecode(const char **inptr, void *result)
+{
+ unsigned char **out = (unsigned char **)result;
+ const char *in = *inptr;
+ unsigned char *ret = OPENSSL_malloc(strlen(in)/2);
+ unsigned char *cp = ret;
+ uint8_t byte;
+ int nibble = 0;
+
+ if (ret == NULL)
+ return -1;
+
+ for (byte = 0; *in; ++in) {
+ char c;
+
+ if (isspace(*in))
+ continue;
+ c = tolower(*in);
+ if ('0' <= c && c <= '9') {
+ byte |= c - '0';
+ } else if ('a' <= c && c <= 'f') {
+ byte |= c - 'a' + 10;
+ } else {
+ OPENSSL_free(ret);
+ return 0;
+ }
+ if ((nibble ^= 1) == 0) {
+ *cp++ = byte;
+ byte = 0;
+ } else {
+ byte <<= 4;
+ }
+ }
+ if (nibble != 0) {
+ OPENSSL_free(ret);
+ return 0;
+ }
+ *inptr = in;
+
+ return cp - (*out = ret);
+}
+
+/*
+ * Decode unsigned 0..255, returns 1 on success, <= 0 on failure. Advances
+ * inptr to next field skipping leading whitespace.
+ */
+static ossl_ssize_t checked_uint8(const char **inptr, void *out)
+{
+ uint8_t *result = (uint8_t *)out;
+ const char *in = *inptr;
+ char *endp;
+ long v;
+ int e;
+
+ save_errno();
+ v = strtol(in, &endp, 10);
+ e = restore_errno();
+
+ if (((v == LONG_MIN || v == LONG_MAX) && e == ERANGE) ||
+ endp == in || !isspace(*endp) ||
+ v != (*result = (uint8_t) v)) {
+ return -1;
+ }
+ for (in = endp; isspace(*in); ++in)
+ continue;
+
+ *inptr = in;
+ return 1;
+}
+
+static int tlsa_import_rr(SSL *con, const char *rrdata)
+{
+ int ret;
+ uint8_t usage;
+ uint8_t selector;
+ uint8_t mtype;
+ unsigned char *data = NULL;
+ const char *cp = rrdata;
+ ossl_ssize_t len = 0;
+ struct tlsa_field {
+ void *var;
+ const char *name;
+ ossl_ssize_t (*parser)(const char **, void *);
+ } tlsa_fields[] = {
+ { &usage, "usage", checked_uint8 },
+ { &selector, "selector", checked_uint8 },
+ { &mtype, "mtype", checked_uint8 },
+ { &data, "data", hexdecode },
+ { NULL, }
+ };
+ struct tlsa_field *f;
+
+ for (f = tlsa_fields; f->var; ++f) {
+ /* Returns number of bytes produced, advances cp to next field */
+ if ((len = f->parser(&cp, f->var)) <= 0) {
+ BIO_printf(bio_err, "%s: warning: bad TLSA %s field in: %s\n",
+ prog, f->name, rrdata);
+ return 0;
+ }
+ }
+ /* The data field is last, so len is its length */
+ ret = SSL_dane_tlsa_add(con, usage, selector, mtype, data, len);
+ OPENSSL_free(data);
+
+ if (ret == 0) {
+ ERR_print_errors(bio_err);
+ BIO_printf(bio_err, "%s: warning: unusable TLSA rrdata: %s\n",
+ prog, rrdata);
+ return 0;
+ }
+ if (ret < 0) {
+ ERR_print_errors(bio_err);
+ BIO_printf(bio_err, "%s: warning: error loading TLSA rrdata: %s\n",
+ prog, rrdata);
+ return 0;
+ }
+ return ret;
+}
+
+static int tlsa_import_rrset(SSL *con, STACK_OF(OPENSSL_STRING) *rrset)
+{
+ int num = sk_OPENSSL_STRING_num(rrset);
+ int count = 0;
+ int i;
+
+ for (i = 0; i < num; ++i) {
+ char *rrdata = sk_OPENSSL_STRING_value(rrset, i);
+ if (tlsa_import_rr(con, rrdata) > 0)
+ ++count;
+ }
+ return count > 0;
+}
+