From 3468ea13e61f95732975dbab5c8ae4f1f644ccdb Mon Sep 17 00:00:00 2001 From: Willem Toorop Date: Tue, 9 Sep 2014 15:42:28 +0200 Subject: [PATCH] And not actually include on-board ldns2 (gldns) --- src/gldns/import.sh | 10 + src/gldns/keyraw.c | 368 ++++++++ src/gldns/keyraw.h | 112 +++ src/gldns/parse.c | 470 ++++++++++ src/gldns/parse.h | 184 ++++ src/gldns/parseutil.c | 726 +++++++++++++++ src/gldns/parseutil.h | 148 +++ src/gldns/pkthdr.h | 158 ++++ src/gldns/rrdef.c | 734 +++++++++++++++ src/gldns/rrdef.h | 503 +++++++++++ src/gldns/sbuffer.c | 178 ++++ src/gldns/sbuffer.h | 706 +++++++++++++++ src/gldns/str2wire.c | 2001 +++++++++++++++++++++++++++++++++++++++++ src/gldns/str2wire.h | 541 +++++++++++ src/gldns/wire2str.c | 1967 ++++++++++++++++++++++++++++++++++++++++ src/gldns/wire2str.h | 984 ++++++++++++++++++++ 16 files changed, 9790 insertions(+) create mode 100755 src/gldns/import.sh create mode 100644 src/gldns/keyraw.c create mode 100644 src/gldns/keyraw.h create mode 100644 src/gldns/parse.c create mode 100644 src/gldns/parse.h create mode 100644 src/gldns/parseutil.c create mode 100644 src/gldns/parseutil.h create mode 100644 src/gldns/pkthdr.h create mode 100644 src/gldns/rrdef.c create mode 100644 src/gldns/rrdef.h create mode 100644 src/gldns/sbuffer.c create mode 100644 src/gldns/sbuffer.h create mode 100644 src/gldns/str2wire.c create mode 100644 src/gldns/str2wire.h create mode 100644 src/gldns/wire2str.c create mode 100644 src/gldns/wire2str.h diff --git a/src/gldns/import.sh b/src/gldns/import.sh new file mode 100755 index 00000000..af1c9904 --- /dev/null +++ b/src/gldns/import.sh @@ -0,0 +1,10 @@ +#!/bin/sh + +# Meant to be run from this directory + +svn co http://unbound.net/svn/trunk/ldns/ +for f in ldns/*.[ch] +do + sed -e 's/sldns_/gldns_/g' -e 's/LDNS_/GLDNS_/g' -e 's/include "ldns/include "gldns/g' -e 's//"gldns\/rrdef.h"/g' $f > ${f#ldns/} +done +rm -r ldns diff --git a/src/gldns/keyraw.c b/src/gldns/keyraw.c new file mode 100644 index 00000000..a1bc2719 --- /dev/null +++ b/src/gldns/keyraw.c @@ -0,0 +1,368 @@ +/* + * keyraw.c - raw key operations and conversions + * + * (c) NLnet Labs, 2004-2008 + * + * See the file LICENSE for the license + */ +/** + * \file + * Implementation of raw DNSKEY functions (work on wire rdata). + */ + +#include "config.h" +#include "gldns/keyraw.h" +#include "gldns/rrdef.h" + +#ifdef HAVE_SSL +#include +#include +#include +#include +#include +#ifdef HAVE_OPENSSL_ENGINE_H +# include +#endif +#endif /* HAVE_SSL */ + +size_t +gldns_rr_dnskey_key_size_raw(const unsigned char* keydata, + const size_t len, int alg) +{ + /* for DSA keys */ + uint8_t t; + + /* for RSA keys */ + uint16_t exp; + uint16_t int16; + + switch ((gldns_algorithm)alg) { + case GLDNS_DSA: + case GLDNS_DSA_NSEC3: + if (len > 0) { + t = keydata[0]; + return (64 + t*8)*8; + } else { + return 0; + } + break; + case GLDNS_RSAMD5: + case GLDNS_RSASHA1: + case GLDNS_RSASHA1_NSEC3: +#ifdef USE_SHA2 + case GLDNS_RSASHA256: + case GLDNS_RSASHA512: +#endif + if (len > 0) { + if (keydata[0] == 0) { + /* big exponent */ + if (len > 3) { + memmove(&int16, keydata + 1, 2); + exp = ntohs(int16); + return (len - exp - 3)*8; + } else { + return 0; + } + } else { + exp = keydata[0]; + return (len-exp-1)*8; + } + } else { + return 0; + } + break; +#ifdef USE_GOST + case GLDNS_ECC_GOST: + return 512; +#endif +#ifdef USE_ECDSA + case GLDNS_ECDSAP256SHA256: + return 256; + case GLDNS_ECDSAP384SHA384: + return 384; +#endif + default: + return 0; + } +} + +uint16_t gldns_calc_keytag_raw(uint8_t* key, size_t keysize) +{ + if(keysize < 4) { + return 0; + } + /* look at the algorithm field, copied from 2535bis */ + if (key[3] == GLDNS_RSAMD5) { + uint16_t ac16 = 0; + if (keysize > 4) { + memmove(&ac16, key + keysize - 3, 2); + } + ac16 = ntohs(ac16); + return (uint16_t) ac16; + } else { + size_t i; + uint32_t ac32 = 0; + for (i = 0; i < keysize; ++i) { + ac32 += (i & 1) ? key[i] : key[i] << 8; + } + ac32 += (ac32 >> 16) & 0xFFFF; + return (uint16_t) (ac32 & 0xFFFF); + } +} + +#ifdef HAVE_SSL +#ifdef USE_GOST +/** store GOST engine reference loaded into OpenSSL library */ +ENGINE* gldns_gost_engine = NULL; + +int +gldns_key_EVP_load_gost_id(void) +{ + static int gost_id = 0; + const EVP_PKEY_ASN1_METHOD* meth; + ENGINE* e; + + if(gost_id) return gost_id; + + /* see if configuration loaded gost implementation from other engine*/ + meth = EVP_PKEY_asn1_find_str(NULL, "gost2001", -1); + if(meth) { + EVP_PKEY_asn1_get0_info(&gost_id, NULL, NULL, NULL, NULL, meth); + return gost_id; + } + + /* see if engine can be loaded already */ + e = ENGINE_by_id("gost"); + if(!e) { + /* load it ourself, in case statically linked */ + ENGINE_load_builtin_engines(); + ENGINE_load_dynamic(); + e = ENGINE_by_id("gost"); + } + if(!e) { + /* no gost engine in openssl */ + return 0; + } + if(!ENGINE_set_default(e, ENGINE_METHOD_ALL)) { + ENGINE_finish(e); + ENGINE_free(e); + return 0; + } + + meth = EVP_PKEY_asn1_find_str(&e, "gost2001", -1); + if(!meth) { + /* algo not found */ + ENGINE_finish(e); + ENGINE_free(e); + return 0; + } + /* Note: do not ENGINE_finish and ENGINE_free the acquired engine + * on some platforms this frees up the meth and unloads gost stuff */ + gldns_gost_engine = e; + + EVP_PKEY_asn1_get0_info(&gost_id, NULL, NULL, NULL, NULL, meth); + return gost_id; +} + +void gldns_key_EVP_unload_gost(void) +{ + if(gldns_gost_engine) { + ENGINE_finish(gldns_gost_engine); + ENGINE_free(gldns_gost_engine); + gldns_gost_engine = NULL; + } +} +#endif /* USE_GOST */ + +DSA * +gldns_key_buf2dsa_raw(unsigned char* key, size_t len) +{ + uint8_t T; + uint16_t length; + uint16_t offset; + DSA *dsa; + BIGNUM *Q; BIGNUM *P; + BIGNUM *G; BIGNUM *Y; + + if(len == 0) + return NULL; + T = (uint8_t)key[0]; + length = (64 + T * 8); + offset = 1; + + if (T > 8) { + return NULL; + } + if(len < (size_t)1 + SHA_DIGEST_LENGTH + 3*length) + return NULL; + + Q = BN_bin2bn(key+offset, SHA_DIGEST_LENGTH, NULL); + offset += SHA_DIGEST_LENGTH; + + P = BN_bin2bn(key+offset, (int)length, NULL); + offset += length; + + G = BN_bin2bn(key+offset, (int)length, NULL); + offset += length; + + Y = BN_bin2bn(key+offset, (int)length, NULL); + offset += length; + + /* 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); + return NULL; + } +#ifndef S_SPLINT_S + dsa->p = P; + dsa->q = Q; + dsa->g = G; + dsa->pub_key = Y; +#endif /* splint */ + + return dsa; +} + +RSA * +gldns_key_buf2rsa_raw(unsigned char* key, size_t len) +{ + uint16_t offset; + uint16_t exp; + uint16_t int16; + RSA *rsa; + BIGNUM *modulus; + BIGNUM *exponent; + + if (len == 0) + return NULL; + if (key[0] == 0) { + if(len < 3) + return NULL; + memmove(&int16, key+1, 2); + exp = ntohs(int16); + offset = 3; + } else { + exp = key[0]; + offset = 1; + } + + /* key length at least one */ + if(len < (size_t)offset + exp + 1) + return NULL; + + /* Exponent */ + exponent = BN_new(); + if(!exponent) return NULL; + (void) BN_bin2bn(key+offset, (int)exp, exponent); + offset += exp; + + /* Modulus */ + modulus = BN_new(); + if(!modulus) { + BN_free(exponent); + return NULL; + } + /* length of the buffer must match the key length! */ + (void) BN_bin2bn(key+offset, (int)(len - offset), modulus); + + rsa = RSA_new(); + if(!rsa) { + BN_free(exponent); + BN_free(modulus); + return NULL; + } +#ifndef S_SPLINT_S + rsa->n = modulus; + rsa->e = exponent; +#endif /* splint */ + + return rsa; +} + +#ifdef USE_GOST +EVP_PKEY* +gldns_gost2pkey_raw(unsigned char* key, size_t keylen) +{ + /* prefix header for X509 encoding */ + uint8_t asn[37] = { 0x30, 0x63, 0x30, 0x1c, 0x06, 0x06, 0x2a, 0x85, + 0x03, 0x02, 0x02, 0x13, 0x30, 0x12, 0x06, 0x07, 0x2a, 0x85, + 0x03, 0x02, 0x02, 0x23, 0x01, 0x06, 0x07, 0x2a, 0x85, 0x03, + 0x02, 0x02, 0x1e, 0x01, 0x03, 0x43, 0x00, 0x04, 0x40}; + unsigned char encoded[37+64]; + const unsigned char* pp; + if(keylen != 64) { + /* key wrong size */ + return NULL; + } + + /* create evp_key */ + memmove(encoded, asn, 37); + memmove(encoded+37, key, 64); + pp = (unsigned char*)&encoded[0]; + + return d2i_PUBKEY(NULL, &pp, (int)sizeof(encoded)); +} +#endif /* USE_GOST */ + +#ifdef USE_ECDSA +EVP_PKEY* +gldns_ecdsa2pkey_raw(unsigned char* key, size_t keylen, uint8_t algo) +{ + unsigned char buf[256+2]; /* sufficient for 2*384/8+1 */ + const unsigned char* pp = buf; + EVP_PKEY *evp_key; + EC_KEY *ec; + /* check length, which uncompressed must be 2 bignums */ + if(algo == GLDNS_ECDSAP256SHA256) { + if(keylen != 2*256/8) return NULL; + ec = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1); + } else if(algo == GLDNS_ECDSAP384SHA384) { + if(keylen != 2*384/8) return NULL; + ec = EC_KEY_new_by_curve_name(NID_secp384r1); + } else ec = NULL; + if(!ec) return NULL; + if(keylen+1 > sizeof(buf)) + return NULL; /* sanity check */ + /* prepend the 0x02 (from docs) (or actually 0x04 from implementation + * of openssl) for uncompressed data */ + buf[0] = POINT_CONVERSION_UNCOMPRESSED; + memmove(buf+1, key, keylen); + if(!o2i_ECPublicKey(&ec, &pp, (int)keylen+1)) { + EC_KEY_free(ec); + return NULL; + } + evp_key = EVP_PKEY_new(); + if(!evp_key) { + EC_KEY_free(ec); + return NULL; + } + if (!EVP_PKEY_assign_EC_KEY(evp_key, ec)) { + EVP_PKEY_free(evp_key); + EC_KEY_free(ec); + return NULL; + } + return evp_key; +} +#endif /* USE_ECDSA */ + +int +gldns_digest_evp(unsigned char* data, unsigned int len, unsigned char* dest, + const EVP_MD* md) +{ + EVP_MD_CTX* ctx; + ctx = EVP_MD_CTX_create(); + if(!ctx) + return 0; + if(!EVP_DigestInit_ex(ctx, md, NULL) || + !EVP_DigestUpdate(ctx, data, len) || + !EVP_DigestFinal_ex(ctx, dest, NULL)) { + EVP_MD_CTX_destroy(ctx); + return 0; + } + EVP_MD_CTX_destroy(ctx); + return 1; +} +#endif /* HAVE_SSL */ diff --git a/src/gldns/keyraw.h b/src/gldns/keyraw.h new file mode 100644 index 00000000..c0d33e60 --- /dev/null +++ b/src/gldns/keyraw.h @@ -0,0 +1,112 @@ +/* + * keyraw.h -- raw key and signature access and conversion + * + * Copyright (c) 2005-2008, NLnet Labs. All rights reserved. + * + * See LICENSE for the license. + * + */ + +/** + * \file + * + * raw key and signature access and conversion + * + * Since those functions heavily rely op cryptographic operations, + * this module is dependent on openssl. + * + */ + +#ifndef GLDNS_KEYRAW_H +#define GLDNS_KEYRAW_H + +#ifdef __cplusplus +extern "C" { +#endif +#if GLDNS_BUILD_CONFIG_HAVE_SSL +# include +# include +#endif /* GLDNS_BUILD_CONFIG_HAVE_SSL */ + +/** + * get the length of the keydata in bits + * \param[in] keydata the raw key data + * \param[in] len the length of the keydata + * \param[in] alg the cryptographic algorithm this is a key for + * \return the keysize in bits, or 0 on error + */ +size_t gldns_rr_dnskey_key_size_raw(const unsigned char *keydata, + const size_t len, int alg); + +/** + * Calculates keytag of DNSSEC key, operates on wireformat rdata. + * \param[in] key the key as uncompressed wireformat rdata. + * \param[in] keysize length of key data. + * \return the keytag + */ +uint16_t gldns_calc_keytag_raw(uint8_t* key, size_t keysize); + +#if GLDNS_BUILD_CONFIG_HAVE_SSL +/** + * Get the PKEY id for GOST, loads GOST into openssl as a side effect. + * Only available if GOST is compiled into the library and openssl. + * \return the gost id for EVP_CTX creation. + */ +int gldns_key_EVP_load_gost_id(void); + +/** Release the engine reference held for the GOST engine. */ +void gldns_key_EVP_unload_gost(void); + +/** + * Like gldns_key_buf2dsa, but uses raw buffer. + * \param[in] key the uncompressed wireformat of the key. + * \param[in] len length of key data + * \return a DSA * structure with the key material + */ +DSA *gldns_key_buf2dsa_raw(unsigned char* key, size_t len); + +/** + * Converts a holding buffer with key material to EVP PKEY in openssl. + * Only available if ldns was compiled with GOST. + * \param[in] key data to convert + * \param[in] keylen length of the key data + * \return the key or NULL on error. + */ +EVP_PKEY* gldns_gost2pkey_raw(unsigned char* key, size_t keylen); + +/** + * Converts a holding buffer with key material to EVP PKEY in openssl. + * Only available if ldns was compiled with ECDSA. + * \param[in] key data to convert + * \param[in] keylen length of the key data + * \param[in] algo precise algorithm to initialize ECC group values. + * \return the key or NULL on error. + */ +EVP_PKEY* gldns_ecdsa2pkey_raw(unsigned char* key, size_t keylen, uint8_t algo); + +/** + * Like gldns_key_buf2rsa, but uses raw buffer. + * \param[in] key the uncompressed wireformat of the key. + * \param[in] len length of key data + * \return a RSA * structure with the key material + */ +RSA *gldns_key_buf2rsa_raw(unsigned char* key, size_t len); + +/** + * Utility function to calculate hash using generic EVP_MD pointer. + * \param[in] data the data to hash. + * \param[in] len length of data. + * \param[out] dest the destination of the hash, must be large enough. + * \param[in] md the message digest to use. + * \return true if worked, false on failure. + */ +int gldns_digest_evp(unsigned char* data, unsigned int len, + unsigned char* dest, const EVP_MD* md); + +#endif /* GLDNS_BUILD_CONFIG_HAVE_SSL */ + +#ifdef __cplusplus +} +#endif + +#endif /* GLDNS_KEYRAW_H */ diff --git a/src/gldns/parse.c b/src/gldns/parse.c new file mode 100644 index 00000000..7e1e4534 --- /dev/null +++ b/src/gldns/parse.c @@ -0,0 +1,470 @@ +/* + * a generic (simple) parser. Use to parse rr's, private key + * information and /etc/resolv.conf files + * + * a Net::DNS like library for C + * LibDNS Team @ NLnet Labs + * (c) NLnet Labs, 2005-2006 + * See the file LICENSE for the license + */ +#include "config.h" +#include "gldns/parse.h" +#include "gldns/parseutil.h" +#include "gldns/sbuffer.h" + +#include +#include + +gldns_lookup_table gldns_directive_types[] = { + { GLDNS_DIR_TTL, "$TTL" }, + { GLDNS_DIR_ORIGIN, "$ORIGIN" }, + { GLDNS_DIR_INCLUDE, "$INCLUDE" }, + { 0, NULL } +}; + +/* add max_limit here? */ +ssize_t +gldns_fget_token(FILE *f, char *token, const char *delim, size_t limit) +{ + return gldns_fget_token_l(f, token, delim, limit, NULL); +} + +ssize_t +gldns_fget_token_l(FILE *f, char *token, const char *delim, size_t limit, int *line_nr) +{ + int c, prev_c; + int p; /* 0 -> no parenthese seen, >0 nr of ( seen */ + int com, quoted; + char *t; + size_t i; + const char *d; + const char *del; + + /* standard delimeters */ + if (!delim) { + /* from isspace(3) */ + del = GLDNS_PARSE_NORMAL; + } else { + del = delim; + } + + p = 0; + i = 0; + com = 0; + quoted = 0; + prev_c = 0; + t = token; + if (del[0] == '"') { + quoted = 1; + } + while ((c = getc(f)) != EOF) { + if (c == '\r') /* carriage return */ + c = ' '; + if (c == '(' && prev_c != '\\' && !quoted) { + /* this only counts for non-comments */ + if (com == 0) { + p++; + } + prev_c = c; + continue; + } + + if (c == ')' && prev_c != '\\' && !quoted) { + /* this only counts for non-comments */ + if (com == 0) { + p--; + } + prev_c = c; + continue; + } + + if (p < 0) { + /* more ) then ( - close off the string */ + *t = '\0'; + return 0; + } + + /* do something with comments ; */ + if (c == ';' && quoted == 0) { + if (prev_c != '\\') { + com = 1; + } + } + if (c == '\"' && com == 0 && prev_c != '\\') { + quoted = 1 - quoted; + } + + if (c == '\n' && com != 0) { + /* comments */ + com = 0; + *t = ' '; + if (line_nr) { + *line_nr = *line_nr + 1; + } + if (p == 0 && i > 0) { + goto tokenread; + } else { + prev_c = c; + continue; + } + } + + if (com == 1) { + *t = ' '; + prev_c = c; + continue; + } + + if (c == '\n' && p != 0 && t > token) { + /* in parentheses */ + if (line_nr) { + *line_nr = *line_nr + 1; + } + *t++ = ' '; + prev_c = c; + continue; + } + + /* check if we hit the delim */ + for (d = del; *d; d++) { + if (c == *d && i > 0 && prev_c != '\\' && p == 0) { + if (c == '\n' && line_nr) { + *line_nr = *line_nr + 1; + } + goto tokenread; + } + } + if (c != '\0' && c != '\n') { + i++; + } + if (limit > 0 && (i >= limit || (size_t)(t-token) >= limit)) { + *t = '\0'; + return -1; + } + if (c != '\0' && c != '\n') { + *t++ = c; + } + if (c == '\\' && prev_c == '\\') + prev_c = 0; + else prev_c = c; + } + *t = '\0'; + if (c == EOF) { + return (ssize_t)i; + } + + if (i == 0) { + /* nothing read */ + return -1; + } + if (p != 0) { + return -1; + } + return (ssize_t)i; + +tokenread: + if(*del == '"') + /* do not skip over quotes after the string, they are part + * of the next string. But skip over whitespace (if needed)*/ + gldns_fskipcs_l(f, del+1, line_nr); + else gldns_fskipcs_l(f, del, line_nr); + *t = '\0'; + if (p != 0) { + return -1; + } + + return (ssize_t)i; +} + +ssize_t +gldns_fget_keyword_data(FILE *f, const char *keyword, const char *k_del, char *data, + const char *d_del, size_t data_limit) +{ + return gldns_fget_keyword_data_l(f, keyword, k_del, data, d_del, + data_limit, NULL); +} + +ssize_t +gldns_fget_keyword_data_l(FILE *f, const char *keyword, const char *k_del, char *data, + const char *d_del, size_t data_limit, int *line_nr) +{ + /* we assume: keyword|sep|data */ + char *fkeyword; + ssize_t i; + + if(strlen(keyword) >= GLDNS_MAX_KEYWORDLEN) + return -1; + fkeyword = (char*)malloc(GLDNS_MAX_KEYWORDLEN); + if(!fkeyword) + return -1; + + i = gldns_fget_token(f, fkeyword, k_del, GLDNS_MAX_KEYWORDLEN); + if(i==0 || i==-1) { + free(fkeyword); + return -1; + } + + /* case??? i instead of strlen? */ + if (strncmp(fkeyword, keyword, GLDNS_MAX_KEYWORDLEN - 1) == 0) { + /* whee! */ + /* printf("%s\n%s\n", "Matching keyword", fkeyword); */ + i = gldns_fget_token_l(f, data, d_del, data_limit, line_nr); + free(fkeyword); + return i; + } else { + /*printf("no match for %s (read: %s)\n", keyword, fkeyword);*/ + free(fkeyword); + return -1; + } +} + +int +gldns_bgetc(gldns_buffer *buffer) +{ + if (!gldns_buffer_available_at(buffer, buffer->_position, sizeof(uint8_t))) { + gldns_buffer_set_position(buffer, gldns_buffer_limit(buffer)); + /* gldns_buffer_rewind(buffer);*/ + return EOF; + } + return (int)gldns_buffer_read_u8(buffer); +} + +ssize_t +gldns_bget_token(gldns_buffer *b, char *token, const char *delim, size_t limit) +{ + return gldns_bget_token_par(b, token, delim, limit, NULL, NULL); +} + +ssize_t +gldns_bget_token_par(gldns_buffer *b, char *token, const char *delim, + size_t limit, int* par, const char* skipw) +{ + int c, lc; + int p; /* 0 -> no parenthese seen, >0 nr of ( seen */ + int com, quoted; + char *t; + size_t i; + const char *d; + const char *del; + + /* standard delimiters */ + if (!delim) { + /* from isspace(3) */ + del = GLDNS_PARSE_NORMAL; + } else { + del = delim; + } + + p = (par?*par:0); + i = 0; + com = 0; + quoted = 0; + t = token; + lc = 0; + if (del[0] == '"') { + quoted = 1; + } + + while ((c = gldns_bgetc(b)) != EOF) { + if (c == '\r') /* carriage return */ + c = ' '; + if (c == '(' && lc != '\\' && !quoted) { + /* this only counts for non-comments */ + if (com == 0) { + if(par) (*par)++; + p++; + } + lc = c; + continue; + } + + if (c == ')' && lc != '\\' && !quoted) { + /* this only counts for non-comments */ + if (com == 0) { + if(par) (*par)--; + p--; + } + lc = c; + continue; + } + + if (p < 0) { + /* more ) then ( */ + *t = '\0'; + return 0; + } + + /* do something with comments ; */ + if (c == ';' && quoted == 0) { + if (lc != '\\') { + com = 1; + } + } + if (c == '"' && com == 0 && lc != '\\') { + quoted = 1 - quoted; + } + + if (c == '\n' && com != 0) { + /* comments */ + com = 0; + *t = ' '; + lc = c; + continue; + } + + if (com == 1) { + *t = ' '; + lc = c; + continue; + } + + if (c == '\n' && p != 0) { + /* in parentheses */ + /* do not write ' ' if we want to skip spaces */ + if(!(skipw && (strchr(skipw, c)||strchr(skipw, ' ')))) + *t++ = ' '; + lc = c; + continue; + } + + /* check to skip whitespace at start, but also after ( */ + if(skipw && i==0 && !com && !quoted && lc != '\\') { + if(strchr(skipw, c)) { + lc = c; + continue; + } + } + + /* check if we hit the delim */ + for (d = del; *d; d++) { + /* we can only exit if no parens or user tracks them */ + if (c == *d && lc != '\\' && (p == 0 || par)) { + goto tokenread; + } + } + + i++; + if (limit > 0 && (i >= limit || (size_t)(t-token) >= limit)) { + *t = '\0'; + return -1; + } + *t++ = c; + + if (c == '\\' && lc == '\\') { + lc = 0; + } else { + lc = c; + } + } + *t = '\0'; + if (i == 0) { + /* nothing read */ + return -1; + } + if (!par && p != 0) { + return -1; + } + return (ssize_t)i; + +tokenread: + if(*del == '"') + /* do not skip over quotes after the string, they are part + * of the next string. But skip over whitespace (if needed)*/ + gldns_bskipcs(b, del+1); + else gldns_bskipcs(b, del); + *t = '\0'; + + if (!par && p != 0) { + return -1; + } + return (ssize_t)i; +} + + +void +gldns_bskipcs(gldns_buffer *buffer, const char *s) +{ + int found; + char c; + const char *d; + + while(gldns_buffer_available_at(buffer, buffer->_position, sizeof(char))) { + c = (char) gldns_buffer_read_u8_at(buffer, buffer->_position); + found = 0; + for (d = s; *d; d++) { + if (*d == c) { + found = 1; + } + } + if (found && buffer->_limit > buffer->_position) { + buffer->_position += sizeof(char); + } else { + return; + } + } +} + +void +gldns_fskipcs(FILE *fp, const char *s) +{ + gldns_fskipcs_l(fp, s, NULL); +} + +void +gldns_fskipcs_l(FILE *fp, const char *s, int *line_nr) +{ + int found; + int c; + const char *d; + + while ((c = fgetc(fp)) != EOF) { + if (line_nr && c == '\n') { + *line_nr = *line_nr + 1; + } + found = 0; + for (d = s; *d; d++) { + if (*d == c) { + found = 1; + } + } + if (!found) { + /* with getc, we've read too far */ + ungetc(c, fp); + return; + } + } +} + +ssize_t +gldns_bget_keyword_data(gldns_buffer *b, const char *keyword, const char *k_del, char +*data, const char *d_del, size_t data_limit) +{ + /* we assume: keyword|sep|data */ + char *fkeyword; + ssize_t i; + + if(strlen(keyword) >= GLDNS_MAX_KEYWORDLEN) + return -1; + fkeyword = (char*)malloc(GLDNS_MAX_KEYWORDLEN); + if(!fkeyword) + return -1; /* out of memory */ + + i = gldns_bget_token(b, fkeyword, k_del, data_limit); + if(i==0 || i==-1) { + free(fkeyword); + return -1; /* nothing read */ + } + + /* case??? */ + if (strncmp(fkeyword, keyword, strlen(keyword)) == 0) { + free(fkeyword); + /* whee, the match! */ + /* retrieve it's data */ + i = gldns_bget_token(b, data, d_del, 0); + return i; + } else { + free(fkeyword); + return -1; + } +} + diff --git a/src/gldns/parse.h b/src/gldns/parse.h new file mode 100644 index 00000000..a62efaf5 --- /dev/null +++ b/src/gldns/parse.h @@ -0,0 +1,184 @@ +/* + * parse.h + * + * a Net::DNS like library for C + * LibDNS Team @ NLnet Labs + * (c) NLnet Labs, 2005-2006 + * See the file LICENSE for the license + */ + +#ifndef GLDNS_PARSE_H +#define GLDNS_PARSE_H + +struct gldns_buffer; + +#ifdef __cplusplus +extern "C" { +#endif + +#define GLDNS_PARSE_SKIP_SPACE "\f\n\r\v" +#define GLDNS_PARSE_NORMAL " \f\n\r\t\v" +#define GLDNS_PARSE_NO_NL " \t" +#define GLDNS_MAX_LINELEN 10230 +#define GLDNS_MAX_KEYWORDLEN 32 + + +/** + * \file + * + * Contains some low-level parsing functions, mostly used in the _frm_str + * family of functions. + */ + +/** + * different type of directives in zone files + * We now deal with $TTL, $ORIGIN and $INCLUDE. + * The latter is not implemented in ldns (yet) + */ +enum gldns_enum_directive +{ + GLDNS_DIR_TTL, + GLDNS_DIR_ORIGIN, + GLDNS_DIR_INCLUDE +}; +typedef enum gldns_enum_directive gldns_directive; + +/** + * returns a token/char from the stream F. + * This function deals with ( and ) in the stream, + * and ignores them when encountered + * \param[in] *f the file to read from + * \param[out] *token the read token is put here + * \param[in] *delim chars at which the parsing should stop + * \param[in] *limit how much to read. If 0 the builtin maximum is used + * \return 0 on error of EOF of the stream F. Otherwise return the length of what is read + */ +ssize_t gldns_fget_token(FILE *f, char *token, const char *delim, size_t limit); + +/** + * returns a token/char from the stream F. + * This function deals with ( and ) in the stream, + * and ignores when it finds them. + * \param[in] *f the file to read from + * \param[out] *token the token is put here + * \param[in] *delim chars at which the parsing should stop + * \param[in] *limit how much to read. If 0 use builtin maximum + * \param[in] line_nr pointer to an integer containing the current line number (for debugging purposes) + * \return 0 on error of EOF of F otherwise return the length of what is read + */ +ssize_t gldns_fget_token_l(FILE *f, char *token, const char *delim, size_t limit, int *line_nr); + +/** + * returns a token/char from the buffer b. + * This function deals with ( and ) in the buffer, + * and ignores when it finds them. + * \param[in] *b the buffer to read from + * \param[out] *token the token is put here + * \param[in] *delim chars at which the parsing should stop + * \param[in] *limit how much to read. If 0 the builtin maximum is used + * \param[in] *par if you pass nonNULL, set to 0 on first call, the parenthesis + * state is stored in it, for use on next call. User must check it is back + * to zero after last bget in string (for parse error). If you pass NULL, + * the entire parenthesized string is read in. + * \param[in] skipw string with whitespace to skip before the start of the + * token, like " ", or " \t", or NULL for none. + * \returns 0 on error of EOF of b. Otherwise return the length of what is read + */ +ssize_t gldns_bget_token_par(struct gldns_buffer *b, char *token, const char *delim, size_t limit, int* par, const char* skipw); + +/** + * returns a token/char from the buffer b. + * This function deals with ( and ) in the buffer, + * and ignores when it finds them. + * \param[in] *b the buffer to read from + * \param[out] *token the token is put here + * \param[in] *delim chars at which the parsing should stop + * \param[in] *limit how much to read. If 0 the builtin maximum is used + * \returns 0 on error of EOF of b. Otherwise return the length of what is read + */ +ssize_t gldns_bget_token(struct gldns_buffer *b, char *token, const char *delim, size_t limit); + +/* + * searches for keyword and delim in a file. Gives everything back + * after the keyword + k_del until we hit d_del + * \param[in] f file pointer to read from + * \param[in] keyword keyword to look for + * \param[in] k_del keyword delimeter + * \param[out] data the data found + * \param[in] d_del the data delimeter + * \param[in] data_limit maximum size the the data buffer + * \return the number of character read + */ +ssize_t gldns_fget_keyword_data(FILE *f, const char *keyword, const char *k_del, char *data, const char *d_del, size_t data_limit); + +/* + * searches for keyword and delim. Gives everything back + * after the keyword + k_del until we hit d_del + * \param[in] f file pointer to read from + * \param[in] keyword keyword to look for + * \param[in] k_del keyword delimeter + * \param[out] data the data found + * \param[in] d_del the data delimeter + * \param[in] data_limit maximum size the the data buffer + * \param[in] line_nr pointer to an integer containing the current line number (for +debugging purposes) + * \return the number of character read + */ +ssize_t gldns_fget_keyword_data_l(FILE *f, const char *keyword, const char *k_del, char *data, const char *d_del, size_t data_limit, int *line_nr); + +/* + * searches for keyword and delim in a buffer. Gives everything back + * after the keyword + k_del until we hit d_del + * \param[in] b buffer pointer to read from + * \param[in] keyword keyword to look for + * \param[in] k_del keyword delimeter + * \param[out] data the data found + * \param[in] d_del the data delimeter + * \param[in] data_limit maximum size the the data buffer + * \return the number of character read + */ +ssize_t gldns_bget_keyword_data(struct gldns_buffer *b, const char *keyword, const char *k_del, char *data, const char *d_del, size_t data_limit); + +/** + * returns the next character from a buffer. Advances the position pointer with 1. + * When end of buffer is reached returns EOF. This is the buffer's equivalent + * for getc(). + * \param[in] *buffer buffer to read from + * \return EOF on failure otherwise return the character + */ +int gldns_bgetc(struct gldns_buffer *buffer); + +/** + * skips all of the characters in the given string in the buffer, moving + * 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 gldns_bskipcs(struct gldns_buffer *buffer, const char *s); + +/** + * skips all of the characters in the given string in the fp, moving + * 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 gldns_fskipcs(FILE *fp, const char *s); + + +/** + * skips all of the characters in the given string in the fp, moving + * the position to the first character that is not in *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 gldns_fskipcs_l(FILE *fp, const char *s, int *line_nr); + +#ifdef __cplusplus +} +#endif + +#endif /* GLDNS_PARSE_H */ diff --git a/src/gldns/parseutil.c b/src/gldns/parseutil.c new file mode 100644 index 00000000..ac6d3fef --- /dev/null +++ b/src/gldns/parseutil.c @@ -0,0 +1,726 @@ +/* + * parseutil.c - parse utilities for string and wire conversion + * + * (c) NLnet Labs, 2004-2006 + * + * See the file LICENSE for the license + */ +/** + * \file + * + * Utility functions for parsing, base32(DNS variant) and base64 encoding + * and decoding, Hex, Time units, Escape codes. + */ + +#include "config.h" +#include "gldns/parseutil.h" +#include +#include +#include + +gldns_lookup_table * +gldns_lookup_by_name(gldns_lookup_table *table, const char *name) +{ + while (table->name != NULL) { + if (strcasecmp(name, table->name) == 0) + return table; + table++; + } + return NULL; +} + +gldns_lookup_table * +gldns_lookup_by_id(gldns_lookup_table *table, int id) +{ + while (table->name != NULL) { + if (table->id == id) + return table; + table++; + } + return NULL; +} + +/* Number of days per month (except for February in leap years). */ +static const int mdays[] = { + 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 +}; + +#define GLDNS_MOD(x,y) (((x) % (y) < 0) ? ((x) % (y) + (y)) : ((x) % (y))) +#define GLDNS_DIV(x,y) (((x) % (y) < 0) ? ((x) / (y) - 1 ) : ((x) / (y))) + +static int +is_leap_year(int year) +{ + return GLDNS_MOD(year, 4) == 0 && (GLDNS_MOD(year, 100) != 0 + || GLDNS_MOD(year, 400) == 0); +} + +static int +leap_days(int y1, int y2) +{ + --y1; + --y2; + return (GLDNS_DIV(y2, 4) - GLDNS_DIV(y1, 4)) - + (GLDNS_DIV(y2, 100) - GLDNS_DIV(y1, 100)) + + (GLDNS_DIV(y2, 400) - GLDNS_DIV(y1, 400)); +} + +/* + * Code adapted from Python 2.4.1 sources (Lib/calendar.py). + */ +time_t +gldns_mktime_from_utc(const struct tm *tm) +{ + int year = 1900 + tm->tm_year; + time_t days = 365 * ((time_t) year - 1970) + leap_days(1970, year); + time_t hours; + time_t minutes; + time_t seconds; + int i; + + for (i = 0; i < tm->tm_mon; ++i) { + days += mdays[i]; + } + if (tm->tm_mon > 1 && is_leap_year(year)) { + ++days; + } + days += tm->tm_mday - 1; + + hours = days * 24 + tm->tm_hour; + minutes = hours * 60 + tm->tm_min; + seconds = minutes * 60 + tm->tm_sec; + + return seconds; +} + +#if SIZEOF_TIME_T <= 4 + +static void +gldns_year_and_yday_from_days_since_epoch(int64_t days, struct tm *result) +{ + int year = 1970; + int new_year; + + while (days < 0 || days >= (int64_t) (is_leap_year(year) ? 366 : 365)) { + new_year = year + (int) GLDNS_DIV(days, 365); + days -= (new_year - year) * 365; + days -= leap_days(year, new_year); + year = new_year; + } + result->tm_year = year; + result->tm_yday = (int) days; +} + +/* Number of days per month in a leap year. */ +static const int leap_year_mdays[] = { + 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 +}; + +static void +gldns_mon_and_mday_from_year_and_yday(struct tm *result) +{ + int idays = result->tm_yday; + const int *mon_lengths = is_leap_year(result->tm_year) ? + leap_year_mdays : mdays; + + result->tm_mon = 0; + while (idays >= mon_lengths[result->tm_mon]) { + idays -= mon_lengths[result->tm_mon++]; + } + result->tm_mday = idays + 1; +} + +static void +gldns_wday_from_year_and_yday(struct tm *result) +{ + result->tm_wday = 4 /* 1-1-1970 was a thursday */ + + GLDNS_MOD((result->tm_year - 1970), 7) * GLDNS_MOD(365, 7) + + leap_days(1970, result->tm_year) + + result->tm_yday; + result->tm_wday = GLDNS_MOD(result->tm_wday, 7); + if (result->tm_wday < 0) { + result->tm_wday += 7; + } +} + +static struct tm * +gldns_gmtime64_r(int64_t clock, struct tm *result) +{ + result->tm_isdst = 0; + result->tm_sec = (int) GLDNS_MOD(clock, 60); + clock = GLDNS_DIV(clock, 60); + result->tm_min = (int) GLDNS_MOD(clock, 60); + clock = GLDNS_DIV(clock, 60); + result->tm_hour = (int) GLDNS_MOD(clock, 24); + clock = GLDNS_DIV(clock, 24); + + gldns_year_and_yday_from_days_since_epoch(clock, result); + gldns_mon_and_mday_from_year_and_yday(result); + gldns_wday_from_year_and_yday(result); + result->tm_year -= 1900; + + return result; +} + +#endif /* SIZEOF_TIME_T <= 4 */ + +static int64_t +gldns_serial_arithmitics_time(int32_t time, time_t now) +{ + int32_t offset = time - (int32_t) now; + return (int64_t) now + offset; +} + +struct tm * +gldns_serial_arithmitics_gmtime_r(int32_t time, time_t now, struct tm *result) +{ +#if SIZEOF_TIME_T <= 4 + int64_t secs_since_epoch = gldns_serial_arithmitics_time(time, now); + return gldns_gmtime64_r(secs_since_epoch, result); +#else + time_t secs_since_epoch = gldns_serial_arithmitics_time(time, now); + return gmtime_r(&secs_since_epoch, result); +#endif +} + +int +gldns_hexdigit_to_int(char ch) +{ + switch (ch) { + case '0': return 0; + case '1': return 1; + case '2': return 2; + case '3': return 3; + case '4': return 4; + case '5': return 5; + case '6': return 6; + case '7': return 7; + case '8': return 8; + case '9': return 9; + case 'a': case 'A': return 10; + case 'b': case 'B': return 11; + case 'c': case 'C': return 12; + case 'd': case 'D': return 13; + case 'e': case 'E': return 14; + case 'f': case 'F': return 15; + default: + return -1; + } +} + +uint32_t +gldns_str2period(const char *nptr, const char **endptr) +{ + int sign = 0; + uint32_t i = 0; + uint32_t seconds = 0; + + for(*endptr = nptr; **endptr; (*endptr)++) { + switch (**endptr) { + case ' ': + case '\t': + break; + case '-': + if(sign == 0) { + sign = -1; + } else { + return seconds; + } + break; + case '+': + if(sign == 0) { + sign = 1; + } else { + return seconds; + } + break; + case 's': + case 'S': + seconds += i; + i = 0; + break; + case 'm': + case 'M': + seconds += i * 60; + i = 0; + break; + case 'h': + case 'H': + seconds += i * 60 * 60; + i = 0; + break; + case 'd': + case 'D': + seconds += i * 60 * 60 * 24; + i = 0; + break; + case 'w': + case 'W': + seconds += i * 60 * 60 * 24 * 7; + i = 0; + break; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + i *= 10; + i += (**endptr - '0'); + break; + default: + seconds += i; + /* disregard signedness */ + return seconds; + } + } + seconds += i; + /* disregard signedness */ + return seconds; +} + +int +gldns_parse_escape(uint8_t *ch_p, const char** str_p) +{ + uint16_t val; + + if ((*str_p)[0] && isdigit((*str_p)[0]) && + (*str_p)[1] && isdigit((*str_p)[1]) && + (*str_p)[2] && isdigit((*str_p)[2])) { + + val = (uint16_t)(((*str_p)[0] - '0') * 100 + + ((*str_p)[1] - '0') * 10 + + ((*str_p)[2] - '0')); + + if (val > 255) { + goto error; + } + *ch_p = (uint8_t)val; + *str_p += 3; + return 1; + + } else if ((*str_p)[0] && !isdigit((*str_p)[0])) { + + *ch_p = (uint8_t)*(*str_p)++; + return 1; + } +error: + *str_p = NULL; + return 0; /* GLDNS_WIREPARSE_ERR_SYNTAX_BAD_ESCAPE */ +} + +/** parse one character, with escape codes */ +int +gldns_parse_char(uint8_t *ch_p, const char** str_p) +{ + switch (**str_p) { + + case '\0': return 0; + + case '\\': *str_p += 1; + return gldns_parse_escape(ch_p, str_p); + + default: *ch_p = (uint8_t)*(*str_p)++; + return 1; + } +} + +size_t gldns_b32_ntop_calculate_size(size_t src_data_length) +{ + return src_data_length == 0 ? 0 : ((src_data_length - 1) / 5 + 1) * 8; +} + +size_t gldns_b32_ntop_calculate_size_no_padding(size_t src_data_length) +{ + return ((src_data_length + 3) * 8 / 5) - 4; +} + +static int +gldns_b32_ntop_base(const uint8_t* src, size_t src_sz, char* dst, size_t dst_sz, + int extended_hex, int add_padding) +{ + size_t ret_sz; + const char* b32 = extended_hex ? "0123456789abcdefghijklmnopqrstuv" + : "abcdefghijklmnopqrstuvwxyz234567"; + + size_t c = 0; /* c is used to carry partial base32 character over + * byte boundaries for sizes with a remainder. + * (i.e. src_sz % 5 != 0) + */ + + ret_sz = add_padding ? gldns_b32_ntop_calculate_size(src_sz) + : gldns_b32_ntop_calculate_size_no_padding(src_sz); + + /* Do we have enough space? */ + if (dst_sz < ret_sz + 1) + return -1; + + /* We know the size; terminate the string */ + dst[ret_sz] = '\0'; + + /* First process all chunks of five */ + while (src_sz >= 5) { + /* 00000... ........ ........ ........ ........ */ + dst[0] = b32[(src[0] ) >> 3]; + + /* .....111 11...... ........ ........ ........ */ + dst[1] = b32[(src[0] & 0x07) << 2 | src[1] >> 6]; + + /* ........ ..22222. ........ ........ ........ */ + dst[2] = b32[(src[1] & 0x3e) >> 1]; + + /* ........ .......3 3333.... ........ ........ */ + dst[3] = b32[(src[1] & 0x01) << 4 | src[2] >> 4]; + + /* ........ ........ ....4444 4....... ........ */ + dst[4] = b32[(src[2] & 0x0f) << 1 | src[3] >> 7]; + + /* ........ ........ ........ .55555.. ........ */ + dst[5] = b32[(src[3] & 0x7c) >> 2]; + + /* ........ ........ ........ ......66 666..... */ + dst[6] = b32[(src[3] & 0x03) << 3 | src[4] >> 5]; + + /* ........ ........ ........ ........ ...77777 */ + dst[7] = b32[(src[4] & 0x1f) ]; + + src_sz -= 5; + src += 5; + dst += 8; + } + /* Process what remains */ + switch (src_sz) { + case 4: /* ........ ........ ........ ......66 666..... */ + dst[6] = b32[(src[3] & 0x03) << 3]; + + /* ........ ........ ........ .55555.. ........ */ + dst[5] = b32[(src[3] & 0x7c) >> 2]; + + /* ........ ........ ....4444 4....... ........ */ + c = src[3] >> 7 ; + case 3: dst[4] = b32[(src[2] & 0x0f) << 1 | c]; + + /* ........ .......3 3333.... ........ ........ */ + c = src[2] >> 4 ; + case 2: dst[3] = b32[(src[1] & 0x01) << 4 | c]; + + /* ........ ..22222. ........ ........ ........ */ + dst[2] = b32[(src[1] & 0x3e) >> 1]; + + /* .....111 11...... ........ ........ ........ */ + c = src[1] >> 6 ; + case 1: dst[1] = b32[(src[0] & 0x07) << 2 | c]; + + /* 00000... ........ ........ ........ ........ */ + dst[0] = b32[ src[0] >> 3]; + } + /* Add padding */ + if (add_padding) { + switch (src_sz) { + case 1: dst[2] = '='; + dst[3] = '='; + case 2: dst[4] = '='; + case 3: dst[5] = '='; + dst[6] = '='; + case 4: dst[7] = '='; + } + } + return (int)ret_sz; +} + +int +gldns_b32_ntop(const uint8_t* src, size_t src_sz, char* dst, size_t dst_sz) +{ + return gldns_b32_ntop_base(src, src_sz, dst, dst_sz, 0, 1); +} + +int +gldns_b32_ntop_extended_hex(const uint8_t* src, size_t src_sz, + char* dst, size_t dst_sz) +{ + return gldns_b32_ntop_base(src, src_sz, dst, dst_sz, 1, 1); +} + +size_t gldns_b32_pton_calculate_size(size_t src_text_length) +{ + return src_text_length * 5 / 8; +} + +static int +gldns_b32_pton_base(const char* src, size_t src_sz, uint8_t* dst, size_t dst_sz, + int extended_hex, int check_padding) +{ + size_t i = 0; + char ch = '\0'; + uint8_t buf[8]; + uint8_t* start = dst; + + while (src_sz) { + /* Collect 8 characters in buf (if possible) */ + for (i = 0; i < 8; i++) { + + do { + ch = *src++; + --src_sz; + + } while (isspace(ch) && src_sz > 0); + + if (ch == '=' || ch == '\0') + break; + + else if (extended_hex) + + if (ch >= '0' && ch <= '9') + buf[i] = (uint8_t)ch - '0'; + else if (ch >= 'a' && ch <= 'v') + buf[i] = (uint8_t)ch - 'a' + 10; + else if (ch >= 'A' && ch <= 'V') + buf[i] = (uint8_t)ch - 'A' + 10; + else + return -1; + + else if (ch >= 'a' && ch <= 'z') + buf[i] = (uint8_t)ch - 'a'; + else if (ch >= 'A' && ch <= 'Z') + buf[i] = (uint8_t)ch - 'A'; + else if (ch >= '2' && ch <= '7') + buf[i] = (uint8_t)ch - '2' + 26; + else + return -1; + } + /* Less that 8 characters. We're done. */ + if (i < 8) + break; + + /* Enough space available at the destination? */ + if (dst_sz < 5) + return -1; + + /* 00000... ........ ........ ........ ........ */ + /* .....111 11...... ........ ........ ........ */ + dst[0] = buf[0] << 3 | buf[1] >> 2; + + /* .....111 11...... ........ ........ ........ */ + /* ........ ..22222. ........ ........ ........ */ + /* ........ .......3 3333.... ........ ........ */ + dst[1] = buf[1] << 6 | buf[2] << 1 | buf[3] >> 4; + + /* ........ .......3 3333.... ........ ........ */ + /* ........ ........ ....4444 4....... ........ */ + dst[2] = buf[3] << 4 | buf[4] >> 1; + + /* ........ ........ ....4444 4....... ........ */ + /* ........ ........ ........ .55555.. ........ */ + /* ........ ........ ........ ......66 666..... */ + dst[3] = buf[4] << 7 | buf[5] << 2 | buf[6] >> 3; + + /* ........ ........ ........ ......66 666..... */ + /* ........ ........ ........ ........ ...77777 */ + dst[4] = buf[6] << 5 | buf[7]; + + dst += 5; + dst_sz -= 5; + } + /* Not ending on a eight byte boundary? */ + if (i > 0 && i < 8) { + + /* Enough space available at the destination? */ + if (dst_sz < (i + 1) / 2) + return -1; + + switch (i) { + case 7: /* ........ ........ ........ ......66 666..... */ + /* ........ ........ ........ .55555.. ........ */ + /* ........ ........ ....4444 4....... ........ */ + dst[3] = buf[4] << 7 | buf[5] << 2 | buf[6] >> 3; + + case 5: /* ........ ........ ....4444 4....... ........ */ + /* ........ .......3 3333.... ........ ........ */ + dst[2] = buf[3] << 4 | buf[4] >> 1; + + case 4: /* ........ .......3 3333.... ........ ........ */ + /* ........ ..22222. ........ ........ ........ */ + /* .....111 11...... ........ ........ ........ */ + dst[1] = buf[1] << 6 | buf[2] << 1 | buf[3] >> 4; + + case 2: /* .....111 11...... ........ ........ ........ */ + /* 00000... ........ ........ ........ ........ */ + dst[0] = buf[0] << 3 | buf[1] >> 2; + + break; + + default: + return -1; + } + dst += (i + 1) / 2; + + if (check_padding) { + /* Check remaining padding characters */ + if (ch != '=') + return -1; + + /* One down, 8 - i - 1 more to come... */ + for (i = 8 - i - 1; i > 0; i--) { + + do { + if (src_sz == 0) + return -1; + ch = *src++; + src_sz--; + + } while (isspace(ch)); + + if (ch != '=') + return -1; + } + } + } + return dst - start; +} + +int +gldns_b32_pton(const char* src, size_t src_sz, uint8_t* dst, size_t dst_sz) +{ + return gldns_b32_pton_base(src, src_sz, dst, dst_sz, 0, 1); +} + +int +gldns_b32_pton_extended_hex(const char* src, size_t src_sz, + uint8_t* dst, size_t dst_sz) +{ + return gldns_b32_pton_base(src, src_sz, dst, dst_sz, 1, 1); +} + +size_t gldns_b64_ntop_calculate_size(size_t srcsize) +{ + return ((((srcsize + 2) / 3) * 4) + 1); +} + +/* RFC 1521, section 5.2. + * + * The encoding process represents 24-bit groups of input bits as output + * strings of 4 encoded characters. Proceeding from left to right, a + * 24-bit input group is formed by concatenating 3 8-bit input groups. + * These 24 bits are then treated as 4 concatenated 6-bit groups, each + * of which is translated into a single digit in the base64 alphabet. + * + * This routine does not insert spaces or linebreaks after 76 characters. + */ +int gldns_b64_ntop(uint8_t const *src, size_t srclength, + char *target, size_t targsize) +{ + const char* b64 = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + const char pad64 = '='; + size_t i = 0, o = 0; + if(targsize < gldns_b64_ntop_calculate_size(srclength)) + return -1; + /* whole chunks: xxxxxxyy yyyyzzzz zzwwwwww */ + while(i+3 <= srclength) { + if(o+4 > targsize) return -1; + target[o] = b64[src[i] >> 2]; + target[o+1] = b64[ ((src[i]&0x03)<<4) | (src[i+1]>>4) ]; + target[o+2] = b64[ ((src[i+1]&0x0f)<<2) | (src[i+2]>>6) ]; + target[o+3] = b64[ (src[i+2]&0x3f) ]; + i += 3; + o += 4; + } + /* remainder */ + switch(srclength - i) { + case 2: + /* two at end, converted into A B C = */ + target[o] = b64[src[i] >> 2]; + target[o+1] = b64[ ((src[i]&0x03)<<4) | (src[i+1]>>4) ]; + target[o+2] = b64[ ((src[i+1]&0x0f)<<2) ]; + target[o+3] = pad64; + i += 2; + o += 4; + break; + case 1: + /* one at end, converted into A B = = */ + target[o] = b64[src[i] >> 2]; + target[o+1] = b64[ ((src[i]&0x03)<<4) ]; + target[o+2] = pad64; + target[o+3] = pad64; + i += 1; + o += 4; + break; + case 0: + default: + /* nothing */ + break; + } + /* assert: i == srclength */ + if(o+1 > targsize) return -1; + target[o] = 0; + return (int)o; +} + +size_t gldns_b64_pton_calculate_size(size_t srcsize) +{ + return (((((srcsize + 3) / 4) * 3)) + 1); +} + +int gldns_b64_pton(char const *src, uint8_t *target, size_t targsize) +{ + const uint8_t pad64 = 64; /* is 64th in the b64 array */ + const char* s = src; + uint8_t in[4]; + size_t o = 0, incount = 0; + + while(*s) { + /* skip any character that is not base64 */ + /* conceptually we do: + const char* b64 = pad'=' is appended to array + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; + const char* d = strchr(b64, *s++); + and use d-b64; + */ + char d = *s++; + if(d <= 'Z' && d >= 'A') + d -= 'A'; + else if(d <= 'z' && d >= 'a') + d = d - 'a' + 26; + else if(d <= '9' && d >= '0') + d = d - '0' + 52; + else if(d == '+') + d = 62; + else if(d == '/') + d = 63; + else if(d == '=') + d = 64; + else continue; + in[incount++] = (uint8_t)d; + if(incount != 4) + continue; + /* process whole block of 4 characters into 3 output bytes */ + if(in[3] == pad64 && in[2] == pad64) { /* A B = = */ + if(o+1 > targsize) + return -1; + target[o] = (in[0]<<2) | ((in[1]&0x30)>>4); + o += 1; + break; /* we are done */ + } else if(in[3] == pad64) { /* A B C = */ + if(o+2 > targsize) + return -1; + target[o] = (in[0]<<2) | ((in[1]&0x30)>>4); + target[o+1]= ((in[1]&0x0f)<<4) | ((in[2]&0x3c)>>2); + o += 2; + break; /* we are done */ + } else { + if(o+3 > targsize) + return -1; + /* write xxxxxxyy yyyyzzzz zzwwwwww */ + target[o] = (in[0]<<2) | ((in[1]&0x30)>>4); + target[o+1]= ((in[1]&0x0f)<<4) | ((in[2]&0x3c)>>2); + target[o+2]= ((in[2]&0x03)<<6) | in[3]; + o += 3; + } + incount = 0; + } + return (int)o; +} diff --git a/src/gldns/parseutil.h b/src/gldns/parseutil.h new file mode 100644 index 00000000..d4a03ad3 --- /dev/null +++ b/src/gldns/parseutil.h @@ -0,0 +1,148 @@ +/* + * parseutil.h - parse utilities for string and wire conversion + * + * (c) NLnet Labs, 2004 + * + * See the file LICENSE for the license + */ +/** + * \file + * + * Utility functions for parsing, base32(DNS variant) and base64 encoding + * and decoding, Hex, Time units, Escape codes. + */ + +#ifndef GLDNS_PARSEUTIL_H +#define GLDNS_PARSEUTIL_H +struct tm; + +/** + * A general purpose lookup table + * + * Lookup tables are arrays of (id, name) pairs, + * So you can for instance lookup the RCODE 3, which is "NXDOMAIN", + * and vice versa. The lookup tables themselves are defined wherever needed, + * for instance in host2str.c + */ +struct gldns_struct_lookup_table { + int id; + const char *name; +}; +typedef struct gldns_struct_lookup_table gldns_lookup_table; + +/** + * Looks up the table entry by name, returns NULL if not found. + * \param[in] table the lookup table to search in + * \param[in] name what to search for + * \return the item found + */ +gldns_lookup_table *gldns_lookup_by_name(gldns_lookup_table table[], + const char *name); +/** + * Looks up the table entry by id, returns NULL if not found. + * \param[in] table the lookup table to search in + * \param[in] id what to search for + * \return the item found + */ +gldns_lookup_table *gldns_lookup_by_id(gldns_lookup_table table[], int id); + +/** + * Convert TM to seconds since epoch (midnight, January 1st, 1970). + * Like timegm(3), which is not always available. + * \param[in] tm a struct tm* with the date + * \return the seconds since epoch + */ +time_t gldns_mktime_from_utc(const struct tm *tm); + +/** + * The function interprets time as the number of seconds since epoch + * with respect to now using serial arithmitics (rfc1982). + * That number of seconds is then converted to broken-out time information. + * This is especially usefull when converting the inception and expiration + * fields of RRSIG records. + * + * \param[in] time number of seconds since epoch (midnight, January 1st, 1970) + * to be intepreted as a serial arithmitics number relative to now. + * \param[in] now number of seconds since epoch (midnight, January 1st, 1970) + * to which the time value is compared to determine the final value. + * \param[out] result the struct with the broken-out time information + * \return result on success or NULL on error + */ +struct tm * gldns_serial_arithmitics_gmtime_r(int32_t time, time_t now, struct tm *result); + +/** + * converts a ttl value (like 5d2h) to a long. + * \param[in] nptr the start of the string + * \param[out] endptr points to the last char in case of error + * \return the convert duration value + */ +uint32_t gldns_str2period(const char *nptr, const char **endptr); + +/** + * Returns the int value of the given (hex) digit + * \param[in] ch the hex char to convert + * \return the converted decimal value + */ +int gldns_hexdigit_to_int(char ch); + +/** + * calculates the size needed to store the result of b64_ntop + */ +size_t gldns_b64_ntop_calculate_size(size_t srcsize); + +int gldns_b64_ntop(uint8_t const *src, size_t srclength, + char *target, size_t targsize); + +/** + * calculates the size needed to store the result of gldns_b64_pton + */ +size_t gldns_b64_pton_calculate_size(size_t srcsize); + +int gldns_b64_pton(char const *src, uint8_t *target, size_t targsize); + +/** + * calculates the size needed to store the result of b32_ntop + */ +size_t gldns_b32_ntop_calculate_size(size_t src_data_length); + +size_t gldns_b32_ntop_calculate_size_no_padding(size_t src_data_length); + +int gldns_b32_ntop(const uint8_t* src_data, size_t src_data_length, + char* target_text_buffer, size_t target_text_buffer_size); + +int gldns_b32_ntop_extended_hex(const uint8_t* src_data, size_t src_data_length, + char* target_text_buffer, size_t target_text_buffer_size); + +/** + * calculates the size needed to store the result of b32_pton + */ +size_t gldns_b32_pton_calculate_size(size_t src_text_length); + +int gldns_b32_pton(const char* src_text, size_t src_text_length, + uint8_t* target_data_buffer, size_t target_data_buffer_size); + +int gldns_b32_pton_extended_hex(const char* src_text, size_t src_text_length, + uint8_t* target_data_buffer, size_t target_data_buffer_size); + +/* + * Checks whether the escaped value at **s is an octal value or + * a 'normally' escaped character (and not eos) + * + * @param ch_p: the parsed character + * @param str_p: the string. moved along for characters read. + * The string pointer at *s is increased by either 0 (on error), 1 (on + * normal escapes), or 3 (on octals) + * + * @return 0 on error + */ +int gldns_parse_escape(uint8_t *ch_p, const char** str_p); + +/** + * Parse one character, with escape codes, + * @param ch_p: the parsed character + * @param str_p: the string. moved along for characters read. + * @return 0 on error + */ +int gldns_parse_char(uint8_t *ch_p, const char** str_p); + +#endif /* GLDNS_PARSEUTIL_H */ diff --git a/src/gldns/pkthdr.h b/src/gldns/pkthdr.h new file mode 100644 index 00000000..d2572d12 --- /dev/null +++ b/src/gldns/pkthdr.h @@ -0,0 +1,158 @@ +/* + * pkthdr.h - packet header from wire conversion routines + * + * a Net::DNS like library for C + * + * (c) NLnet Labs, 2005-2006 + * + * See the file LICENSE for the license + */ + +/** + * \file + * + * Contains functions that translate dns data from the wire format (as sent + * by servers and clients) to the internal structures for the packet header. + */ + +#ifndef GLDNS_PKTHDR_H +#define GLDNS_PKTHDR_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* The length of the header */ +#define GLDNS_HEADER_SIZE 12 + +/* First octet of flags */ +#define GLDNS_RD_MASK 0x01U +#define GLDNS_RD_SHIFT 0 +#define GLDNS_RD_WIRE(wirebuf) (*(wirebuf+2) & GLDNS_RD_MASK) +#define GLDNS_RD_SET(wirebuf) (*(wirebuf+2) |= GLDNS_RD_MASK) +#define GLDNS_RD_CLR(wirebuf) (*(wirebuf+2) &= ~GLDNS_RD_MASK) + +#define GLDNS_TC_MASK 0x02U +#define GLDNS_TC_SHIFT 1 +#define GLDNS_TC_WIRE(wirebuf) (*(wirebuf+2) & GLDNS_TC_MASK) +#define GLDNS_TC_SET(wirebuf) (*(wirebuf+2) |= GLDNS_TC_MASK) +#define GLDNS_TC_CLR(wirebuf) (*(wirebuf+2) &= ~GLDNS_TC_MASK) + +#define GLDNS_AA_MASK 0x04U +#define GLDNS_AA_SHIFT 2 +#define GLDNS_AA_WIRE(wirebuf) (*(wirebuf+2) & GLDNS_AA_MASK) +#define GLDNS_AA_SET(wirebuf) (*(wirebuf+2) |= GLDNS_AA_MASK) +#define GLDNS_AA_CLR(wirebuf) (*(wirebuf+2) &= ~GLDNS_AA_MASK) + +#define GLDNS_OPCODE_MASK 0x78U +#define GLDNS_OPCODE_SHIFT 3 +#define GLDNS_OPCODE_WIRE(wirebuf) ((*(wirebuf+2) & GLDNS_OPCODE_MASK) >> GLDNS_OPCODE_SHIFT) +#define GLDNS_OPCODE_SET(wirebuf, opcode) \ + (*(wirebuf+2) = ((*(wirebuf+2)) & ~GLDNS_OPCODE_MASK) | ((opcode) << GLDNS_OPCODE_SHIFT)) + +#define GLDNS_QR_MASK 0x80U +#define GLDNS_QR_SHIFT 7 +#define GLDNS_QR_WIRE(wirebuf) (*(wirebuf+2) & GLDNS_QR_MASK) +#define GLDNS_QR_SET(wirebuf) (*(wirebuf+2) |= GLDNS_QR_MASK) +#define GLDNS_QR_CLR(wirebuf) (*(wirebuf+2) &= ~GLDNS_QR_MASK) + +/* Second octet of flags */ +#define GLDNS_RCODE_MASK 0x0fU +#define GLDNS_RCODE_SHIFT 0 +#define GLDNS_RCODE_WIRE(wirebuf) (*(wirebuf+3) & GLDNS_RCODE_MASK) +#define GLDNS_RCODE_SET(wirebuf, rcode) \ + (*(wirebuf+3) = ((*(wirebuf+3)) & ~GLDNS_RCODE_MASK) | (rcode)) + +#define GLDNS_CD_MASK 0x10U +#define GLDNS_CD_SHIFT 4 +#define GLDNS_CD_WIRE(wirebuf) (*(wirebuf+3) & GLDNS_CD_MASK) +#define GLDNS_CD_SET(wirebuf) (*(wirebuf+3) |= GLDNS_CD_MASK) +#define GLDNS_CD_CLR(wirebuf) (*(wirebuf+3) &= ~GLDNS_CD_MASK) + +#define GLDNS_AD_MASK 0x20U +#define GLDNS_AD_SHIFT 5 +#define GLDNS_AD_WIRE(wirebuf) (*(wirebuf+3) & GLDNS_AD_MASK) +#define GLDNS_AD_SET(wirebuf) (*(wirebuf+3) |= GLDNS_AD_MASK) +#define GLDNS_AD_CLR(wirebuf) (*(wirebuf+3) &= ~GLDNS_AD_MASK) + +#define GLDNS_Z_MASK 0x40U +#define GLDNS_Z_SHIFT 6 +#define GLDNS_Z_WIRE(wirebuf) (*(wirebuf+3) & GLDNS_Z_MASK) +#define GLDNS_Z_SET(wirebuf) (*(wirebuf+3) |= GLDNS_Z_MASK) +#define GLDNS_Z_CLR(wirebuf) (*(wirebuf+3) &= ~GLDNS_Z_MASK) + +#define GLDNS_RA_MASK 0x80U +#define GLDNS_RA_SHIFT 7 +#define GLDNS_RA_WIRE(wirebuf) (*(wirebuf+3) & GLDNS_RA_MASK) +#define GLDNS_RA_SET(wirebuf) (*(wirebuf+3) |= GLDNS_RA_MASK) +#define GLDNS_RA_CLR(wirebuf) (*(wirebuf+3) &= ~GLDNS_RA_MASK) + +/* Query ID */ +#define GLDNS_ID_WIRE(wirebuf) (gldns_read_uint16(wirebuf)) +#define GLDNS_ID_SET(wirebuf, id) (gldns_write_uint16(wirebuf, id)) + +/* Counter of the question section */ +#define GLDNS_QDCOUNT_OFF 4 +/* +#define QDCOUNT(wirebuf) (ntohs(*(uint16_t *)(wirebuf+QDCOUNT_OFF))) +*/ +#define GLDNS_QDCOUNT(wirebuf) (gldns_read_uint16(wirebuf+GLDNS_QDCOUNT_OFF)) + +/* Counter of the answer section */ +#define GLDNS_ANCOUNT_OFF 6 +#define GLDNS_ANCOUNT(wirebuf) (gldns_read_uint16(wirebuf+GLDNS_ANCOUNT_OFF)) + +/* Counter of the authority section */ +#define GLDNS_NSCOUNT_OFF 8 +#define GLDNS_NSCOUNT(wirebuf) (gldns_read_uint16(wirebuf+GLDNS_NSCOUNT_OFF)) + +/* Counter of the additional section */ +#define GLDNS_ARCOUNT_OFF 10 +#define GLDNS_ARCOUNT(wirebuf) (gldns_read_uint16(wirebuf+GLDNS_ARCOUNT_OFF)) + +/** + * The sections of a packet + */ +enum gldns_enum_pkt_section { + GLDNS_SECTION_QUESTION = 0, + GLDNS_SECTION_ANSWER = 1, + GLDNS_SECTION_AUTHORITY = 2, + GLDNS_SECTION_ADDITIONAL = 3, + /** bogus section, if not interested */ + GLDNS_SECTION_ANY = 4, + /** used to get all non-question rrs from a packet */ + GLDNS_SECTION_ANY_NOQUESTION = 5 +}; +typedef enum gldns_enum_pkt_section gldns_pkt_section; + +/* opcodes for pkt's */ +enum gldns_enum_pkt_opcode { + GLDNS_PACKET_QUERY = 0, + GLDNS_PACKET_IQUERY = 1, + GLDNS_PACKET_STATUS = 2, /* there is no 3?? DNS is weird */ + GLDNS_PACKET_NOTIFY = 4, + GLDNS_PACKET_UPDATE = 5 +}; +typedef enum gldns_enum_pkt_opcode gldns_pkt_opcode; + +/* rcodes for pkts */ +enum gldns_enum_pkt_rcode { + GLDNS_RCODE_NOERROR = 0, + GLDNS_RCODE_FORMERR = 1, + GLDNS_RCODE_SERVFAIL = 2, + GLDNS_RCODE_NXDOMAIN = 3, + GLDNS_RCODE_NOTIMPL = 4, + GLDNS_RCODE_REFUSED = 5, + GLDNS_RCODE_YXDOMAIN = 6, + GLDNS_RCODE_YXRRSET = 7, + GLDNS_RCODE_NXRRSET = 8, + GLDNS_RCODE_NOTAUTH = 9, + GLDNS_RCODE_NOTZONE = 10 +}; +typedef enum gldns_enum_pkt_rcode gldns_pkt_rcode; + +#ifdef __cplusplus +} +#endif + +#endif /* GLDNS_PKTHDR_H */ diff --git a/src/gldns/rrdef.c b/src/gldns/rrdef.c new file mode 100644 index 00000000..976574d9 --- /dev/null +++ b/src/gldns/rrdef.c @@ -0,0 +1,734 @@ +/* rrdef.c + * + * access functions to rr definitions list. + * a Net::DNS like library for C + * LibDNS Team @ NLnet Labs + * + * (c) NLnet Labs, 2004-2006 + * See the file LICENSE for the license + */ +/** + * \file + * + * Defines resource record types and constants. + */ +#include "config.h" +#include "gldns/rrdef.h" +#include "gldns/parseutil.h" + +/* classes */ +static gldns_lookup_table gldns_rr_classes_data[] = { + { GLDNS_RR_CLASS_IN, "IN" }, + { GLDNS_RR_CLASS_CH, "CH" }, + { GLDNS_RR_CLASS_HS, "HS" }, + { GLDNS_RR_CLASS_NONE, "NONE" }, + { GLDNS_RR_CLASS_ANY, "ANY" }, + { 0, NULL } +}; +gldns_lookup_table* gldns_rr_classes = gldns_rr_classes_data; + +/* types */ +static const gldns_rdf_type type_0_wireformat[] = { GLDNS_RDF_TYPE_UNKNOWN }; +static const gldns_rdf_type type_a_wireformat[] = { GLDNS_RDF_TYPE_A }; +static const gldns_rdf_type type_ns_wireformat[] = { GLDNS_RDF_TYPE_DNAME }; +static const gldns_rdf_type type_md_wireformat[] = { GLDNS_RDF_TYPE_DNAME }; +static const gldns_rdf_type type_mf_wireformat[] = { GLDNS_RDF_TYPE_DNAME }; +static const gldns_rdf_type type_cname_wireformat[] = { GLDNS_RDF_TYPE_DNAME }; +static const gldns_rdf_type type_soa_wireformat[] = { + GLDNS_RDF_TYPE_DNAME, GLDNS_RDF_TYPE_DNAME, GLDNS_RDF_TYPE_INT32, + GLDNS_RDF_TYPE_PERIOD, GLDNS_RDF_TYPE_PERIOD, GLDNS_RDF_TYPE_PERIOD, + GLDNS_RDF_TYPE_PERIOD +}; +static const gldns_rdf_type type_mb_wireformat[] = { GLDNS_RDF_TYPE_DNAME }; +static const gldns_rdf_type type_mg_wireformat[] = { GLDNS_RDF_TYPE_DNAME }; +static const gldns_rdf_type type_mr_wireformat[] = { GLDNS_RDF_TYPE_DNAME }; +static const gldns_rdf_type type_wks_wireformat[] = { + GLDNS_RDF_TYPE_A, GLDNS_RDF_TYPE_WKS +}; +static const gldns_rdf_type type_ptr_wireformat[] = { GLDNS_RDF_TYPE_DNAME }; +static const gldns_rdf_type type_hinfo_wireformat[] = { + GLDNS_RDF_TYPE_STR, GLDNS_RDF_TYPE_STR +}; +static const gldns_rdf_type type_minfo_wireformat[] = { + GLDNS_RDF_TYPE_DNAME, GLDNS_RDF_TYPE_DNAME +}; +static const gldns_rdf_type type_mx_wireformat[] = { + GLDNS_RDF_TYPE_INT16, GLDNS_RDF_TYPE_DNAME +}; +static const gldns_rdf_type type_rp_wireformat[] = { + GLDNS_RDF_TYPE_DNAME, GLDNS_RDF_TYPE_DNAME +}; +static const gldns_rdf_type type_afsdb_wireformat[] = { + GLDNS_RDF_TYPE_INT16, GLDNS_RDF_TYPE_DNAME +}; +static const gldns_rdf_type type_x25_wireformat[] = { GLDNS_RDF_TYPE_STR }; +static const gldns_rdf_type type_isdn_wireformat[] = { + GLDNS_RDF_TYPE_STR, GLDNS_RDF_TYPE_STR +}; +static const gldns_rdf_type type_rt_wireformat[] = { + GLDNS_RDF_TYPE_INT16, GLDNS_RDF_TYPE_DNAME +}; +static const gldns_rdf_type type_nsap_wireformat[] = { + GLDNS_RDF_TYPE_NSAP +}; +static const gldns_rdf_type type_nsap_ptr_wireformat[] = { + GLDNS_RDF_TYPE_STR +}; +static const gldns_rdf_type type_sig_wireformat[] = { + GLDNS_RDF_TYPE_TYPE, GLDNS_RDF_TYPE_ALG, GLDNS_RDF_TYPE_INT8, GLDNS_RDF_TYPE_INT32, + GLDNS_RDF_TYPE_TIME, GLDNS_RDF_TYPE_TIME, GLDNS_RDF_TYPE_INT16, + GLDNS_RDF_TYPE_DNAME, GLDNS_RDF_TYPE_B64 +}; +static const gldns_rdf_type type_key_wireformat[] = { + GLDNS_RDF_TYPE_INT16, GLDNS_RDF_TYPE_INT8, GLDNS_RDF_TYPE_INT8, GLDNS_RDF_TYPE_B64 +}; +static const gldns_rdf_type type_px_wireformat[] = { + GLDNS_RDF_TYPE_INT16, GLDNS_RDF_TYPE_DNAME, GLDNS_RDF_TYPE_DNAME +}; +static const gldns_rdf_type type_gpos_wireformat[] = { + GLDNS_RDF_TYPE_STR, GLDNS_RDF_TYPE_STR, GLDNS_RDF_TYPE_STR +}; +static const gldns_rdf_type type_aaaa_wireformat[] = { GLDNS_RDF_TYPE_AAAA }; +static const gldns_rdf_type type_loc_wireformat[] = { GLDNS_RDF_TYPE_LOC }; +static const gldns_rdf_type type_nxt_wireformat[] = { + GLDNS_RDF_TYPE_DNAME, GLDNS_RDF_TYPE_UNKNOWN +}; +static const gldns_rdf_type type_eid_wireformat[] = { + GLDNS_RDF_TYPE_HEX +}; +static const gldns_rdf_type type_nimloc_wireformat[] = { + GLDNS_RDF_TYPE_HEX +}; +static const gldns_rdf_type type_srv_wireformat[] = { + GLDNS_RDF_TYPE_INT16, GLDNS_RDF_TYPE_INT16, GLDNS_RDF_TYPE_INT16, GLDNS_RDF_TYPE_DNAME +}; +static const gldns_rdf_type type_atma_wireformat[] = { + GLDNS_RDF_TYPE_ATMA +}; +static const gldns_rdf_type type_naptr_wireformat[] = { + GLDNS_RDF_TYPE_INT16, GLDNS_RDF_TYPE_INT16, GLDNS_RDF_TYPE_STR, GLDNS_RDF_TYPE_STR, GLDNS_RDF_TYPE_STR, GLDNS_RDF_TYPE_DNAME +}; +static const gldns_rdf_type type_kx_wireformat[] = { + GLDNS_RDF_TYPE_INT16, GLDNS_RDF_TYPE_DNAME +}; +static const gldns_rdf_type type_cert_wireformat[] = { + GLDNS_RDF_TYPE_CERT_ALG, GLDNS_RDF_TYPE_INT16, GLDNS_RDF_TYPE_ALG, GLDNS_RDF_TYPE_B64 +}; +static const gldns_rdf_type type_a6_wireformat[] = { GLDNS_RDF_TYPE_UNKNOWN }; +static const gldns_rdf_type type_dname_wireformat[] = { GLDNS_RDF_TYPE_DNAME }; +static const gldns_rdf_type type_sink_wireformat[] = { GLDNS_RDF_TYPE_INT8, + GLDNS_RDF_TYPE_INT8, GLDNS_RDF_TYPE_INT8, GLDNS_RDF_TYPE_B64 +}; +static const gldns_rdf_type type_apl_wireformat[] = { + GLDNS_RDF_TYPE_APL +}; +static const gldns_rdf_type type_ds_wireformat[] = { + GLDNS_RDF_TYPE_INT16, GLDNS_RDF_TYPE_ALG, GLDNS_RDF_TYPE_INT8, GLDNS_RDF_TYPE_HEX +}; +static const gldns_rdf_type type_sshfp_wireformat[] = { + GLDNS_RDF_TYPE_INT8, GLDNS_RDF_TYPE_INT8, GLDNS_RDF_TYPE_HEX +}; +static const gldns_rdf_type type_ipseckey_wireformat[] = { + GLDNS_RDF_TYPE_IPSECKEY +}; +static const gldns_rdf_type type_rrsig_wireformat[] = { + GLDNS_RDF_TYPE_TYPE, GLDNS_RDF_TYPE_ALG, GLDNS_RDF_TYPE_INT8, GLDNS_RDF_TYPE_INT32, + GLDNS_RDF_TYPE_TIME, GLDNS_RDF_TYPE_TIME, GLDNS_RDF_TYPE_INT16, GLDNS_RDF_TYPE_DNAME, GLDNS_RDF_TYPE_B64 +}; +static const gldns_rdf_type type_nsec_wireformat[] = { + GLDNS_RDF_TYPE_DNAME, GLDNS_RDF_TYPE_NSEC +}; +static const gldns_rdf_type type_dhcid_wireformat[] = { + GLDNS_RDF_TYPE_B64 +}; +static const gldns_rdf_type type_talink_wireformat[] = { + GLDNS_RDF_TYPE_DNAME, GLDNS_RDF_TYPE_DNAME +}; +/* nsec3 is some vars, followed by same type of data of nsec */ +static const gldns_rdf_type type_nsec3_wireformat[] = { +/* GLDNS_RDF_TYPE_NSEC3_VARS, GLDNS_RDF_TYPE_NSEC3_NEXT_OWNER, GLDNS_RDF_TYPE_NSEC*/ + GLDNS_RDF_TYPE_INT8, GLDNS_RDF_TYPE_INT8, GLDNS_RDF_TYPE_INT16, GLDNS_RDF_TYPE_NSEC3_SALT, GLDNS_RDF_TYPE_NSEC3_NEXT_OWNER, GLDNS_RDF_TYPE_NSEC +}; + +static const gldns_rdf_type type_nsec3param_wireformat[] = { +/* GLDNS_RDF_TYPE_NSEC3_PARAMS_VARS*/ + GLDNS_RDF_TYPE_INT8, + GLDNS_RDF_TYPE_INT8, + GLDNS_RDF_TYPE_INT16, + GLDNS_RDF_TYPE_NSEC3_SALT +}; + +static const gldns_rdf_type type_dnskey_wireformat[] = { + GLDNS_RDF_TYPE_INT16, + GLDNS_RDF_TYPE_INT8, + GLDNS_RDF_TYPE_ALG, + GLDNS_RDF_TYPE_B64 +}; +static const gldns_rdf_type type_tkey_wireformat[] = { + GLDNS_RDF_TYPE_DNAME, + GLDNS_RDF_TYPE_TIME, + GLDNS_RDF_TYPE_TIME, + GLDNS_RDF_TYPE_INT16, + GLDNS_RDF_TYPE_INT16, + GLDNS_RDF_TYPE_INT16_DATA, + GLDNS_RDF_TYPE_INT16_DATA, +}; +static const gldns_rdf_type type_tsig_wireformat[] = { + GLDNS_RDF_TYPE_DNAME, + GLDNS_RDF_TYPE_TSIGTIME, + GLDNS_RDF_TYPE_INT16, + GLDNS_RDF_TYPE_INT16_DATA, + GLDNS_RDF_TYPE_INT16, + GLDNS_RDF_TYPE_INT16, + GLDNS_RDF_TYPE_INT16_DATA +}; +static const gldns_rdf_type type_tlsa_wireformat[] = { + GLDNS_RDF_TYPE_INT8, + GLDNS_RDF_TYPE_INT8, + GLDNS_RDF_TYPE_INT8, + GLDNS_RDF_TYPE_HEX +}; +static const gldns_rdf_type type_hip_wireformat[] = { + GLDNS_RDF_TYPE_HIP +}; +static const gldns_rdf_type type_nid_wireformat[] = { + GLDNS_RDF_TYPE_INT16, + GLDNS_RDF_TYPE_ILNP64 +}; +static const gldns_rdf_type type_l32_wireformat[] = { + GLDNS_RDF_TYPE_INT16, + GLDNS_RDF_TYPE_A +}; +static const gldns_rdf_type type_l64_wireformat[] = { + GLDNS_RDF_TYPE_INT16, + GLDNS_RDF_TYPE_ILNP64 +}; +static const gldns_rdf_type type_lp_wireformat[] = { + GLDNS_RDF_TYPE_INT16, + GLDNS_RDF_TYPE_DNAME +}; +static const gldns_rdf_type type_eui48_wireformat[] = { + GLDNS_RDF_TYPE_EUI48 +}; +static const gldns_rdf_type type_eui64_wireformat[] = { + GLDNS_RDF_TYPE_EUI64 +}; +#ifdef DRAFT_RRTYPES +static const gldns_rdf_type type_uri_wireformat[] = { + GLDNS_RDF_TYPE_INT16, + GLDNS_RDF_TYPE_INT16, + GLDNS_RDF_TYPE_LONG_STR +}; +#endif +static const gldns_rdf_type type_caa_wireformat[] = { + GLDNS_RDF_TYPE_INT8, + GLDNS_RDF_TYPE_TAG, + GLDNS_RDF_TYPE_LONG_STR +}; + +/* All RR's defined in 1035 are well known and can thus + * be compressed. See RFC3597. These RR's are: + * CNAME HINFO MB MD MF MG MINFO MR MX NULL NS PTR SOA TXT + */ +static gldns_rr_descriptor rdata_field_descriptors[] = { + /* 0 */ + { 0, NULL, 0, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, + /* 1 */ + {GLDNS_RR_TYPE_A, "A", 1, 1, type_a_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, + /* 2 */ + {GLDNS_RR_TYPE_NS, "NS", 1, 1, type_ns_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_COMPRESS, 1 }, + /* 3 */ + {GLDNS_RR_TYPE_MD, "MD", 1, 1, type_md_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_COMPRESS, 1 }, + /* 4 */ + {GLDNS_RR_TYPE_MF, "MF", 1, 1, type_mf_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_COMPRESS, 1 }, + /* 5 */ + {GLDNS_RR_TYPE_CNAME, "CNAME", 1, 1, type_cname_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_COMPRESS, 1 }, + /* 6 */ + {GLDNS_RR_TYPE_SOA, "SOA", 7, 7, type_soa_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_COMPRESS, 2 }, + /* 7 */ + {GLDNS_RR_TYPE_MB, "MB", 1, 1, type_mb_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_COMPRESS, 1 }, + /* 8 */ + {GLDNS_RR_TYPE_MG, "MG", 1, 1, type_mg_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_COMPRESS, 1 }, + /* 9 */ + {GLDNS_RR_TYPE_MR, "MR", 1, 1, type_mr_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_COMPRESS, 1 }, + /* 10 */ + {GLDNS_RR_TYPE_NULL, "NULL", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, + /* 11 */ + {GLDNS_RR_TYPE_WKS, "WKS", 2, 2, type_wks_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, + /* 12 */ + {GLDNS_RR_TYPE_PTR, "PTR", 1, 1, type_ptr_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_COMPRESS, 1 }, + /* 13 */ + {GLDNS_RR_TYPE_HINFO, "HINFO", 2, 2, type_hinfo_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, + /* 14 */ + {GLDNS_RR_TYPE_MINFO, "MINFO", 2, 2, type_minfo_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_COMPRESS, 2 }, + /* 15 */ + {GLDNS_RR_TYPE_MX, "MX", 2, 2, type_mx_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_COMPRESS, 1 }, + /* 16 */ + {GLDNS_RR_TYPE_TXT, "TXT", 1, 0, NULL, GLDNS_RDF_TYPE_STR, GLDNS_RR_NO_COMPRESS, 0 }, + /* 17 */ + {GLDNS_RR_TYPE_RP, "RP", 2, 2, type_rp_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 2 }, + /* 18 */ + {GLDNS_RR_TYPE_AFSDB, "AFSDB", 2, 2, type_afsdb_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 1 }, + /* 19 */ + {GLDNS_RR_TYPE_X25, "X25", 1, 1, type_x25_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, + /* 20 */ + {GLDNS_RR_TYPE_ISDN, "ISDN", 1, 2, type_isdn_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, + /* 21 */ + {GLDNS_RR_TYPE_RT, "RT", 2, 2, type_rt_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 1 }, + /* 22 */ + {GLDNS_RR_TYPE_NSAP, "NSAP", 1, 1, type_nsap_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, + /* 23 */ + {GLDNS_RR_TYPE_NSAP_PTR, "NSAP-PTR", 1, 1, type_nsap_ptr_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, + /* 24 */ + {GLDNS_RR_TYPE_SIG, "SIG", 9, 9, type_sig_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 1 }, + /* 25 */ + {GLDNS_RR_TYPE_KEY, "KEY", 4, 4, type_key_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, + /* 26 */ + {GLDNS_RR_TYPE_PX, "PX", 3, 3, type_px_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 2 }, + /* 27 */ + {GLDNS_RR_TYPE_GPOS, "GPOS", 3, 3, type_gpos_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, + /* 28 */ + {GLDNS_RR_TYPE_AAAA, "AAAA", 1, 1, type_aaaa_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, + /* 29 */ + {GLDNS_RR_TYPE_LOC, "LOC", 1, 1, type_loc_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, + /* 30 */ + {GLDNS_RR_TYPE_NXT, "NXT", 2, 2, type_nxt_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 1 }, + /* 31 */ + {GLDNS_RR_TYPE_EID, "EID", 1, 1, type_eid_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, + /* 32 */ + {GLDNS_RR_TYPE_NIMLOC, "NIMLOC", 1, 1, type_nimloc_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, + /* 33 */ + {GLDNS_RR_TYPE_SRV, "SRV", 4, 4, type_srv_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 1 }, + /* 34 */ + {GLDNS_RR_TYPE_ATMA, "ATMA", 1, 1, type_atma_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, + /* 35 */ + {GLDNS_RR_TYPE_NAPTR, "NAPTR", 6, 6, type_naptr_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 1 }, + /* 36 */ + {GLDNS_RR_TYPE_KX, "KX", 2, 2, type_kx_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 1 }, + /* 37 */ + {GLDNS_RR_TYPE_CERT, "CERT", 4, 4, type_cert_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, + /* 38 */ + {GLDNS_RR_TYPE_A6, "A6", 1, 1, type_a6_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, + /* 39 */ + {GLDNS_RR_TYPE_DNAME, "DNAME", 1, 1, type_dname_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 1 }, + /* 40 */ + {GLDNS_RR_TYPE_SINK, "SINK", 1, 1, type_sink_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, + /* 41 */ + {GLDNS_RR_TYPE_OPT, "OPT", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, + /* 42 */ + {GLDNS_RR_TYPE_APL, "APL", 0, 0, type_apl_wireformat, GLDNS_RDF_TYPE_APL, GLDNS_RR_NO_COMPRESS, 0 }, + /* 43 */ + {GLDNS_RR_TYPE_DS, "DS", 4, 4, type_ds_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, + /* 44 */ + {GLDNS_RR_TYPE_SSHFP, "SSHFP", 3, 3, type_sshfp_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, + /* 45 */ + {GLDNS_RR_TYPE_IPSECKEY, "IPSECKEY", 1, 1, type_ipseckey_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, + /* 46 */ + {GLDNS_RR_TYPE_RRSIG, "RRSIG", 9, 9, type_rrsig_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 1 }, + /* 47 */ + {GLDNS_RR_TYPE_NSEC, "NSEC", 1, 2, type_nsec_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 1 }, + /* 48 */ + {GLDNS_RR_TYPE_DNSKEY, "DNSKEY", 4, 4, type_dnskey_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, + /* 49 */ + {GLDNS_RR_TYPE_DHCID, "DHCID", 1, 1, type_dhcid_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, + /* 50 */ + {GLDNS_RR_TYPE_NSEC3, "NSEC3", 5, 6, type_nsec3_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, + /* 51 */ + {GLDNS_RR_TYPE_NSEC3PARAM, "NSEC3PARAM", 4, 4, type_nsec3param_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, + /* 52 */ + {GLDNS_RR_TYPE_TLSA, "TLSA", 4, 4, type_tlsa_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, + +{GLDNS_RR_TYPE_NULL, "TYPE53", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, +{GLDNS_RR_TYPE_NULL, "TYPE54", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, + /* 55 + * Hip ends with 0 or more Rendezvous Servers represented as dname's. + * Hence the GLDNS_RDF_TYPE_DNAME _variable field and the _maximum field + * set to 0. + */ + {GLDNS_RR_TYPE_HIP, "HIP", 1, 1, type_hip_wireformat, GLDNS_RDF_TYPE_DNAME, GLDNS_RR_NO_COMPRESS, 0 }, + +#ifdef DRAFT_RRTYPES + /* 56 */ + {GLDNS_RR_TYPE_NINFO, "NINFO", 1, 0, NULL, GLDNS_RDF_TYPE_STR, GLDNS_RR_NO_COMPRESS, 0 }, + /* 57 */ + {GLDNS_RR_TYPE_RKEY, "RKEY", 4, 4, type_key_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, +#else +{GLDNS_RR_TYPE_NULL, "TYPE56", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, +{GLDNS_RR_TYPE_NULL, "TYPE57", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, +#endif + /* 58 */ + {GLDNS_RR_TYPE_TALINK, "TALINK", 2, 2, type_talink_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 2 }, + + /* 59 */ + {GLDNS_RR_TYPE_CDS, "CDS", 4, 4, type_ds_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, + /* 60 */ + {GLDNS_RR_TYPE_CDNSKEY, "CDNSKEY", 4, 4, type_dnskey_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, +{GLDNS_RR_TYPE_NULL, "TYPE61", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, +{GLDNS_RR_TYPE_NULL, "TYPE62", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, +{GLDNS_RR_TYPE_NULL, "TYPE63", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, +{GLDNS_RR_TYPE_NULL, "TYPE64", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, +{GLDNS_RR_TYPE_NULL, "TYPE65", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, +{GLDNS_RR_TYPE_NULL, "TYPE66", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, +{GLDNS_RR_TYPE_NULL, "TYPE67", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, +{GLDNS_RR_TYPE_NULL, "TYPE68", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, +{GLDNS_RR_TYPE_NULL, "TYPE69", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, +{GLDNS_RR_TYPE_NULL, "TYPE70", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, +{GLDNS_RR_TYPE_NULL, "TYPE71", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, +{GLDNS_RR_TYPE_NULL, "TYPE72", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, +{GLDNS_RR_TYPE_NULL, "TYPE73", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, +{GLDNS_RR_TYPE_NULL, "TYPE74", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, +{GLDNS_RR_TYPE_NULL, "TYPE75", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, +{GLDNS_RR_TYPE_NULL, "TYPE76", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, +{GLDNS_RR_TYPE_NULL, "TYPE77", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, +{GLDNS_RR_TYPE_NULL, "TYPE78", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, +{GLDNS_RR_TYPE_NULL, "TYPE79", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, +{GLDNS_RR_TYPE_NULL, "TYPE80", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, +{GLDNS_RR_TYPE_NULL, "TYPE81", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, +{GLDNS_RR_TYPE_NULL, "TYPE82", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, +{GLDNS_RR_TYPE_NULL, "TYPE83", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, +{GLDNS_RR_TYPE_NULL, "TYPE84", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, +{GLDNS_RR_TYPE_NULL, "TYPE85", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, +{GLDNS_RR_TYPE_NULL, "TYPE86", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, +{GLDNS_RR_TYPE_NULL, "TYPE87", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, +{GLDNS_RR_TYPE_NULL, "TYPE88", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, +{GLDNS_RR_TYPE_NULL, "TYPE89", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, +{GLDNS_RR_TYPE_NULL, "TYPE90", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, +{GLDNS_RR_TYPE_NULL, "TYPE91", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, +{GLDNS_RR_TYPE_NULL, "TYPE92", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, +{GLDNS_RR_TYPE_NULL, "TYPE93", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, +{GLDNS_RR_TYPE_NULL, "TYPE94", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, +{GLDNS_RR_TYPE_NULL, "TYPE95", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, +{GLDNS_RR_TYPE_NULL, "TYPE96", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, +{GLDNS_RR_TYPE_NULL, "TYPE97", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, +{GLDNS_RR_TYPE_NULL, "TYPE98", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, + + /* 99 */ + {GLDNS_RR_TYPE_SPF, "SPF", 1, 0, NULL, GLDNS_RDF_TYPE_STR, GLDNS_RR_NO_COMPRESS, 0 }, + + /* UINFO [IANA-Reserved] */ +{GLDNS_RR_TYPE_NULL, "TYPE100", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, + /* UID [IANA-Reserved] */ +{GLDNS_RR_TYPE_NULL, "TYPE101", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, + /* GID [IANA-Reserved] */ +{GLDNS_RR_TYPE_NULL, "TYPE102", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, + /* UNSPEC [IANA-Reserved] */ +{GLDNS_RR_TYPE_NULL, "TYPE103", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, + + /* 104 */ + {GLDNS_RR_TYPE_NID, "NID", 2, 2, type_nid_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, + /* 105 */ + {GLDNS_RR_TYPE_L32, "L32", 2, 2, type_l32_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, + /* 106 */ + {GLDNS_RR_TYPE_L64, "L64", 2, 2, type_l64_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, + /* 107 */ + {GLDNS_RR_TYPE_LP, "LP", 2, 2, type_lp_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 1 }, + + /* 108 */ + {GLDNS_RR_TYPE_EUI48, "EUI48", 1, 1, type_eui48_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, + /* 109 */ + {GLDNS_RR_TYPE_EUI64, "EUI64", 1, 1, type_eui64_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, + +{GLDNS_RR_TYPE_NULL, "TYPE110", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, +{GLDNS_RR_TYPE_NULL, "TYPE111", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, +{GLDNS_RR_TYPE_NULL, "TYPE112", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, +{GLDNS_RR_TYPE_NULL, "TYPE113", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, +{GLDNS_RR_TYPE_NULL, "TYPE114", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, +{GLDNS_RR_TYPE_NULL, "TYPE115", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, +{GLDNS_RR_TYPE_NULL, "TYPE116", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, +{GLDNS_RR_TYPE_NULL, "TYPE117", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, +{GLDNS_RR_TYPE_NULL, "TYPE118", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, +{GLDNS_RR_TYPE_NULL, "TYPE119", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, +{GLDNS_RR_TYPE_NULL, "TYPE120", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, +{GLDNS_RR_TYPE_NULL, "TYPE121", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, +{GLDNS_RR_TYPE_NULL, "TYPE122", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, +{GLDNS_RR_TYPE_NULL, "TYPE123", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, +{GLDNS_RR_TYPE_NULL, "TYPE124", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, +{GLDNS_RR_TYPE_NULL, "TYPE125", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, +{GLDNS_RR_TYPE_NULL, "TYPE126", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, +{GLDNS_RR_TYPE_NULL, "TYPE127", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, +{GLDNS_RR_TYPE_NULL, "TYPE128", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, +{GLDNS_RR_TYPE_NULL, "TYPE129", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, +{GLDNS_RR_TYPE_NULL, "TYPE130", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, +{GLDNS_RR_TYPE_NULL, "TYPE131", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, +{GLDNS_RR_TYPE_NULL, "TYPE132", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, +{GLDNS_RR_TYPE_NULL, "TYPE133", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, +{GLDNS_RR_TYPE_NULL, "TYPE134", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, +{GLDNS_RR_TYPE_NULL, "TYPE135", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, +{GLDNS_RR_TYPE_NULL, "TYPE136", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, +{GLDNS_RR_TYPE_NULL, "TYPE137", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, +{GLDNS_RR_TYPE_NULL, "TYPE138", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, +{GLDNS_RR_TYPE_NULL, "TYPE139", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, +{GLDNS_RR_TYPE_NULL, "TYPE140", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, +{GLDNS_RR_TYPE_NULL, "TYPE141", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, +{GLDNS_RR_TYPE_NULL, "TYPE142", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, +{GLDNS_RR_TYPE_NULL, "TYPE143", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, +{GLDNS_RR_TYPE_NULL, "TYPE144", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, +{GLDNS_RR_TYPE_NULL, "TYPE145", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, +{GLDNS_RR_TYPE_NULL, "TYPE146", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, +{GLDNS_RR_TYPE_NULL, "TYPE147", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, +{GLDNS_RR_TYPE_NULL, "TYPE148", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, +{GLDNS_RR_TYPE_NULL, "TYPE149", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, +{GLDNS_RR_TYPE_NULL, "TYPE150", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, +{GLDNS_RR_TYPE_NULL, "TYPE151", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, +{GLDNS_RR_TYPE_NULL, "TYPE152", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, +{GLDNS_RR_TYPE_NULL, "TYPE153", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, +{GLDNS_RR_TYPE_NULL, "TYPE154", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, +{GLDNS_RR_TYPE_NULL, "TYPE155", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, +{GLDNS_RR_TYPE_NULL, "TYPE156", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, +{GLDNS_RR_TYPE_NULL, "TYPE157", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, +{GLDNS_RR_TYPE_NULL, "TYPE158", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, +{GLDNS_RR_TYPE_NULL, "TYPE159", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, +{GLDNS_RR_TYPE_NULL, "TYPE160", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, +{GLDNS_RR_TYPE_NULL, "TYPE161", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, +{GLDNS_RR_TYPE_NULL, "TYPE162", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, +{GLDNS_RR_TYPE_NULL, "TYPE163", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, +{GLDNS_RR_TYPE_NULL, "TYPE164", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, +{GLDNS_RR_TYPE_NULL, "TYPE165", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, +{GLDNS_RR_TYPE_NULL, "TYPE166", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, +{GLDNS_RR_TYPE_NULL, "TYPE167", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, +{GLDNS_RR_TYPE_NULL, "TYPE168", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, +{GLDNS_RR_TYPE_NULL, "TYPE169", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, +{GLDNS_RR_TYPE_NULL, "TYPE170", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, +{GLDNS_RR_TYPE_NULL, "TYPE171", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, +{GLDNS_RR_TYPE_NULL, "TYPE172", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, +{GLDNS_RR_TYPE_NULL, "TYPE173", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, +{GLDNS_RR_TYPE_NULL, "TYPE174", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, +{GLDNS_RR_TYPE_NULL, "TYPE175", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, +{GLDNS_RR_TYPE_NULL, "TYPE176", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, +{GLDNS_RR_TYPE_NULL, "TYPE177", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, +{GLDNS_RR_TYPE_NULL, "TYPE178", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, +{GLDNS_RR_TYPE_NULL, "TYPE179", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, +{GLDNS_RR_TYPE_NULL, "TYPE180", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, +{GLDNS_RR_TYPE_NULL, "TYPE181", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, +{GLDNS_RR_TYPE_NULL, "TYPE182", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, +{GLDNS_RR_TYPE_NULL, "TYPE183", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, +{GLDNS_RR_TYPE_NULL, "TYPE184", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, +{GLDNS_RR_TYPE_NULL, "TYPE185", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, +{GLDNS_RR_TYPE_NULL, "TYPE186", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, +{GLDNS_RR_TYPE_NULL, "TYPE187", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, +{GLDNS_RR_TYPE_NULL, "TYPE188", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, +{GLDNS_RR_TYPE_NULL, "TYPE189", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, +{GLDNS_RR_TYPE_NULL, "TYPE190", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, +{GLDNS_RR_TYPE_NULL, "TYPE191", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, +{GLDNS_RR_TYPE_NULL, "TYPE192", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, +{GLDNS_RR_TYPE_NULL, "TYPE193", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, +{GLDNS_RR_TYPE_NULL, "TYPE194", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, +{GLDNS_RR_TYPE_NULL, "TYPE195", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, +{GLDNS_RR_TYPE_NULL, "TYPE196", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, +{GLDNS_RR_TYPE_NULL, "TYPE197", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, +{GLDNS_RR_TYPE_NULL, "TYPE198", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, +{GLDNS_RR_TYPE_NULL, "TYPE199", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, +{GLDNS_RR_TYPE_NULL, "TYPE200", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, +{GLDNS_RR_TYPE_NULL, "TYPE201", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, +{GLDNS_RR_TYPE_NULL, "TYPE202", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, +{GLDNS_RR_TYPE_NULL, "TYPE203", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, +{GLDNS_RR_TYPE_NULL, "TYPE204", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, +{GLDNS_RR_TYPE_NULL, "TYPE205", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, +{GLDNS_RR_TYPE_NULL, "TYPE206", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, +{GLDNS_RR_TYPE_NULL, "TYPE207", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, +{GLDNS_RR_TYPE_NULL, "TYPE208", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, +{GLDNS_RR_TYPE_NULL, "TYPE209", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, +{GLDNS_RR_TYPE_NULL, "TYPE210", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, +{GLDNS_RR_TYPE_NULL, "TYPE211", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, +{GLDNS_RR_TYPE_NULL, "TYPE212", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, +{GLDNS_RR_TYPE_NULL, "TYPE213", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, +{GLDNS_RR_TYPE_NULL, "TYPE214", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, +{GLDNS_RR_TYPE_NULL, "TYPE215", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, +{GLDNS_RR_TYPE_NULL, "TYPE216", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, +{GLDNS_RR_TYPE_NULL, "TYPE217", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, +{GLDNS_RR_TYPE_NULL, "TYPE218", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, +{GLDNS_RR_TYPE_NULL, "TYPE219", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, +{GLDNS_RR_TYPE_NULL, "TYPE220", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, +{GLDNS_RR_TYPE_NULL, "TYPE221", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, +{GLDNS_RR_TYPE_NULL, "TYPE222", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, +{GLDNS_RR_TYPE_NULL, "TYPE223", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, +{GLDNS_RR_TYPE_NULL, "TYPE224", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, +{GLDNS_RR_TYPE_NULL, "TYPE225", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, +{GLDNS_RR_TYPE_NULL, "TYPE226", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, +{GLDNS_RR_TYPE_NULL, "TYPE227", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, +{GLDNS_RR_TYPE_NULL, "TYPE228", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, +{GLDNS_RR_TYPE_NULL, "TYPE229", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, +{GLDNS_RR_TYPE_NULL, "TYPE230", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, +{GLDNS_RR_TYPE_NULL, "TYPE231", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, +{GLDNS_RR_TYPE_NULL, "TYPE232", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, +{GLDNS_RR_TYPE_NULL, "TYPE233", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, +{GLDNS_RR_TYPE_NULL, "TYPE234", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, +{GLDNS_RR_TYPE_NULL, "TYPE235", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, +{GLDNS_RR_TYPE_NULL, "TYPE236", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, +{GLDNS_RR_TYPE_NULL, "TYPE237", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, +{GLDNS_RR_TYPE_NULL, "TYPE238", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, +{GLDNS_RR_TYPE_NULL, "TYPE239", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, +{GLDNS_RR_TYPE_NULL, "TYPE240", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, +{GLDNS_RR_TYPE_NULL, "TYPE241", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, +{GLDNS_RR_TYPE_NULL, "TYPE242", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, +{GLDNS_RR_TYPE_NULL, "TYPE243", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, +{GLDNS_RR_TYPE_NULL, "TYPE244", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, +{GLDNS_RR_TYPE_NULL, "TYPE245", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, +{GLDNS_RR_TYPE_NULL, "TYPE246", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, +{GLDNS_RR_TYPE_NULL, "TYPE247", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, +{GLDNS_RR_TYPE_NULL, "TYPE248", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, + + /* GLDNS_RDF_TYPE_INT16_DATA takes two fields (length and data) as one. + * So, unlike RFC 2930 spec, we have 7 min/max rdf's i.s.o. 8/9. + */ + /* 249 */ + {GLDNS_RR_TYPE_TKEY, "TKEY", 7, 7, type_tkey_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 1 }, + /* GLDNS_RDF_TYPE_INT16_DATA takes two fields (length and data) as one. + * So, unlike RFC 2930 spec, we have 7 min/max rdf's i.s.o. 8/9. + */ + /* 250 */ + {GLDNS_RR_TYPE_TSIG, "TSIG", 7, 7, type_tsig_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 1 }, + + /* IXFR: A request for a transfer of an incremental zone transfer */ +{GLDNS_RR_TYPE_IXFR, "IXFR", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, + /* AXFR: A request for a transfer of an entire zone */ +{GLDNS_RR_TYPE_AXFR, "AXFR", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, + /* MAILB: A request for mailbox-related records (MB, MG or MR) */ +{GLDNS_RR_TYPE_MAILB, "MAILB", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, + /* MAILA: A request for mail agent RRs (Obsolete - see MX) */ +{GLDNS_RR_TYPE_MAILA, "MAILA", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, + /* ANY: A request for all (available) records */ +{GLDNS_RR_TYPE_ANY, "ANY", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, + +#ifdef DRAFT_RRTYPES + /* 256 */ + {GLDNS_RR_TYPE_URI, "URI", 3, 3, type_uri_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, +#else +{GLDNS_RR_TYPE_NULL, "TYPE256", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, +#endif + /* 257 */ + {GLDNS_RR_TYPE_CAA, "CAA", 3, 3, type_caa_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, + +/* split in array, no longer contiguous */ + +#ifdef DRAFT_RRTYPES + /* 32768 */ + {GLDNS_RR_TYPE_TA, "TA", 4, 4, type_ds_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, +#else +{GLDNS_RR_TYPE_NULL, "TYPE32768", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, +#endif + /* 32769 */ + {GLDNS_RR_TYPE_DLV, "DLV", 4, 4, type_ds_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 } +}; + +/** + * \def GLDNS_RDATA_FIELD_DESCRIPTORS_COUNT + * computes the number of rdata fields + */ +#define GLDNS_RDATA_FIELD_DESCRIPTORS_COUNT \ + (sizeof(rdata_field_descriptors)/sizeof(rdata_field_descriptors[0])) + +const gldns_rr_descriptor * +gldns_rr_descript(uint16_t type) +{ + size_t i; + if (type < GLDNS_RDATA_FIELD_DESCRIPTORS_COMMON) { + return &rdata_field_descriptors[type]; + } else { + /* because not all array index equals type code */ + for (i = GLDNS_RDATA_FIELD_DESCRIPTORS_COMMON; + i < GLDNS_RDATA_FIELD_DESCRIPTORS_COUNT; + i++) { + if (rdata_field_descriptors[i]._type == type) { + return &rdata_field_descriptors[i]; + } + } + return &rdata_field_descriptors[0]; + } +} + +size_t +gldns_rr_descriptor_minimum(const gldns_rr_descriptor *descriptor) +{ + if (descriptor) { + return descriptor->_minimum; + } else { + return 0; + } +} + +size_t +gldns_rr_descriptor_maximum(const gldns_rr_descriptor *descriptor) +{ + if (descriptor) { + if (descriptor->_variable != GLDNS_RDF_TYPE_NONE) { + return 65535; /* cannot be more than 64k */ + } else { + return descriptor->_maximum; + } + } else { + return 0; + } +} + +gldns_rdf_type +gldns_rr_descriptor_field_type(const gldns_rr_descriptor *descriptor, + size_t index) +{ + assert(descriptor != NULL); + assert(index < descriptor->_maximum + || descriptor->_variable != GLDNS_RDF_TYPE_NONE); + if (index < descriptor->_maximum) { + return descriptor->_wireformat[index]; + } else { + return descriptor->_variable; + } +} + +gldns_rr_type +gldns_get_rr_type_by_name(const char *name) +{ + unsigned int i; + const char *desc_name; + const gldns_rr_descriptor *desc; + + /* TYPEXX representation */ + if (strlen(name) > 4 && strncasecmp(name, "TYPE", 4) == 0) { + return atoi(name + 4); + } + + /* Normal types */ + for (i = 0; i < (unsigned int) GLDNS_RDATA_FIELD_DESCRIPTORS_COUNT; i++) { + desc = &rdata_field_descriptors[i]; + desc_name = desc->_name; + if(desc_name && + strlen(name) == strlen(desc_name) && + strncasecmp(name, desc_name, strlen(desc_name)) == 0) { + /* because not all array index equals type code */ + return desc->_type; + } + } + + /* special cases for query types */ + if (strlen(name) == 4 && strncasecmp(name, "IXFR", 4) == 0) { + return 251; + } else if (strlen(name) == 4 && strncasecmp(name, "AXFR", 4) == 0) { + return 252; + } else if (strlen(name) == 5 && strncasecmp(name, "MAILB", 5) == 0) { + return 253; + } else if (strlen(name) == 5 && strncasecmp(name, "MAILA", 5) == 0) { + return 254; + } else if (strlen(name) == 3 && strncasecmp(name, "ANY", 3) == 0) { + return 255; + } + + return 0; +} + +gldns_rr_class +gldns_get_rr_class_by_name(const char *name) +{ + gldns_lookup_table *lt; + + /* CLASSXX representation */ + if (strlen(name) > 5 && strncasecmp(name, "CLASS", 5) == 0) { + return atoi(name + 5); + } + + /* Normal types */ + lt = gldns_lookup_by_name(gldns_rr_classes, name); + if (lt) { + return lt->id; + } + return 0; +} diff --git a/src/gldns/rrdef.h b/src/gldns/rrdef.h new file mode 100644 index 00000000..8a14ad36 --- /dev/null +++ b/src/gldns/rrdef.h @@ -0,0 +1,503 @@ +/* + * rrdef.h + * + * RR definitions + * + * a Net::DNS like library for C + * + * (c) NLnet Labs, 2005-2006 + * + * See the file LICENSE for the license + */ + +/** + * \file + * + * Defines resource record types and constants. + */ + +#ifndef GLDNS_RRDEF_H +#define GLDNS_RRDEF_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** Maximum length of a dname label */ +#define GLDNS_MAX_LABELLEN 63 +/** Maximum length of a complete dname */ +#define GLDNS_MAX_DOMAINLEN 255 +/** Maximum number of pointers in 1 dname */ +#define GLDNS_MAX_POINTERS 65535 +/** The bytes TTL, CLASS and length use up in an rr */ +#define GLDNS_RR_OVERHEAD 10 + +#define GLDNS_DNSSEC_KEYPROTO 3 +#define GLDNS_KEY_ZONE_KEY 0x0100 /* set for ZSK&KSK, rfc 4034 */ +#define GLDNS_KEY_SEP_KEY 0x0001 /* set for KSK, rfc 4034 */ +#define GLDNS_KEY_REVOKE_KEY 0x0080 /* used to revoke KSK, rfc 5011 */ + +/* The first fields are contiguous and can be referenced instantly */ +#define GLDNS_RDATA_FIELD_DESCRIPTORS_COMMON 258 + +/** lookuptable for rr classes */ +extern struct gldns_struct_lookup_table* gldns_rr_classes; + +/** + * The different RR classes. + */ +enum gldns_enum_rr_class +{ + /** the Internet */ + GLDNS_RR_CLASS_IN = 1, + /** Chaos class */ + GLDNS_RR_CLASS_CH = 3, + /** Hesiod (Dyer 87) */ + GLDNS_RR_CLASS_HS = 4, + /** None class, dynamic update */ + GLDNS_RR_CLASS_NONE = 254, + /** Any class */ + GLDNS_RR_CLASS_ANY = 255, + + GLDNS_RR_CLASS_FIRST = 0, + GLDNS_RR_CLASS_LAST = 65535, + GLDNS_RR_CLASS_COUNT = GLDNS_RR_CLASS_LAST - GLDNS_RR_CLASS_FIRST + 1 +}; +typedef enum gldns_enum_rr_class gldns_rr_class; + +/** + * Used to specify whether compression is allowed. + */ +enum gldns_enum_rr_compress +{ + /** compression is allowed */ + GLDNS_RR_COMPRESS, + GLDNS_RR_NO_COMPRESS +}; +typedef enum gldns_enum_rr_compress gldns_rr_compress; + +/** + * The different RR types. + */ +enum gldns_enum_rr_type +{ + /** a host address */ + GLDNS_RR_TYPE_A = 1, + /** an authoritative name server */ + GLDNS_RR_TYPE_NS = 2, + /** a mail destination (Obsolete - use MX) */ + GLDNS_RR_TYPE_MD = 3, + /** a mail forwarder (Obsolete - use MX) */ + GLDNS_RR_TYPE_MF = 4, + /** the canonical name for an alias */ + GLDNS_RR_TYPE_CNAME = 5, + /** marks the start of a zone of authority */ + GLDNS_RR_TYPE_SOA = 6, + /** a mailbox domain name (EXPERIMENTAL) */ + GLDNS_RR_TYPE_MB = 7, + /** a mail group member (EXPERIMENTAL) */ + GLDNS_RR_TYPE_MG = 8, + /** a mail rename domain name (EXPERIMENTAL) */ + GLDNS_RR_TYPE_MR = 9, + /** a null RR (EXPERIMENTAL) */ + GLDNS_RR_TYPE_NULL = 10, + /** a well known service description */ + GLDNS_RR_TYPE_WKS = 11, + /** a domain name pointer */ + GLDNS_RR_TYPE_PTR = 12, + /** host information */ + GLDNS_RR_TYPE_HINFO = 13, + /** mailbox or mail list information */ + GLDNS_RR_TYPE_MINFO = 14, + /** mail exchange */ + GLDNS_RR_TYPE_MX = 15, + /** text strings */ + GLDNS_RR_TYPE_TXT = 16, + /** RFC1183 */ + GLDNS_RR_TYPE_RP = 17, + /** RFC1183 */ + GLDNS_RR_TYPE_AFSDB = 18, + /** RFC1183 */ + GLDNS_RR_TYPE_X25 = 19, + /** RFC1183 */ + GLDNS_RR_TYPE_ISDN = 20, + /** RFC1183 */ + GLDNS_RR_TYPE_RT = 21, + /** RFC1706 */ + GLDNS_RR_TYPE_NSAP = 22, + /** RFC1348 */ + GLDNS_RR_TYPE_NSAP_PTR = 23, + /** 2535typecode */ + GLDNS_RR_TYPE_SIG = 24, + /** 2535typecode */ + GLDNS_RR_TYPE_KEY = 25, + /** RFC2163 */ + GLDNS_RR_TYPE_PX = 26, + /** RFC1712 */ + GLDNS_RR_TYPE_GPOS = 27, + /** ipv6 address */ + GLDNS_RR_TYPE_AAAA = 28, + /** LOC record RFC1876 */ + GLDNS_RR_TYPE_LOC = 29, + /** 2535typecode */ + GLDNS_RR_TYPE_NXT = 30, + /** draft-ietf-nimrod-dns-01.txt */ + GLDNS_RR_TYPE_EID = 31, + /** draft-ietf-nimrod-dns-01.txt */ + GLDNS_RR_TYPE_NIMLOC = 32, + /** SRV record RFC2782 */ + GLDNS_RR_TYPE_SRV = 33, + /** http://www.jhsoft.com/rfc/af-saa-0069.000.rtf */ + GLDNS_RR_TYPE_ATMA = 34, + /** RFC2915 */ + GLDNS_RR_TYPE_NAPTR = 35, + /** RFC2230 */ + GLDNS_RR_TYPE_KX = 36, + /** RFC2538 */ + GLDNS_RR_TYPE_CERT = 37, + /** RFC2874 */ + GLDNS_RR_TYPE_A6 = 38, + /** RFC2672 */ + GLDNS_RR_TYPE_DNAME = 39, + /** dnsind-kitchen-sink-02.txt */ + GLDNS_RR_TYPE_SINK = 40, + /** Pseudo OPT record... */ + GLDNS_RR_TYPE_OPT = 41, + /** RFC3123 */ + GLDNS_RR_TYPE_APL = 42, + /** RFC4034, RFC3658 */ + GLDNS_RR_TYPE_DS = 43, + /** SSH Key Fingerprint */ + GLDNS_RR_TYPE_SSHFP = 44, /* RFC 4255 */ + /** IPsec Key */ + GLDNS_RR_TYPE_IPSECKEY = 45, /* RFC 4025 */ + /** DNSSEC */ + GLDNS_RR_TYPE_RRSIG = 46, /* RFC 4034 */ + GLDNS_RR_TYPE_NSEC = 47, /* RFC 4034 */ + GLDNS_RR_TYPE_DNSKEY = 48, /* RFC 4034 */ + + GLDNS_RR_TYPE_DHCID = 49, /* RFC 4701 */ + /* NSEC3 */ + GLDNS_RR_TYPE_NSEC3 = 50, /* RFC 5155 */ + GLDNS_RR_TYPE_NSEC3PARAM = 51, /* RFC 5155 */ + GLDNS_RR_TYPE_NSEC3PARAMS = 51, + GLDNS_RR_TYPE_TLSA = 52, /* RFC 6698 */ + + GLDNS_RR_TYPE_HIP = 55, /* RFC 5205 */ + + /** draft-reid-dnsext-zs */ + GLDNS_RR_TYPE_NINFO = 56, + /** draft-reid-dnsext-rkey */ + GLDNS_RR_TYPE_RKEY = 57, + /** draft-ietf-dnsop-trust-history */ + GLDNS_RR_TYPE_TALINK = 58, + GLDNS_RR_TYPE_CDS = 59, /** RFC 7344 */ + GLDNS_RR_TYPE_CDNSKEY = 60, /** RFC 7344 */ + + GLDNS_RR_TYPE_SPF = 99, /* RFC 4408 */ + + GLDNS_RR_TYPE_UINFO = 100, + GLDNS_RR_TYPE_UID = 101, + GLDNS_RR_TYPE_GID = 102, + GLDNS_RR_TYPE_UNSPEC = 103, + + GLDNS_RR_TYPE_NID = 104, /* RFC 6742 */ + GLDNS_RR_TYPE_L32 = 105, /* RFC 6742 */ + GLDNS_RR_TYPE_L64 = 106, /* RFC 6742 */ + GLDNS_RR_TYPE_LP = 107, /* RFC 6742 */ + + /** draft-jabley-dnsext-eui48-eui64-rrtypes */ + GLDNS_RR_TYPE_EUI48 = 108, + GLDNS_RR_TYPE_EUI64 = 109, + + GLDNS_RR_TYPE_TKEY = 249, /* RFC 2930 */ + GLDNS_RR_TYPE_TSIG = 250, + GLDNS_RR_TYPE_IXFR = 251, + GLDNS_RR_TYPE_AXFR = 252, + /** A request for mailbox-related records (MB, MG or MR) */ + GLDNS_RR_TYPE_MAILB = 253, + /** A request for mail agent RRs (Obsolete - see MX) */ + GLDNS_RR_TYPE_MAILA = 254, + /** any type (wildcard) */ + GLDNS_RR_TYPE_ANY = 255, + /** draft-faltstrom-uri-06 */ + GLDNS_RR_TYPE_URI = 256, + GLDNS_RR_TYPE_CAA = 257, /* RFC 6844 */ + + /** DNSSEC Trust Authorities */ + GLDNS_RR_TYPE_TA = 32768, + /* RFC 4431, 5074, DNSSEC Lookaside Validation */ + GLDNS_RR_TYPE_DLV = 32769, + + /* type codes from nsec3 experimental phase + GLDNS_RR_TYPE_NSEC3 = 65324, + GLDNS_RR_TYPE_NSEC3PARAMS = 65325, */ + GLDNS_RR_TYPE_FIRST = 0, + GLDNS_RR_TYPE_LAST = 65535, + GLDNS_RR_TYPE_COUNT = GLDNS_RR_TYPE_LAST - GLDNS_RR_TYPE_FIRST + 1 +}; +typedef enum gldns_enum_rr_type gldns_rr_type; + +/* RDATA */ +#define GLDNS_MAX_RDFLEN 65535 + +#define GLDNS_RDF_SIZE_BYTE 1 +#define GLDNS_RDF_SIZE_WORD 2 +#define GLDNS_RDF_SIZE_DOUBLEWORD 4 +#define GLDNS_RDF_SIZE_6BYTES 6 +#define GLDNS_RDF_SIZE_8BYTES 8 +#define GLDNS_RDF_SIZE_16BYTES 16 + +#define GLDNS_NSEC3_VARS_OPTOUT_MASK 0x01 + +#define GLDNS_APL_IP4 1 +#define GLDNS_APL_IP6 2 +#define GLDNS_APL_MASK 0x7f +#define GLDNS_APL_NEGATION 0x80 + +/** + * The different types of RDATA fields. + */ +enum gldns_enum_rdf_type +{ + /** none */ + GLDNS_RDF_TYPE_NONE, + /** domain name */ + GLDNS_RDF_TYPE_DNAME, + /** 8 bits */ + GLDNS_RDF_TYPE_INT8, + /** 16 bits */ + GLDNS_RDF_TYPE_INT16, + /** 32 bits */ + GLDNS_RDF_TYPE_INT32, + /** A record */ + GLDNS_RDF_TYPE_A, + /** AAAA record */ + GLDNS_RDF_TYPE_AAAA, + /** txt string */ + GLDNS_RDF_TYPE_STR, + /** apl data */ + GLDNS_RDF_TYPE_APL, + /** b32 string */ + GLDNS_RDF_TYPE_B32_EXT, + /** b64 string */ + GLDNS_RDF_TYPE_B64, + /** hex string */ + GLDNS_RDF_TYPE_HEX, + /** nsec type codes */ + GLDNS_RDF_TYPE_NSEC, + /** a RR type */ + GLDNS_RDF_TYPE_TYPE, + /** a class */ + GLDNS_RDF_TYPE_CLASS, + /** certificate algorithm */ + GLDNS_RDF_TYPE_CERT_ALG, + /** a key algorithm */ + GLDNS_RDF_TYPE_ALG, + /** unknown types */ + GLDNS_RDF_TYPE_UNKNOWN, + /** time (32 bits) */ + GLDNS_RDF_TYPE_TIME, + /** period */ + GLDNS_RDF_TYPE_PERIOD, + /** tsig time 48 bits */ + GLDNS_RDF_TYPE_TSIGTIME, + /** Represents the Public Key Algorithm, HIT and Public Key fields + for the HIP RR types. A HIP specific rdf type is used because of + the unusual layout in wireformat (see RFC 5205 Section 5) */ + GLDNS_RDF_TYPE_HIP, + /** variable length any type rdata where the length + is specified by the first 2 bytes */ + GLDNS_RDF_TYPE_INT16_DATA, + /** protocol and port bitmaps */ + GLDNS_RDF_TYPE_SERVICE, + /** location data */ + GLDNS_RDF_TYPE_LOC, + /** well known services */ + GLDNS_RDF_TYPE_WKS, + /** NSAP */ + GLDNS_RDF_TYPE_NSAP, + /** ATMA */ + GLDNS_RDF_TYPE_ATMA, + /** IPSECKEY */ + GLDNS_RDF_TYPE_IPSECKEY, + /** nsec3 hash salt */ + GLDNS_RDF_TYPE_NSEC3_SALT, + /** nsec3 base32 string (with length byte on wire */ + GLDNS_RDF_TYPE_NSEC3_NEXT_OWNER, + + /** 4 shorts represented as 4 * 16 bit hex numbers + * seperated by colons. For NID and L64. + */ + GLDNS_RDF_TYPE_ILNP64, + + /** 6 * 8 bit hex numbers seperated by dashes. For EUI48. */ + GLDNS_RDF_TYPE_EUI48, + /** 8 * 8 bit hex numbers seperated by dashes. For EUI64. */ + GLDNS_RDF_TYPE_EUI64, + + /** A non-zero sequence of US-ASCII letters and numbers in lower case. + * For CAA. + */ + GLDNS_RDF_TYPE_TAG, + + /** A encoding of the value field as specified + * [RFC1035], Section 5.1., encoded as remaining rdata. + * For CAA. + */ + GLDNS_RDF_TYPE_LONG_STR, + + /* Aliases */ + GLDNS_RDF_TYPE_BITMAP = GLDNS_RDF_TYPE_NSEC +}; +typedef enum gldns_enum_rdf_type gldns_rdf_type; + +/** + * Algorithms used in dns + */ +enum gldns_enum_algorithm +{ + GLDNS_RSAMD5 = 1, /* RFC 4034,4035 */ + GLDNS_DH = 2, + GLDNS_DSA = 3, + GLDNS_ECC = 4, + GLDNS_RSASHA1 = 5, + GLDNS_DSA_NSEC3 = 6, + GLDNS_RSASHA1_NSEC3 = 7, + GLDNS_RSASHA256 = 8, /* RFC 5702 */ + GLDNS_RSASHA512 = 10, /* RFC 5702 */ + GLDNS_ECC_GOST = 12, /* RFC 5933 */ + GLDNS_ECDSAP256SHA256 = 13, /* RFC 6605 */ + GLDNS_ECDSAP384SHA384 = 14, /* RFC 6605 */ + GLDNS_INDIRECT = 252, + GLDNS_PRIVATEDNS = 253, + GLDNS_PRIVATEOID = 254 +}; +typedef enum gldns_enum_algorithm gldns_algorithm; + +/** + * Hashing algorithms used in the DS record + */ +enum gldns_enum_hash +{ + GLDNS_SHA1 = 1, /* RFC 4034 */ + GLDNS_SHA256 = 2, /* RFC 4509 */ + GLDNS_HASH_GOST = 3, /* RFC 5933 */ + GLDNS_SHA384 = 4 /* RFC 6605 */ +}; +typedef enum gldns_enum_hash gldns_hash; + +/** + * algorithms used in CERT rrs + */ +enum gldns_enum_cert_algorithm +{ + GLDNS_CERT_PKIX = 1, + GLDNS_CERT_SPKI = 2, + GLDNS_CERT_PGP = 3, + GLDNS_CERT_IPKIX = 4, + GLDNS_CERT_ISPKI = 5, + GLDNS_CERT_IPGP = 6, + GLDNS_CERT_ACPKIX = 7, + GLDNS_CERT_IACPKIX = 8, + GLDNS_CERT_URI = 253, + GLDNS_CERT_OID = 254 +}; +typedef enum gldns_enum_cert_algorithm gldns_cert_algorithm; + +/** + * EDNS option codes + */ +enum gldns_enum_edns_option +{ + GLDNS_EDNS_LLQ = 1, /* http://files.dns-sd.org/draft-sekar-dns-llq.txt */ + GLDNS_EDNS_UL = 2, /* http://files.dns-sd.org/draft-sekar-dns-ul.txt */ + GLDNS_EDNS_NSID = 3, /* RFC5001 */ + /* 4 draft-cheshire-edns0-owner-option */ + GLDNS_EDNS_DAU = 5, /* RFC6975 */ + GLDNS_EDNS_DHU = 6, /* RFC6975 */ + GLDNS_EDNS_N3U = 7, /* RFC6975 */ + GLDNS_EDNS_CLIENT_SUBNET = 8 /* draft-vandergaast-edns-client-subnet */ +}; +typedef enum gldns_enum_edns_option gldns_edns_option; + +#define GLDNS_EDNS_MASK_DO_BIT 0x8000 + +/** + * Contains all information about resource record types. + * + * This structure contains, for all rr types, the rdata fields that are defined. + */ +struct gldns_struct_rr_descriptor +{ + /** Type of the RR that is described here */ + gldns_rr_type _type; + /** Textual name of the RR type. */ + const char *_name; + /** Minimum number of rdata fields in the RRs of this type. */ + uint8_t _minimum; + /** Maximum number of rdata fields in the RRs of this type. */ + uint8_t _maximum; + /** Wireformat specification for the rr, i.e. the types of rdata fields in their respective order. */ + const gldns_rdf_type *_wireformat; + /** Special rdf types */ + gldns_rdf_type _variable; + /** Specifies whether compression can be used for dnames in this RR type. */ + gldns_rr_compress _compress; + /** The number of DNAMEs in the _wireformat string, for parsing. */ + uint8_t _dname_count; +}; +typedef struct gldns_struct_rr_descriptor gldns_rr_descriptor; + +/** + * returns the resource record descriptor for the given rr type. + * + * \param[in] type the type value of the rr type + *\return the gldns_rr_descriptor for this type + */ +const gldns_rr_descriptor *gldns_rr_descript(uint16_t type); + +/** + * returns the minimum number of rdata fields of the rr type this descriptor describes. + * + * \param[in] descriptor for an rr type + * \return the minimum number of rdata fields + */ +size_t gldns_rr_descriptor_minimum(const gldns_rr_descriptor *descriptor); + +/** + * returns the maximum number of rdata fields of the rr type this descriptor describes. + * + * \param[in] descriptor for an rr type + * \return the maximum number of rdata fields + */ +size_t gldns_rr_descriptor_maximum(const gldns_rr_descriptor *descriptor); + +/** + * returns the rdf type for the given rdata field number of the rr type for the given descriptor. + * + * \param[in] descriptor for an rr type + * \param[in] field the field number + * \return the rdf type for the field + */ +gldns_rdf_type gldns_rr_descriptor_field_type(const gldns_rr_descriptor *descriptor, size_t field); + +/** + * retrieves a rrtype by looking up its name. + * \param[in] name a string with the name + * \return the type which corresponds with the name + */ +gldns_rr_type gldns_get_rr_type_by_name(const char *name); + +/** + * retrieves a class by looking up its name. + * \param[in] name string with the name + * \return the cass which corresponds with the name + */ +gldns_rr_class gldns_get_rr_class_by_name(const char *name); + +#ifdef __cplusplus +} +#endif + +#endif /* GLDNS_RRDEF_H */ diff --git a/src/gldns/sbuffer.c b/src/gldns/sbuffer.c new file mode 100644 index 00000000..db19ef42 --- /dev/null +++ b/src/gldns/sbuffer.c @@ -0,0 +1,178 @@ +/* + * buffer.c -- generic memory buffer . + * + * Copyright (c) 2001-2008, NLnet Labs. All rights reserved. + * + * See LICENSE for the license. + * + */ +/** + * \file + * + * This file contains the definition of gldns_buffer, and functions to manipulate those. + */ +#include "config.h" +#include "gldns/sbuffer.h" +#include + +gldns_buffer * +gldns_buffer_new(size_t capacity) +{ + gldns_buffer *buffer = (gldns_buffer*)malloc(sizeof(gldns_buffer)); + + if (!buffer) { + return NULL; + } + + buffer->_data = (uint8_t *) malloc(capacity); + if (!buffer->_data) { + free(buffer); + return NULL; + } + + buffer->_position = 0; + buffer->_limit = buffer->_capacity = capacity; + buffer->_fixed = 0; + buffer->_status_err = 0; + + gldns_buffer_invariant(buffer); + + return buffer; +} + +void +gldns_buffer_new_frm_data(gldns_buffer *buffer, void *data, size_t size) +{ + assert(data != NULL); + + buffer->_position = 0; + buffer->_limit = buffer->_capacity = size; + buffer->_fixed = 0; + buffer->_data = malloc(size); + if(!buffer->_data) { + buffer->_status_err = 1; + return; + } + memcpy(buffer->_data, data, size); + buffer->_status_err = 0; + + gldns_buffer_invariant(buffer); +} + +void +gldns_buffer_init_frm_data(gldns_buffer *buffer, void *data, size_t size) +{ + memset(buffer, 0, sizeof(*buffer)); + buffer->_data = data; + buffer->_capacity = buffer->_limit = size; + buffer->_fixed = 1; +} + +int +gldns_buffer_set_capacity(gldns_buffer *buffer, size_t capacity) +{ + void *data; + + gldns_buffer_invariant(buffer); + assert(buffer->_position <= capacity); + + data = (uint8_t *) realloc(buffer->_data, capacity); + if (!data) { + buffer->_status_err = 1; + return 0; + } else { + buffer->_data = data; + buffer->_limit = buffer->_capacity = capacity; + return 1; + } +} + +int +gldns_buffer_reserve(gldns_buffer *buffer, size_t amount) +{ + gldns_buffer_invariant(buffer); + assert(!buffer->_fixed); + if (buffer->_capacity < buffer->_position + amount) { + size_t new_capacity = buffer->_capacity * 3 / 2; + + if (new_capacity < buffer->_position + amount) { + new_capacity = buffer->_position + amount; + } + if (!gldns_buffer_set_capacity(buffer, new_capacity)) { + buffer->_status_err = 1; + return 0; + } + } + buffer->_limit = buffer->_capacity; + return 1; +} + +int +gldns_buffer_printf(gldns_buffer *buffer, const char *format, ...) +{ + va_list args; + int written = 0; + size_t remaining; + + if (gldns_buffer_status_ok(buffer)) { + gldns_buffer_invariant(buffer); + assert(buffer->_limit == buffer->_capacity); + + remaining = gldns_buffer_remaining(buffer); + va_start(args, format); + written = vsnprintf((char *) gldns_buffer_current(buffer), remaining, + format, args); + va_end(args); + if (written == -1) { + buffer->_status_err = 1; + return -1; + } else if ((size_t) written >= remaining) { + if (!gldns_buffer_reserve(buffer, (size_t) written + 1)) { + buffer->_status_err = 1; + return -1; + } + va_start(args, format); + written = vsnprintf((char *) gldns_buffer_current(buffer), + gldns_buffer_remaining(buffer), format, args); + va_end(args); + if (written == -1) { + buffer->_status_err = 1; + return -1; + } + } + buffer->_position += written; + } + return written; +} + +void +gldns_buffer_free(gldns_buffer *buffer) +{ + if (!buffer) { + return; + } + + if (!buffer->_fixed) + free(buffer->_data); + + free(buffer); +} + +void * +gldns_buffer_export(gldns_buffer *buffer) +{ + buffer->_fixed = 1; + return buffer->_data; +} + +void +gldns_buffer_copy(gldns_buffer* result, gldns_buffer* from) +{ + size_t tocopy = gldns_buffer_limit(from); + + if(tocopy > gldns_buffer_capacity(result)) + tocopy = gldns_buffer_capacity(result); + gldns_buffer_clear(result); + gldns_buffer_write(result, gldns_buffer_begin(from), tocopy); + gldns_buffer_flip(result); +} diff --git a/src/gldns/sbuffer.h b/src/gldns/sbuffer.h new file mode 100644 index 00000000..01806310 --- /dev/null +++ b/src/gldns/sbuffer.h @@ -0,0 +1,706 @@ +/* + * buffer.h -- generic memory buffer. + * + * Copyright (c) 2005-2008, NLnet Labs. All rights reserved. + * + * See LICENSE for the license. + * + * + * The buffer module implements a generic buffer. The API is based on + * the java.nio.Buffer interface. + */ + +#ifndef GLDNS_SBUFFER_H +#define GLDNS_SBUFFER_H + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef S_SPLINT_S +# define INLINE +#else +# ifdef SWIG +# define INLINE static +# else +# define INLINE static inline +# endif +#endif + +/* + * Copy data allowing for unaligned accesses in network byte order + * (big endian). + */ +INLINE uint16_t +gldns_read_uint16(const void *src) +{ +#ifdef ALLOW_UNALIGNED_ACCESSES + return ntohs(*(uint16_t *) src); +#else + uint8_t *p = (uint8_t *) src; + return ((uint16_t) p[0] << 8) | (uint16_t) p[1]; +#endif +} + +INLINE uint32_t +gldns_read_uint32(const void *src) +{ +#ifdef ALLOW_UNALIGNED_ACCESSES + return ntohl(*(uint32_t *) src); +#else + uint8_t *p = (uint8_t *) src; + return ( ((uint32_t) p[0] << 24) + | ((uint32_t) p[1] << 16) + | ((uint32_t) p[2] << 8) + | (uint32_t) p[3]); +#endif +} + +/* + * Copy data allowing for unaligned accesses in network byte order + * (big endian). + */ +INLINE void +gldns_write_uint16(void *dst, uint16_t data) +{ +#ifdef ALLOW_UNALIGNED_ACCESSES + * (uint16_t *) dst = htons(data); +#else + uint8_t *p = (uint8_t *) dst; + p[0] = (uint8_t) ((data >> 8) & 0xff); + p[1] = (uint8_t) (data & 0xff); +#endif +} + +INLINE void +gldns_write_uint32(void *dst, uint32_t data) +{ +#ifdef ALLOW_UNALIGNED_ACCESSES + * (uint32_t *) dst = htonl(data); +#else + uint8_t *p = (uint8_t *) dst; + p[0] = (uint8_t) ((data >> 24) & 0xff); + p[1] = (uint8_t) ((data >> 16) & 0xff); + p[2] = (uint8_t) ((data >> 8) & 0xff); + p[3] = (uint8_t) (data & 0xff); +#endif +} + + +/** + * \file sbuffer.h + * + * This file contains the definition of gldns_buffer, and functions to manipulate those. + */ + +/** + * implementation of buffers to ease operations + * + * gldns_buffers can contain arbitrary information, per octet. You can write + * to the current end of a buffer, read from the current position, and + * access any data within it. + */ +struct gldns_buffer +{ + /** The current position used for reading/writing */ + size_t _position; + + /** The read/write limit */ + size_t _limit; + + /** The amount of data the buffer can contain */ + size_t _capacity; + + /** The data contained in the buffer */ + uint8_t *_data; + + /** If the buffer is fixed it cannot be resized */ + unsigned _fixed : 1; + + /** The current state of the buffer. If writing to the buffer fails + * for any reason, this value is changed. This way, you can perform + * multiple writes in sequence and check for success afterwards. */ + unsigned _status_err : 1; +}; +typedef struct gldns_buffer gldns_buffer; + +#ifdef NDEBUG +INLINE void +gldns_buffer_invariant(gldns_buffer *ATTR_UNUSED(buffer)) +{ +} +#else +INLINE void +gldns_buffer_invariant(gldns_buffer *buffer) +{ + assert(buffer != NULL); + assert(buffer->_position <= buffer->_limit); + assert(buffer->_limit <= buffer->_capacity); + assert(buffer->_data != NULL); +} +#endif + +/** + * creates a new buffer with the specified capacity. + * + * \param[in] capacity the size (in bytes) to allocate for the buffer + * \return the created buffer + */ +gldns_buffer *gldns_buffer_new(size_t capacity); + +/** + * creates a buffer with the specified data. The data IS copied + * and MEMORY allocations are done. The buffer is not fixed and can + * be resized using buffer_reserve(). + * + * \param[in] buffer pointer to the buffer to put the data in + * \param[in] data the data to encapsulate in the buffer + * \param[in] size the size of the data + */ +void gldns_buffer_new_frm_data(gldns_buffer *buffer, void *data, size_t size); + +/** + * Setup a buffer with the data pointed to. No data copied, no memory allocs. + * The buffer is fixed. + * \param[in] buffer pointer to the buffer to put the data in + * \param[in] data the data to encapsulate in the buffer + * \param[in] size the size of the data + */ +void gldns_buffer_init_frm_data(gldns_buffer *buffer, void *data, size_t size); + +/** + * clears the buffer and make it ready for writing. The buffer's limit + * is set to the capacity and the position is set to 0. + * \param[in] buffer the buffer to clear + */ +INLINE void gldns_buffer_clear(gldns_buffer *buffer) +{ + gldns_buffer_invariant(buffer); + + /* reset status here? */ + + buffer->_position = 0; + buffer->_limit = buffer->_capacity; +} + +/** + * makes the buffer ready for reading the data that has been written to + * the buffer. The buffer's limit is set to the current position and + * the position is set to 0. + * + * \param[in] buffer the buffer to flip + * \return void + */ +INLINE void gldns_buffer_flip(gldns_buffer *buffer) +{ + gldns_buffer_invariant(buffer); + + buffer->_limit = buffer->_position; + buffer->_position = 0; +} + +/** + * make the buffer ready for re-reading the data. The buffer's + * position is reset to 0. + * \param[in] buffer the buffer to rewind + */ +INLINE void gldns_buffer_rewind(gldns_buffer *buffer) +{ + gldns_buffer_invariant(buffer); + + buffer->_position = 0; +} + +/** + * returns the current position in the buffer (as a number of bytes) + * \param[in] buffer the buffer + * \return the current position + */ +INLINE size_t +gldns_buffer_position(gldns_buffer *buffer) +{ + return buffer->_position; +} + +/** + * sets the buffer's position to MARK. The position must be less than + * or equal to the buffer's limit. + * \param[in] buffer the buffer + * \param[in] mark the mark to use + */ +INLINE void +gldns_buffer_set_position(gldns_buffer *buffer, size_t mark) +{ + assert(mark <= buffer->_limit); + buffer->_position = mark; +} + +/** + * changes the buffer's position by COUNT bytes. The position must not + * be moved behind the buffer's limit or before the beginning of the + * buffer. + * \param[in] buffer the buffer + * \param[in] count the count to use + */ +INLINE void +gldns_buffer_skip(gldns_buffer *buffer, ssize_t count) +{ + assert(buffer->_position + count <= buffer->_limit); + buffer->_position += count; +} + +/** + * returns the maximum size of the buffer + * \param[in] buffer + * \return the size + */ +INLINE size_t +gldns_buffer_limit(gldns_buffer *buffer) +{ + return buffer->_limit; +} + +/** + * changes the buffer's limit. If the buffer's position is greater + * than the new limit the position is set to the limit. + * \param[in] buffer the buffer + * \param[in] limit the new limit + */ +INLINE void +gldns_buffer_set_limit(gldns_buffer *buffer, size_t limit) +{ + assert(limit <= buffer->_capacity); + buffer->_limit = limit; + if (buffer->_position > buffer->_limit) + buffer->_position = buffer->_limit; +} + +/** + * returns the number of bytes the buffer can hold. + * \param[in] buffer the buffer + * \return the number of bytes + */ +INLINE size_t +gldns_buffer_capacity(gldns_buffer *buffer) +{ + return buffer->_capacity; +} + +/** + * changes the buffer's capacity. The data is reallocated so any + * pointers to the data may become invalid. The buffer's limit is set + * to the buffer's new capacity. + * \param[in] buffer the buffer + * \param[in] capacity the capacity to use + * \return whether this failed or succeeded + */ +int gldns_buffer_set_capacity(gldns_buffer *buffer, size_t capacity); + +/** + * ensures BUFFER can contain at least AMOUNT more bytes. The buffer's + * capacity is increased if necessary using buffer_set_capacity(). + * + * The buffer's limit is always set to the (possibly increased) + * capacity. + * \param[in] buffer the buffer + * \param[in] amount amount to use + * \return whether this failed or succeeded + */ +int gldns_buffer_reserve(gldns_buffer *buffer, size_t amount); + +/** + * returns a pointer to the data at the indicated position. + * \param[in] buffer the buffer + * \param[in] at position + * \return the pointer to the data + */ +INLINE uint8_t * +gldns_buffer_at(const gldns_buffer *buffer, size_t at) +{ + assert(at <= buffer->_limit); + return buffer->_data + at; +} + +/** + * returns a pointer to the beginning of the buffer (the data at + * position 0). + * \param[in] buffer the buffer + * \return the pointer + */ +INLINE uint8_t * +gldns_buffer_begin(const gldns_buffer *buffer) +{ + return gldns_buffer_at(buffer, 0); +} + +/** + * returns a pointer to the end of the buffer (the data at the buffer's + * limit). + * \param[in] buffer the buffer + * \return the pointer + */ +INLINE uint8_t * +gldns_buffer_end(gldns_buffer *buffer) +{ + return gldns_buffer_at(buffer, buffer->_limit); +} + +/** + * returns a pointer to the data at the buffer's current position. + * \param[in] buffer the buffer + * \return the pointer + */ +INLINE uint8_t * +gldns_buffer_current(gldns_buffer *buffer) +{ + return gldns_buffer_at(buffer, buffer->_position); +} + +/** + * returns the number of bytes remaining between the indicated position and + * the limit. + * \param[in] buffer the buffer + * \param[in] at indicated position + * \return number of bytes + */ +INLINE size_t +gldns_buffer_remaining_at(gldns_buffer *buffer, size_t at) +{ + gldns_buffer_invariant(buffer); + assert(at <= buffer->_limit); + return buffer->_limit - at; +} + +/** + * returns the number of bytes remaining between the buffer's position and + * limit. + * \param[in] buffer the buffer + * \return the number of bytes + */ +INLINE size_t +gldns_buffer_remaining(gldns_buffer *buffer) +{ + return gldns_buffer_remaining_at(buffer, buffer->_position); +} + +/** + * checks if the buffer has at least COUNT more bytes available. + * Before reading or writing the caller needs to ensure enough space + * is available! + * \param[in] buffer the buffer + * \param[in] at indicated position + * \param[in] count how much is available + * \return true or false (as int?) + */ +INLINE int +gldns_buffer_available_at(gldns_buffer *buffer, size_t at, size_t count) +{ + return count <= gldns_buffer_remaining_at(buffer, at); +} + +/** + * checks if the buffer has count bytes available at the current position + * \param[in] buffer the buffer + * \param[in] count how much is available + * \return true or false (as int?) + */ +INLINE int +gldns_buffer_available(gldns_buffer *buffer, size_t count) +{ + return gldns_buffer_available_at(buffer, buffer->_position, count); +} + +/** + * writes the given data to the buffer at the specified position + * \param[in] buffer the buffer + * \param[in] at the position (in number of bytes) to write the data at + * \param[in] data pointer to the data to write to the buffer + * \param[in] count the number of bytes of data to write + */ +INLINE void +gldns_buffer_write_at(gldns_buffer *buffer, size_t at, const void *data, size_t count) +{ + assert(gldns_buffer_available_at(buffer, at, count)); + memcpy(buffer->_data + at, data, count); +} + +/** + * writes count bytes of data to the current position of the buffer + * \param[in] buffer the buffer + * \param[in] data the data to write + * \param[in] count the lenght of the data to write + */ +INLINE void +gldns_buffer_write(gldns_buffer *buffer, const void *data, size_t count) +{ + gldns_buffer_write_at(buffer, buffer->_position, data, count); + buffer->_position += count; +} + +/** + * copies the given (null-delimited) string to the specified position at the buffer + * \param[in] buffer the buffer + * \param[in] at the position in the buffer + * \param[in] str the string to write + */ +INLINE void +gldns_buffer_write_string_at(gldns_buffer *buffer, size_t at, const char *str) +{ + gldns_buffer_write_at(buffer, at, str, strlen(str)); +} + +/** + * copies the given (null-delimited) string to the current position at the buffer + * \param[in] buffer the buffer + * \param[in] str the string to write + */ +INLINE void +gldns_buffer_write_string(gldns_buffer *buffer, const char *str) +{ + gldns_buffer_write(buffer, str, strlen(str)); +} + +/** + * writes the given byte of data at the given position in the buffer + * \param[in] buffer the buffer + * \param[in] at the position in the buffer + * \param[in] data the 8 bits to write + */ +INLINE void +gldns_buffer_write_u8_at(gldns_buffer *buffer, size_t at, uint8_t data) +{ + assert(gldns_buffer_available_at(buffer, at, sizeof(data))); + buffer->_data[at] = data; +} + +/** + * writes the given byte of data at the current position in the buffer + * \param[in] buffer the buffer + * \param[in] data the 8 bits to write + */ +INLINE void +gldns_buffer_write_u8(gldns_buffer *buffer, uint8_t data) +{ + gldns_buffer_write_u8_at(buffer, buffer->_position, data); + buffer->_position += sizeof(data); +} + +/** + * writes the given 2 byte integer at the given position in the buffer + * \param[in] buffer the buffer + * \param[in] at the position in the buffer + * \param[in] data the 16 bits to write + */ +INLINE void +gldns_buffer_write_u16_at(gldns_buffer *buffer, size_t at, uint16_t data) +{ + assert(gldns_buffer_available_at(buffer, at, sizeof(data))); + gldns_write_uint16(buffer->_data + at, data); +} + +/** + * writes the given 2 byte integer at the current position in the buffer + * \param[in] buffer the buffer + * \param[in] data the 16 bits to write + */ +INLINE void +gldns_buffer_write_u16(gldns_buffer *buffer, uint16_t data) +{ + gldns_buffer_write_u16_at(buffer, buffer->_position, data); + buffer->_position += sizeof(data); +} + +/** + * writes the given 4 byte integer at the given position in the buffer + * \param[in] buffer the buffer + * \param[in] at the position in the buffer + * \param[in] data the 32 bits to write + */ +INLINE void +gldns_buffer_write_u32_at(gldns_buffer *buffer, size_t at, uint32_t data) +{ + assert(gldns_buffer_available_at(buffer, at, sizeof(data))); + gldns_write_uint32(buffer->_data + at, data); +} + +/** + * writes the given 4 byte integer at the current position in the buffer + * \param[in] buffer the buffer + * \param[in] data the 32 bits to write + */ +INLINE void +gldns_buffer_write_u32(gldns_buffer *buffer, uint32_t data) +{ + gldns_buffer_write_u32_at(buffer, buffer->_position, data); + buffer->_position += sizeof(data); +} + +/** + * copies count bytes of data at the given position to the given data-array + * \param[in] buffer the buffer + * \param[in] at the position in the buffer to start + * \param[out] data buffer to copy to + * \param[in] count the length of the data to copy + */ +INLINE void +gldns_buffer_read_at(gldns_buffer *buffer, size_t at, void *data, size_t count) +{ + assert(gldns_buffer_available_at(buffer, at, count)); + memcpy(data, buffer->_data + at, count); +} + +/** + * copies count bytes of data at the current position to the given data-array + * \param[in] buffer the buffer + * \param[out] data buffer to copy to + * \param[in] count the length of the data to copy + */ +INLINE void +gldns_buffer_read(gldns_buffer *buffer, void *data, size_t count) +{ + gldns_buffer_read_at(buffer, buffer->_position, data, count); + buffer->_position += count; +} + +/** + * returns the byte value at the given position in the buffer + * \param[in] buffer the buffer + * \param[in] at the position in the buffer + * \return 1 byte integer + */ +INLINE uint8_t +gldns_buffer_read_u8_at(gldns_buffer *buffer, size_t at) +{ + assert(gldns_buffer_available_at(buffer, at, sizeof(uint8_t))); + return buffer->_data[at]; +} + +/** + * returns the byte value at the current position in the buffer + * \param[in] buffer the buffer + * \return 1 byte integer + */ +INLINE uint8_t +gldns_buffer_read_u8(gldns_buffer *buffer) +{ + uint8_t result = gldns_buffer_read_u8_at(buffer, buffer->_position); + buffer->_position += sizeof(uint8_t); + return result; +} + +/** + * returns the 2-byte integer value at the given position in the buffer + * \param[in] buffer the buffer + * \param[in] at position in the buffer + * \return 2 byte integer + */ +INLINE uint16_t +gldns_buffer_read_u16_at(gldns_buffer *buffer, size_t at) +{ + assert(gldns_buffer_available_at(buffer, at, sizeof(uint16_t))); + return gldns_read_uint16(buffer->_data + at); +} + +/** + * returns the 2-byte integer value at the current position in the buffer + * \param[in] buffer the buffer + * \return 2 byte integer + */ +INLINE uint16_t +gldns_buffer_read_u16(gldns_buffer *buffer) +{ + uint16_t result = gldns_buffer_read_u16_at(buffer, buffer->_position); + buffer->_position += sizeof(uint16_t); + return result; +} + +/** + * returns the 4-byte integer value at the given position in the buffer + * \param[in] buffer the buffer + * \param[in] at position in the buffer + * \return 4 byte integer + */ +INLINE uint32_t +gldns_buffer_read_u32_at(gldns_buffer *buffer, size_t at) +{ + assert(gldns_buffer_available_at(buffer, at, sizeof(uint32_t))); + return gldns_read_uint32(buffer->_data + at); +} + +/** + * returns the 4-byte integer value at the current position in the buffer + * \param[in] buffer the buffer + * \return 4 byte integer + */ +INLINE uint32_t +gldns_buffer_read_u32(gldns_buffer *buffer) +{ + uint32_t result = gldns_buffer_read_u32_at(buffer, buffer->_position); + buffer->_position += sizeof(uint32_t); + return result; +} + +/** + * returns the status of the buffer + * \param[in] buffer + * \return the status + */ +INLINE int +gldns_buffer_status(gldns_buffer *buffer) +{ + return (int)buffer->_status_err; +} + +/** + * returns true if the status of the buffer is GLDNS_STATUS_OK, false otherwise + * \param[in] buffer the buffer + * \return true or false + */ +INLINE int +gldns_buffer_status_ok(gldns_buffer *buffer) +{ + if (buffer) { + return gldns_buffer_status(buffer) == 0; + } else { + return 0; + } +} + +/** + * prints to the buffer, increasing the capacity if required using + * buffer_reserve(). The buffer's position is set to the terminating '\\0' + * Returns the number of characters written (not including the + * terminating '\\0') or -1 on failure. + */ +int gldns_buffer_printf(gldns_buffer *buffer, const char *format, ...) + ATTR_FORMAT(printf, 2, 3); + +/** + * frees the buffer. + * \param[in] *buffer the buffer to be freed + * \return void + */ +void gldns_buffer_free(gldns_buffer *buffer); + +/** + * Makes the buffer fixed and returns a pointer to the data. The + * caller is responsible for free'ing the result. + * \param[in] *buffer the buffer to be exported + * \return void + */ +void *gldns_buffer_export(gldns_buffer *buffer); + +/** + * Copy contents of the from buffer to the result buffer and then flips + * the result buffer. Data will be silently truncated if the result buffer is + * too small. + * \param[out] *result resulting buffer which is copied to. + * \param[in] *from what to copy to result. + */ +void gldns_buffer_copy(gldns_buffer* result, gldns_buffer* from); + +#ifdef __cplusplus +} +#endif + +#endif /* GLDNS_SBUFFER_H */ diff --git a/src/gldns/str2wire.c b/src/gldns/str2wire.c new file mode 100644 index 00000000..e01e63ef --- /dev/null +++ b/src/gldns/str2wire.c @@ -0,0 +1,2001 @@ +/** + * str2wire.c - read txt presentation of RRs + * + * (c) NLnet Labs, 2005-2006 + * + * See the file LICENSE for the license + */ + +/** + * \file + * + * Parses text to wireformat. + */ +#include "config.h" +#include "gldns/str2wire.h" +#include "gldns/wire2str.h" +#include "gldns/sbuffer.h" +#include "gldns/parse.h" +#include "gldns/parseutil.h" +#include +#ifdef HAVE_TIME_H +#include +#endif +#ifdef HAVE_NETDB_H +#include +#endif + +/** return an error */ +#define RET_ERR(e, off) ((int)((e)|((off)< GLDNS_MAX_DOMAINLEN * 4) { + return RET_ERR(GLDNS_WIREPARSE_ERR_DOMAINNAME_OVERFLOW, 0); + } + if (0 == len) { + return RET_ERR(GLDNS_WIREPARSE_ERR_DOMAINNAME_UNDERFLOW, 0); + } + + /* root label */ + if (1 == len && *str == '.') { + if(*olen < 1) + return RET_ERR(GLDNS_WIREPARSE_ERR_BUFFER_TOO_SMALL, 0); + buf[0] = 0; + *olen = 1; + return GLDNS_WIREPARSE_ERR_OK; + } + + /* get on with the rest */ + + /* s is on the current character in the string + * pq points to where the labellength is going to go + * label_len keeps track of the current label's length + * q builds the dname inside the buf array + */ + len = 0; + if(*olen < 1) + return RET_ERR(GLDNS_WIREPARSE_ERR_BUFFER_TOO_SMALL, 0); + q = buf+1; + pq = buf; + label_len = 0; + for (s = str; *s; s++, q++) { + if (q >= buf + *olen) + return RET_ERR(GLDNS_WIREPARSE_ERR_BUFFER_TOO_SMALL, q-buf); + if (q > buf + GLDNS_MAX_DOMAINLEN) + return RET_ERR(GLDNS_WIREPARSE_ERR_DOMAINNAME_OVERFLOW, q-buf); + switch (*s) { + case '.': + if (label_len > GLDNS_MAX_LABELLEN) { + return RET_ERR(GLDNS_WIREPARSE_ERR_LABEL_OVERFLOW, q-buf); + } + if (label_len == 0) { + return RET_ERR(GLDNS_WIREPARSE_ERR_EMPTY_LABEL, q-buf); + } + len += label_len + 1; + *q = 0; + *pq = label_len; + label_len = 0; + pq = q; + break; + case '\\': + /* octet value or literal char */ + s += 1; + if (!gldns_parse_escape(q, &s)) { + *q = 0; + return RET_ERR(GLDNS_WIREPARSE_ERR_SYNTAX_BAD_ESCAPE, q-buf); + } + s -= 1; + label_len++; + break; + default: + *q = (uint8_t)*s; + label_len++; + } + } + + /* add root label if last char was not '.' */ + if(label_len != 0) { + if(rel) *rel = 1; + if (q >= buf + *olen) + return RET_ERR(GLDNS_WIREPARSE_ERR_BUFFER_TOO_SMALL, q-buf); + if (q > buf + GLDNS_MAX_DOMAINLEN) { + return RET_ERR(GLDNS_WIREPARSE_ERR_DOMAINNAME_OVERFLOW, q-buf); + } + if (label_len > GLDNS_MAX_LABELLEN) { + return RET_ERR(GLDNS_WIREPARSE_ERR_LABEL_OVERFLOW, q-buf); + } + if (label_len == 0) { /* label_len 0 but not . at end? */ + return RET_ERR(GLDNS_WIREPARSE_ERR_EMPTY_LABEL, q-buf); + } + len += label_len + 1; + *pq = label_len; + *q = 0; + } + len++; + *olen = len; + + return GLDNS_WIREPARSE_ERR_OK; +} + +int gldns_str2wire_dname_buf(const char* str, uint8_t* buf, size_t* len) +{ + return gldns_str2wire_dname_buf_rel(str, buf, len, NULL); +} + +int gldns_str2wire_dname_buf_origin(const char* str, uint8_t* buf, size_t* len, + uint8_t* origin, size_t origin_len) +{ + size_t dlen = *len; + int rel = 0; + int s = gldns_str2wire_dname_buf_rel(str, buf, &dlen, &rel); + if(s) return s; + + if(rel && origin && dlen > 0) { + if(dlen + origin_len - 1 > GLDNS_MAX_DOMAINLEN) + return RET_ERR(GLDNS_WIREPARSE_ERR_DOMAINNAME_OVERFLOW, + GLDNS_MAX_DOMAINLEN); + if(dlen + origin_len - 1 > *len) + return RET_ERR(GLDNS_WIREPARSE_ERR_BUFFER_TOO_SMALL, + *len); + memmove(buf+dlen-1, origin, origin_len); + *len = dlen + origin_len - 1; + } else + *len = dlen; + return GLDNS_WIREPARSE_ERR_OK; +} + +uint8_t* gldns_str2wire_dname(const char* str, size_t* len) +{ + uint8_t dname[GLDNS_MAX_DOMAINLEN+1]; + *len = sizeof(dname); + if(gldns_str2wire_dname_buf(str, dname, len) == 0) { + uint8_t* r = (uint8_t*)malloc(*len); + if(r) return memcpy(r, dname, *len); + } + *len = 0; + return NULL; +} + +/** read owner name */ +static int +rrinternal_get_owner(gldns_buffer* strbuf, uint8_t* rr, size_t* len, + size_t* dname_len, uint8_t* origin, size_t origin_len, uint8_t* prev, + size_t prev_len, char* token, size_t token_len) +{ + /* split the rr in its parts -1 signals trouble */ + if(gldns_bget_token(strbuf, token, "\t\n ", token_len) == -1) { + return RET_ERR(GLDNS_WIREPARSE_ERR_SYNTAX, + gldns_buffer_position(strbuf)); + } + + if(strcmp(token, "@") == 0) { + uint8_t* tocopy; + if (origin) { + *dname_len = origin_len; + tocopy = origin; + } else if (prev) { + *dname_len = prev_len; + tocopy = prev; + } else { + /* default to root */ + *dname_len = 1; + tocopy = (uint8_t*)"\0"; + } + if(*len < *dname_len) + return RET_ERR(GLDNS_WIREPARSE_ERR_BUFFER_TOO_SMALL, + gldns_buffer_position(strbuf)); + memmove(rr, tocopy, *dname_len); + } else if(strlen(token) == 0) { + /* no ownername was given, try prev, if that fails + * origin, else default to root */ + uint8_t* tocopy; + if(prev) { + *dname_len = prev_len; + tocopy = prev; + } else if(origin) { + *dname_len = origin_len; + tocopy = origin; + } else { + *dname_len = 1; + tocopy = (uint8_t*)"\0"; + } + if(*len < *dname_len) + return RET_ERR(GLDNS_WIREPARSE_ERR_BUFFER_TOO_SMALL, + gldns_buffer_position(strbuf)); + memmove(rr, tocopy, *dname_len); + } else { + size_t dlen = *len; + int s = gldns_str2wire_dname_buf_origin(token, rr, &dlen, + origin, origin_len); + if(s) return RET_ERR_SHIFT(s, + gldns_buffer_position(strbuf)-strlen(token)); + *dname_len = dlen; + } + return GLDNS_WIREPARSE_ERR_OK; +} + +/** read ttl */ +static int +rrinternal_get_ttl(gldns_buffer* strbuf, char* token, size_t token_len, + int* not_there, uint32_t* ttl, uint32_t default_ttl) +{ + const char* endptr; + if(gldns_bget_token(strbuf, token, "\t\n ", token_len) == -1) { + return RET_ERR(GLDNS_WIREPARSE_ERR_SYNTAX_TTL, + gldns_buffer_position(strbuf)); + } + *ttl = (uint32_t) gldns_str2period(token, &endptr); + + if (strlen(token) > 0 && !isdigit((int)token[0])) { + *not_there = 1; + /* ah, it's not there or something */ + if (default_ttl == 0) { + *ttl = GLDNS_DEFAULT_TTL; + } else { + *ttl = default_ttl; + } + } + return GLDNS_WIREPARSE_ERR_OK; +} + +/** read class */ +static int +rrinternal_get_class(gldns_buffer* strbuf, char* token, size_t token_len, + int* not_there, uint16_t* cl) +{ + /* if 'not_there' then we got token from previous parse routine */ + if(!*not_there) { + /* parse new token for class */ + if(gldns_bget_token(strbuf, token, "\t\n ", token_len) == -1) { + return RET_ERR(GLDNS_WIREPARSE_ERR_SYNTAX_CLASS, + gldns_buffer_position(strbuf)); + } + } else *not_there = 0; + *cl = gldns_get_rr_class_by_name(token); + /* class can be left out too, assume IN, current token must be type */ + if(*cl == 0 && strcmp(token, "CLASS0") != 0) { + *not_there = 1; + *cl = GLDNS_RR_CLASS_IN; + } + return GLDNS_WIREPARSE_ERR_OK; +} + +/** read type */ +static int +rrinternal_get_type(gldns_buffer* strbuf, char* token, size_t token_len, + int* not_there, uint16_t* tp) +{ + /* if 'not_there' then we got token from previous parse routine */ + if(!*not_there) { + /* parse new token for type */ + if(gldns_bget_token(strbuf, token, "\t\n ", token_len) == -1) { + return RET_ERR(GLDNS_WIREPARSE_ERR_SYNTAX_TYPE, + gldns_buffer_position(strbuf)); + } + } + *tp = gldns_get_rr_type_by_name(token); + if(*tp == 0 && strcmp(token, "TYPE0") != 0) { + return RET_ERR(GLDNS_WIREPARSE_ERR_SYNTAX_TYPE, + gldns_buffer_position(strbuf)); + } + return GLDNS_WIREPARSE_ERR_OK; +} + +/** put type, class, ttl into rr buffer */ +static int +rrinternal_write_typeclassttl(gldns_buffer* strbuf, uint8_t* rr, size_t len, + size_t dname_len, uint16_t tp, uint16_t cl, uint32_t ttl, int question) +{ + if(question) { + /* question is : name, type, class */ + if(dname_len + 4 > len) + return RET_ERR(GLDNS_WIREPARSE_ERR_BUFFER_TOO_SMALL, + gldns_buffer_position(strbuf)); + gldns_write_uint16(rr+dname_len, tp); + gldns_write_uint16(rr+dname_len+2, cl); + return GLDNS_WIREPARSE_ERR_OK; + } + + /* type(2), class(2), ttl(4), rdatalen(2 (later)) = 10 */ + if(dname_len + 10 > len) + return RET_ERR(GLDNS_WIREPARSE_ERR_BUFFER_TOO_SMALL, + gldns_buffer_position(strbuf)); + gldns_write_uint16(rr+dname_len, tp); + gldns_write_uint16(rr+dname_len+2, cl); + gldns_write_uint32(rr+dname_len+4, ttl); + gldns_write_uint16(rr+dname_len+8, 0); /* rdatalen placeholder */ + return GLDNS_WIREPARSE_ERR_OK; +} + +/** find delimiters for type */ +static const char* +rrinternal_get_delims(gldns_rdf_type rdftype, uint16_t r_cnt, uint16_t r_max) +{ + switch(rdftype) { + case GLDNS_RDF_TYPE_B64 : + case GLDNS_RDF_TYPE_HEX : /* These rdf types may con- */ + case GLDNS_RDF_TYPE_LOC : /* tain whitespace, only if */ + case GLDNS_RDF_TYPE_WKS : /* it is the last rd field. */ + case GLDNS_RDF_TYPE_IPSECKEY : + case GLDNS_RDF_TYPE_NSEC : if (r_cnt == r_max - 1) { + return "\n\t"; + } + break; + default : break; + } + return "\n\t "; +} + +/* Syntactic sugar for gldns_rr_new_frm_str_internal */ +static int +gldns_rdf_type_maybe_quoted(gldns_rdf_type rdf_type) +{ + return rdf_type == GLDNS_RDF_TYPE_STR || + rdf_type == GLDNS_RDF_TYPE_LONG_STR; +} + +/** see if rdata is quoted */ +static int +rrinternal_get_quoted(gldns_buffer* strbuf, const char** delimiters, + gldns_rdf_type rdftype) +{ + if(gldns_rdf_type_maybe_quoted(rdftype) && + gldns_buffer_remaining(strbuf) > 0) { + + /* skip spaces */ + while(gldns_buffer_remaining(strbuf) > 0 && + *(gldns_buffer_current(strbuf)) == ' ') { + gldns_buffer_skip(strbuf, 1); + } + + if(gldns_buffer_remaining(strbuf) > 0 && + *(gldns_buffer_current(strbuf)) == '\"') { + *delimiters = "\"\0"; + gldns_buffer_skip(strbuf, 1); + return 1; + } + } + return 0; +} + +/** spool hex data into rdata */ +static int +rrinternal_spool_hex(char* token, uint8_t* rr, size_t rr_len, + size_t rr_cur_len, size_t* cur_hex_data_size, size_t hex_data_size) +{ + char* p = token; + while(*p) { + if(isspace(*p)) { + p++; + continue; + } + if(!isxdigit(*p)) + return RET_ERR(GLDNS_WIREPARSE_ERR_SYNTAX_RDATA, + p-token); + if(*cur_hex_data_size >= hex_data_size) + return RET_ERR(GLDNS_WIREPARSE_ERR_SYNTAX_RDATA, + p-token); + /* extra robust check */ + if(rr_cur_len+(*cur_hex_data_size)/2 >= rr_len) + return RET_ERR(GLDNS_WIREPARSE_ERR_BUFFER_TOO_SMALL, + p-token); + /* see if 16s or 1s */ + if( ((*cur_hex_data_size)&1) == 0) { + rr[rr_cur_len+(*cur_hex_data_size)/2] = + (uint8_t)gldns_hexdigit_to_int(*p)*16; + } else { + rr[rr_cur_len+(*cur_hex_data_size)/2] += + (uint8_t)gldns_hexdigit_to_int(*p); + } + p++; + (*cur_hex_data_size)++; + } + return GLDNS_WIREPARSE_ERR_OK; +} + +/** read unknown rr type format */ +static int +rrinternal_parse_unknown(gldns_buffer* strbuf, char* token, size_t token_len, + uint8_t* rr, size_t* rr_len, size_t* rr_cur_len, size_t pre_data_pos) +{ + const char* delim = "\n\t "; + size_t hex_data_size, cur_hex_data_size; + /* go back to before \# + * and skip it while setting delimiters better + */ + gldns_buffer_set_position(strbuf, pre_data_pos); + if(gldns_bget_token(strbuf, token, delim, token_len) == -1) + return GLDNS_WIREPARSE_ERR_GENERAL; /* should not fail */ + /* read rdata octet length */ + if(gldns_bget_token(strbuf, token, delim, token_len) == -1) { + /* something goes very wrong here */ + return RET_ERR(GLDNS_WIREPARSE_ERR_SYNTAX_RDATA, + gldns_buffer_position(strbuf)); + } + hex_data_size = (size_t)atoi(token); + if(hex_data_size > GLDNS_MAX_RDFLEN || + *rr_cur_len + hex_data_size > *rr_len) { + return RET_ERR(GLDNS_WIREPARSE_ERR_BUFFER_TOO_SMALL, + gldns_buffer_position(strbuf)); + } + /* copy hex chars into hex str (2 chars per byte) */ + hex_data_size *= 2; + cur_hex_data_size = 0; + while(cur_hex_data_size < hex_data_size) { + int status; + ssize_t c = gldns_bget_token(strbuf, token, delim, token_len); + if((status = rrinternal_spool_hex(token, rr, *rr_len, + *rr_cur_len, &cur_hex_data_size, hex_data_size)) != 0) + return RET_ERR_SHIFT(status, + gldns_buffer_position(strbuf)-strlen(token)); + if(c == -1) { + if(cur_hex_data_size != hex_data_size) + return RET_ERR( + GLDNS_WIREPARSE_ERR_SYNTAX_RDATA, + gldns_buffer_position(strbuf)); + break; + } + } + *rr_cur_len += hex_data_size/2; + return GLDNS_WIREPARSE_ERR_OK; +} + +/** parse normal RR rdata element */ +static int +rrinternal_parse_rdf(gldns_buffer* strbuf, char* token, size_t token_len, + uint8_t* rr, size_t rr_len, size_t* rr_cur_len, gldns_rdf_type rdftype, + uint16_t rr_type, uint16_t r_cnt, uint16_t r_max, size_t dname_len, + uint8_t* origin, size_t origin_len) +{ + size_t len; + int status; + + switch(rdftype) { + case GLDNS_RDF_TYPE_DNAME: + /* check if the origin should be used or concatenated */ + if(strcmp(token, "@") == 0) { + uint8_t* tocopy; + size_t copylen; + if(origin) { + copylen = origin_len; + tocopy = origin; + } else if(rr_type == GLDNS_RR_TYPE_SOA) { + copylen = dname_len; + tocopy = rr; /* copy rr owner name */ + } else { + copylen = 1; + tocopy = (uint8_t*)"\0"; + } + if((*rr_cur_len) + copylen > rr_len) + return RET_ERR( + GLDNS_WIREPARSE_ERR_BUFFER_TOO_SMALL, + gldns_buffer_position(strbuf)); + memmove(rr+*rr_cur_len, tocopy, copylen); + (*rr_cur_len) += copylen; + } else { + size_t dlen = rr_len - (*rr_cur_len); + int s = gldns_str2wire_dname_buf_origin(token, + rr+*rr_cur_len, &dlen, origin, origin_len); + if(s) return RET_ERR_SHIFT(s, + gldns_buffer_position(strbuf)-strlen(token)); + (*rr_cur_len) += dlen; + } + return GLDNS_WIREPARSE_ERR_OK; + + case GLDNS_RDF_TYPE_HEX: + case GLDNS_RDF_TYPE_B64: + /* When this is the last rdata field, then the + * rest should be read in (cause then these + * rdf types may contain spaces). */ + if(r_cnt == r_max - 1) { + size_t tlen = strlen(token); + (void)gldns_bget_token(strbuf, token+tlen, "\n", + token_len - tlen); + } + break; + default: + break; + } + + len = rr_len - (*rr_cur_len); + if((status=gldns_str2wire_rdf_buf(token, rr+(*rr_cur_len), &len, + rdftype)) != 0) + return RET_ERR_SHIFT(status, + gldns_buffer_position(strbuf)-strlen(token)); + *rr_cur_len += len; + return GLDNS_WIREPARSE_ERR_OK; +} + +/** + * Parse one rdf token. Takes care of quotes and parenthesis. + */ +static int +gldns_parse_rdf_token(gldns_buffer* strbuf, char* token, size_t token_len, + int* quoted, int* parens, size_t* pre_data_pos, + const char* delimiters, gldns_rdf_type rdftype, size_t* token_strlen) +{ + size_t slen; + + /* skip spaces */ + while(gldns_buffer_remaining(strbuf) > 0 && !*quoted && + *(gldns_buffer_current(strbuf)) == ' ') { + gldns_buffer_skip(strbuf, 1); + } + + *pre_data_pos = gldns_buffer_position(strbuf); + if(gldns_bget_token_par(strbuf, token, (*quoted)?"\"":delimiters, + token_len, parens, (*quoted)?NULL:" \t") == -1) { + return 0; + } + slen = strlen(token); + /* check if not quoted yet, and we have encountered quotes */ + if(!*quoted && gldns_rdf_type_maybe_quoted(rdftype) && + slen >= 2 && + (token[0] == '"' || token[0] == '\'') && + (token[slen-1] == '"' || token[slen-1] == '\'')) { + /* move token two smaller (quotes) with endnull */ + memmove(token, token+1, slen-2); + token[slen-2] = 0; + slen -= 2; + *quoted = 1; + } else if(!*quoted && gldns_rdf_type_maybe_quoted(rdftype) && + slen >= 2 && + (token[0] == '"' || token[0] == '\'')) { + /* got the start quote (remove it) but read remainder + * of quoted string as well into remainder of token */ + memmove(token, token+1, slen-1); + token[slen-1] = 0; + slen -= 1; + *quoted = 1; + /* rewind buffer over skipped whitespace */ + while(gldns_buffer_position(strbuf) > 0 && + (gldns_buffer_current(strbuf)[-1] == ' ' || + gldns_buffer_current(strbuf)[-1] == '\t')) { + gldns_buffer_skip(strbuf, -1); + } + if(gldns_bget_token_par(strbuf, token+slen, + "\"", token_len-slen, + parens, NULL) == -1) { + return 0; + } + slen = strlen(token); + } + *token_strlen = slen; + return 1; +} + +/** Add space and one more rdf token onto the existing token string. */ +static int +gldns_affix_token(gldns_buffer* strbuf, char* token, size_t* token_len, + int* quoted, int* parens, size_t* pre_data_pos, + const char* delimiters, gldns_rdf_type rdftype, size_t* token_strlen) +{ + size_t addlen = *token_len - *token_strlen; + size_t addstrlen = 0; + + /* add space */ + if(addlen < 1) return 0; + token[*token_strlen] = ' '; + token[++(*token_strlen)] = 0; + + /* read another token */ + addlen = *token_len - *token_strlen; + if(!gldns_parse_rdf_token(strbuf, token+*token_strlen, addlen, quoted, + parens, pre_data_pos, delimiters, rdftype, &addstrlen)) + return 0; + (*token_strlen) += addstrlen; + return 1; +} + +/** parse rdata from string into rr buffer(-remainder after dname). */ +static int +rrinternal_parse_rdata(gldns_buffer* strbuf, char* token, size_t token_len, + uint8_t* rr, size_t* rr_len, size_t dname_len, uint16_t rr_type, + uint8_t* origin, size_t origin_len) +{ + const gldns_rr_descriptor *desc = gldns_rr_descript((uint16_t)rr_type); + uint16_t r_cnt, r_min, r_max; + size_t rr_cur_len = dname_len + 10, pre_data_pos, token_strlen; + int was_unknown_rr_format = 0, parens = 0, status, quoted; + const char* delimiters; + gldns_rdf_type rdftype; + /* a desc is always returned */ + if(!desc) return GLDNS_WIREPARSE_ERR_GENERAL; + r_max = gldns_rr_descriptor_maximum(desc); + r_min = gldns_rr_descriptor_minimum(desc); + /* robust check */ + if(rr_cur_len > *rr_len) + return RET_ERR(GLDNS_WIREPARSE_ERR_BUFFER_TOO_SMALL, + gldns_buffer_position(strbuf)); + + /* because number of fields can be variable, we can't rely on + * _maximum() only */ + for(r_cnt=0; r_cnt < r_max; r_cnt++) { + rdftype = gldns_rr_descriptor_field_type(desc, r_cnt); + delimiters = rrinternal_get_delims(rdftype, r_cnt, r_max); + quoted = rrinternal_get_quoted(strbuf, &delimiters, rdftype); + + if(!gldns_parse_rdf_token(strbuf, token, token_len, "ed, + &parens, &pre_data_pos, delimiters, rdftype, + &token_strlen)) + break; + + /* rfc3597 specifies that any type can be represented + * with \# method, which can contain spaces... + * it does specify size though... */ + + /* unknown RR data */ + if(token_strlen>=2 && strncmp(token, "\\#", 2) == 0 && + !quoted && (token_strlen == 2 || token[2]==' ')) { + was_unknown_rr_format = 1; + if((status=rrinternal_parse_unknown(strbuf, token, + token_len, rr, rr_len, &rr_cur_len, + pre_data_pos)) != 0) + return status; + } else if(token_strlen > 0 || quoted) { + if(rdftype == GLDNS_RDF_TYPE_HIP) { + /* affix the HIT and PK fields, with a space */ + if(!gldns_affix_token(strbuf, token, + &token_len, "ed, &parens, + &pre_data_pos, delimiters, + rdftype, &token_strlen)) + break; + if(!gldns_affix_token(strbuf, token, + &token_len, "ed, &parens, + &pre_data_pos, delimiters, + rdftype, &token_strlen)) + break; + } + + /* normal RR */ + if((status=rrinternal_parse_rdf(strbuf, token, + token_len, rr, *rr_len, &rr_cur_len, rdftype, + rr_type, r_cnt, r_max, dname_len, origin, + origin_len)) != 0) { + return status; + } + } + } + if(!was_unknown_rr_format && r_cnt+1 < r_min) { + return RET_ERR(GLDNS_WIREPARSE_ERR_SYNTAX_MISSING_VALUE, + gldns_buffer_position(strbuf)); + } + while(parens != 0) { + /* read remainder, must be "" */ + if(gldns_bget_token_par(strbuf, token, "\n", token_len, + &parens, " \t") == -1) { + if(parens != 0) + return RET_ERR(GLDNS_WIREPARSE_ERR_PARENTHESIS, + gldns_buffer_position(strbuf)); + break; + } + if(strcmp(token, "") != 0) + return RET_ERR(GLDNS_WIREPARSE_ERR_PARENTHESIS, + gldns_buffer_position(strbuf)); + } + /* write rdata length */ + gldns_write_uint16(rr+dname_len+8, rr_cur_len-dname_len-10); + *rr_len = rr_cur_len; + return GLDNS_WIREPARSE_ERR_OK; +} + +/* + * trailing spaces are allowed + * leading spaces are not allowed + * allow ttl to be optional + * class is optional too + * if ttl is missing, and default_ttl is 0, use DEF_TTL + * allow ttl to be written as 1d3h + * So the RR should look like. e.g. + * miek.nl. 3600 IN MX 10 elektron.atoom.net + * or + * miek.nl. 1h IN MX 10 elektron.atoom.net + * or + * miek.nl. IN MX 10 elektron.atoom.net + */ +static int +gldns_str2wire_rr_buf_internal(const char* str, uint8_t* rr, size_t* len, + size_t* dname_len, uint32_t default_ttl, uint8_t* origin, + size_t origin_len, uint8_t* prev, size_t prev_len, int question) +{ + int status; + int not_there = 0; + char token[GLDNS_MAX_RDFLEN+1]; + uint32_t ttl = 0; + uint16_t tp = 0, cl = 0; + size_t ddlen = 0; + + /* string in buffer */ + gldns_buffer strbuf; + gldns_buffer_init_frm_data(&strbuf, (uint8_t*)str, strlen(str)); + if(!dname_len) dname_len = &ddlen; + + /* parse the owner */ + if((status=rrinternal_get_owner(&strbuf, rr, len, dname_len, origin, + origin_len, prev, prev_len, token, sizeof(token))) != 0) + return status; + + /* parse the [ttl] [class] */ + if((status=rrinternal_get_ttl(&strbuf, token, sizeof(token), + ¬_there, &ttl, default_ttl)) != 0) + return status; + if((status=rrinternal_get_class(&strbuf, token, sizeof(token), + ¬_there, &cl)) != 0) + return status; + if((status=rrinternal_get_type(&strbuf, token, sizeof(token), + ¬_there, &tp)) != 0) + return status; + /* put ttl, class, type into the rr result */ + if((status=rrinternal_write_typeclassttl(&strbuf, rr, *len, *dname_len, tp, cl, + ttl, question)) != 0) + return status; + /* for a question-RR we are done, no rdata */ + if(question) { + *len = *dname_len + 4; + return GLDNS_WIREPARSE_ERR_OK; + } + + /* rdata */ + if((status=rrinternal_parse_rdata(&strbuf, token, sizeof(token), + rr, len, *dname_len, tp, origin, origin_len)) != 0) + return status; + + return GLDNS_WIREPARSE_ERR_OK; +} + +int gldns_str2wire_rr_buf(const char* str, uint8_t* rr, size_t* len, + size_t* dname_len, uint32_t default_ttl, uint8_t* origin, + size_t origin_len, uint8_t* prev, size_t prev_len) +{ + return gldns_str2wire_rr_buf_internal(str, rr, len, dname_len, + default_ttl, origin, origin_len, prev, prev_len, 0); +} + +int gldns_str2wire_rr_question_buf(const char* str, uint8_t* rr, size_t* len, + size_t* dname_len, uint8_t* origin, size_t origin_len, uint8_t* prev, + size_t prev_len) +{ + return gldns_str2wire_rr_buf_internal(str, rr, len, dname_len, + 0, origin, origin_len, prev, prev_len, 1); +} + +uint16_t gldns_wirerr_get_type(uint8_t* rr, size_t len, size_t dname_len) +{ + if(len < dname_len+2) + return 0; + return gldns_read_uint16(rr+dname_len); +} + +uint16_t gldns_wirerr_get_class(uint8_t* rr, size_t len, size_t dname_len) +{ + if(len < dname_len+4) + return 0; + return gldns_read_uint16(rr+dname_len+2); +} + +uint32_t gldns_wirerr_get_ttl(uint8_t* rr, size_t len, size_t dname_len) +{ + if(len < dname_len+8) + return 0; + return gldns_read_uint32(rr+dname_len+4); +} + +uint16_t gldns_wirerr_get_rdatalen(uint8_t* rr, size_t len, size_t dname_len) +{ + if(len < dname_len+10) + return 0; + return gldns_read_uint16(rr+dname_len+8); +} + +uint8_t* gldns_wirerr_get_rdata(uint8_t* rr, size_t len, size_t dname_len) +{ + if(len < dname_len+10) + return NULL; + return rr+dname_len+10; +} + +uint8_t* gldns_wirerr_get_rdatawl(uint8_t* rr, size_t len, size_t dname_len) +{ + if(len < dname_len+10) + return NULL; + return rr+dname_len+8; +} + +const char* gldns_get_errorstr_parse(int e) +{ + gldns_lookup_table *lt; + lt = gldns_lookup_by_id(gldns_wireparse_errors, GLDNS_WIREPARSE_ERROR(e)); + return lt?lt->name:"unknown error"; +} + +int gldns_fp2wire_rr_buf(FILE* in, uint8_t* rr, size_t* len, size_t* dname_len, + struct gldns_file_parse_state* parse_state) +{ + char line[GLDNS_RR_BUF_SIZE+1]; + ssize_t size; + + /* read an entire line in from the file */ + if((size = gldns_fget_token_l(in, line, GLDNS_PARSE_SKIP_SPACE, + GLDNS_RR_BUF_SIZE, parse_state?&parse_state->lineno:NULL)) + == -1) { + /* if last line was empty, we are now at feof, which is not + * always a parse error (happens when for instance last line + * was a comment) + */ + return GLDNS_WIREPARSE_ERR_SYNTAX; + } + + /* we can have the situation, where we've read ok, but still got + * no bytes to play with, in this case size is 0 */ + if(size == 0) { + *len = 0; + *dname_len = 0; + return GLDNS_WIREPARSE_ERR_OK; + } + + if(strncmp(line, "$ORIGIN", 7) == 0 && isspace(line[7])) { + size_t off = 8; + int s; + *len = 0; + *dname_len = 0; + if(!parse_state) return GLDNS_WIREPARSE_ERR_OK; + while(isspace(line[off])) + off++; + parse_state->origin_len = sizeof(parse_state->origin); + s = gldns_str2wire_dname_buf(line+off, parse_state->origin, + &parse_state->origin_len); + if(s) parse_state->origin_len = 0; + return s; + } else if(strncmp(line, "$TTL", 4) == 0 && isspace(line[4])) { + const char* end = NULL; + size_t off = 8; + *len = 0; + *dname_len = 0; + if(!parse_state) return GLDNS_WIREPARSE_ERR_OK; + while(isspace(line[off])) + off++; + parse_state->default_ttl = gldns_str2period(line+off, &end); + } else if (strncmp(line, "$INCLUDE", 8) == 0) { + *len = 0; + *dname_len = 0; + return GLDNS_WIREPARSE_ERR_INCLUDE; + } else { + return gldns_str2wire_rr_buf(line, rr, len, dname_len, + parse_state?parse_state->default_ttl:0, + (parse_state&&parse_state->origin_len)? + parse_state->origin:NULL, + parse_state->origin_len, + (parse_state&&parse_state->prev_rr_len)? + parse_state->prev_rr:NULL, + parse_state->prev_rr_len); + } + return GLDNS_WIREPARSE_ERR_OK; +} + +int gldns_str2wire_rdf_buf(const char* str, uint8_t* rd, size_t* len, + gldns_rdf_type rdftype) +{ + switch (rdftype) { + case GLDNS_RDF_TYPE_DNAME: + return gldns_str2wire_dname_buf(str, rd, len); + case GLDNS_RDF_TYPE_INT8: + return gldns_str2wire_int8_buf(str, rd, len); + case GLDNS_RDF_TYPE_INT16: + return gldns_str2wire_int16_buf(str, rd, len); + case GLDNS_RDF_TYPE_INT32: + return gldns_str2wire_int32_buf(str, rd, len); + case GLDNS_RDF_TYPE_A: + return gldns_str2wire_a_buf(str, rd, len); + case GLDNS_RDF_TYPE_AAAA: + return gldns_str2wire_aaaa_buf(str, rd, len); + case GLDNS_RDF_TYPE_STR: + return gldns_str2wire_str_buf(str, rd, len); + case GLDNS_RDF_TYPE_APL: + return gldns_str2wire_apl_buf(str, rd, len); + case GLDNS_RDF_TYPE_B64: + return gldns_str2wire_b64_buf(str, rd, len); + case GLDNS_RDF_TYPE_B32_EXT: + return gldns_str2wire_b32_ext_buf(str, rd, len); + case GLDNS_RDF_TYPE_HEX: + return gldns_str2wire_hex_buf(str, rd, len); + case GLDNS_RDF_TYPE_NSEC: + return gldns_str2wire_nsec_buf(str, rd, len); + case GLDNS_RDF_TYPE_TYPE: + return gldns_str2wire_type_buf(str, rd, len); + case GLDNS_RDF_TYPE_CLASS: + return gldns_str2wire_class_buf(str, rd, len); + case GLDNS_RDF_TYPE_CERT_ALG: + return gldns_str2wire_cert_alg_buf(str, rd, len); + case GLDNS_RDF_TYPE_ALG: + return gldns_str2wire_alg_buf(str, rd, len); + case GLDNS_RDF_TYPE_TIME: + return gldns_str2wire_time_buf(str, rd, len); + case GLDNS_RDF_TYPE_PERIOD: + return gldns_str2wire_period_buf(str, rd, len); + case GLDNS_RDF_TYPE_LOC: + return gldns_str2wire_loc_buf(str, rd, len); + case GLDNS_RDF_TYPE_WKS: + return gldns_str2wire_wks_buf(str, rd, len); + case GLDNS_RDF_TYPE_NSAP: + return gldns_str2wire_nsap_buf(str, rd, len); + case GLDNS_RDF_TYPE_ATMA: + return gldns_str2wire_atma_buf(str, rd, len); + case GLDNS_RDF_TYPE_IPSECKEY: + return gldns_str2wire_ipseckey_buf(str, rd, len); + case GLDNS_RDF_TYPE_NSEC3_SALT: + return gldns_str2wire_nsec3_salt_buf(str, rd, len); + case GLDNS_RDF_TYPE_NSEC3_NEXT_OWNER: + return gldns_str2wire_b32_ext_buf(str, rd, len); + case GLDNS_RDF_TYPE_ILNP64: + return gldns_str2wire_ilnp64_buf(str, rd, len); + case GLDNS_RDF_TYPE_EUI48: + return gldns_str2wire_eui48_buf(str, rd, len); + case GLDNS_RDF_TYPE_EUI64: + return gldns_str2wire_eui64_buf(str, rd, len); + case GLDNS_RDF_TYPE_TAG: + return gldns_str2wire_tag_buf(str, rd, len); + case GLDNS_RDF_TYPE_LONG_STR: + return gldns_str2wire_long_str_buf(str, rd, len); + case GLDNS_RDF_TYPE_HIP: + return gldns_str2wire_hip_buf(str, rd, len); + case GLDNS_RDF_TYPE_INT16_DATA: + return gldns_str2wire_int16_data_buf(str, rd, len); + case GLDNS_RDF_TYPE_UNKNOWN: + case GLDNS_RDF_TYPE_SERVICE: + return GLDNS_WIREPARSE_ERR_NOT_IMPL; + case GLDNS_RDF_TYPE_NONE: + default: + break; + } + return GLDNS_WIREPARSE_ERR_GENERAL; +} + +int gldns_str2wire_int8_buf(const char* str, uint8_t* rd, size_t* len) +{ + char* end; + uint8_t r = (uint8_t)strtol((char*)str, &end, 10); + if(*end != 0) + return RET_ERR(GLDNS_WIREPARSE_ERR_SYNTAX_INT, end-(char*)str); + if(*len < 1) + return GLDNS_WIREPARSE_ERR_BUFFER_TOO_SMALL; + rd[0] = r; + *len = 1; + return GLDNS_WIREPARSE_ERR_OK; +} + +int gldns_str2wire_int16_buf(const char* str, uint8_t* rd, size_t* len) +{ + char* end; + uint16_t r = (uint16_t)strtol((char*)str, &end, 10); + if(*end != 0) + return RET_ERR(GLDNS_WIREPARSE_ERR_SYNTAX_INT, end-(char*)str); + if(*len < 2) + return GLDNS_WIREPARSE_ERR_BUFFER_TOO_SMALL; + gldns_write_uint16(rd, r); + *len = 2; + return GLDNS_WIREPARSE_ERR_OK; +} + +int gldns_str2wire_int32_buf(const char* str, uint8_t* rd, size_t* len) +{ + char* end; + uint32_t r; + errno = 0; /* must set to zero before call, + note race condition on errno */ + if(*str == '-') + r = (uint32_t)strtol((char*)str, &end, 10); + else r = (uint32_t)strtoul((char*)str, &end, 10); + if(*end != 0) + return RET_ERR(GLDNS_WIREPARSE_ERR_SYNTAX_INT, end-(char*)str); + if(errno == ERANGE) + return GLDNS_WIREPARSE_ERR_SYNTAX_INTEGER_OVERFLOW; + if(*len < 4) + return GLDNS_WIREPARSE_ERR_BUFFER_TOO_SMALL; + gldns_write_uint32(rd, r); + *len = 4; + return GLDNS_WIREPARSE_ERR_OK; +} + +int gldns_str2wire_a_buf(const char* str, uint8_t* rd, size_t* len) +{ + struct in_addr address; + if(inet_pton(AF_INET, (char*)str, &address) != 1) + return GLDNS_WIREPARSE_ERR_SYNTAX_IP4; + if(*len < sizeof(address)) + return GLDNS_WIREPARSE_ERR_BUFFER_TOO_SMALL; + memmove(rd, &address, sizeof(address)); + *len = sizeof(address); + return GLDNS_WIREPARSE_ERR_OK; +} + +int gldns_str2wire_aaaa_buf(const char* str, uint8_t* rd, size_t* len) +{ +#ifdef AF_INET6 + uint8_t address[GLDNS_IP6ADDRLEN + 1]; + if(inet_pton(AF_INET6, (char*)str, address) != 1) + return GLDNS_WIREPARSE_ERR_SYNTAX_IP6; + if(*len < GLDNS_IP6ADDRLEN) + return GLDNS_WIREPARSE_ERR_BUFFER_TOO_SMALL; + memmove(rd, address, GLDNS_IP6ADDRLEN); + *len = GLDNS_IP6ADDRLEN; + return GLDNS_WIREPARSE_ERR_OK; +#else + return GLDNS_WIREPARSE_ERR_NOT_IMPL; +#endif +} + +int gldns_str2wire_str_buf(const char* str, uint8_t* rd, size_t* len) +{ + uint8_t ch = 0; + size_t sl = 0; + const char* s = str; + /* skip length byte */ + if(*len < 1) + return GLDNS_WIREPARSE_ERR_BUFFER_TOO_SMALL; + + /* read characters */ + while(gldns_parse_char(&ch, &s)) { + if(sl >= 255) + return RET_ERR(GLDNS_WIREPARSE_ERR_INVALID_STR, s-str); + if(*len < sl+1) + return RET_ERR(GLDNS_WIREPARSE_ERR_BUFFER_TOO_SMALL, + s-str); + rd[++sl] = ch; + } + if(!s) + return GLDNS_WIREPARSE_ERR_SYNTAX_BAD_ESCAPE; + rd[0] = (uint8_t)sl; + *len = sl+1; + return GLDNS_WIREPARSE_ERR_OK; +} + +int gldns_str2wire_apl_buf(const char* str, uint8_t* rd, size_t* len) +{ + const char *my_str = str; + + char my_ip_str[64]; + size_t ip_str_len; + + uint16_t family; + int negation; + size_t adflength = 0; + uint8_t data[16+4]; + uint8_t prefix; + size_t i; + + if(strlen(my_str) == 0) { + /* empty APL element, no data, no string */ + *len = 0; + return GLDNS_WIREPARSE_ERR_OK; + } + + /* [!]afi:address/prefix */ + if (strlen(my_str) < 2 + || strchr(my_str, ':') == NULL + || strchr(my_str, '/') == NULL + || strchr(my_str, ':') > strchr(my_str, '/')) { + return GLDNS_WIREPARSE_ERR_INVALID_STR; + } + + if (my_str[0] == '!') { + negation = 1; + my_str += 1; + } else { + negation = 0; + } + + family = (uint16_t) atoi(my_str); + + my_str = strchr(my_str, ':') + 1; + + /* need ip addr and only ip addr for inet_pton */ + ip_str_len = (size_t) (strchr(my_str, '/') - my_str); + if(ip_str_len+1 > sizeof(my_ip_str)) + return GLDNS_WIREPARSE_ERR_INVALID_STR; + (void)strlcpy(my_ip_str, my_str, sizeof(my_ip_str)); + my_ip_str[ip_str_len] = 0; + + if (family == 1) { + /* ipv4 */ + if(inet_pton(AF_INET, my_ip_str, data+4) == 0) + return GLDNS_WIREPARSE_ERR_INVALID_STR; + for (i = 0; i < 4; i++) { + if (data[i+4] != 0) { + adflength = i + 1; + } + } + } else if (family == 2) { + /* ipv6 */ + if (inet_pton(AF_INET6, my_ip_str, data+4) == 0) + return GLDNS_WIREPARSE_ERR_INVALID_STR; + for (i = 0; i < 16; i++) { + if (data[i+4] != 0) { + adflength = i + 1; + } + } + } else { + /* unknown family */ + return GLDNS_WIREPARSE_ERR_INVALID_STR; + } + + my_str = strchr(my_str, '/') + 1; + prefix = (uint8_t) atoi(my_str); + + gldns_write_uint16(data, family); + data[2] = prefix; + data[3] = (uint8_t)adflength; + if (negation) { + /* set bit 1 of byte 3 */ + data[3] = data[3] | 0x80; + } + + if(*len < 4+adflength) + return GLDNS_WIREPARSE_ERR_BUFFER_TOO_SMALL; + memmove(rd, data, 4+adflength); + *len = 4+adflength; + return GLDNS_WIREPARSE_ERR_OK; +} + +int gldns_str2wire_b64_buf(const char* str, uint8_t* rd, size_t* len) +{ + size_t sz = gldns_b64_pton_calculate_size(strlen(str)); + int n; + if(*len < sz) + return GLDNS_WIREPARSE_ERR_BUFFER_TOO_SMALL; + n = gldns_b64_pton(str, rd, *len); + if(n < 0) + return GLDNS_WIREPARSE_ERR_SYNTAX_B64; + *len = (size_t)n; + return GLDNS_WIREPARSE_ERR_OK; +} + +int gldns_str2wire_b32_ext_buf(const char* str, uint8_t* rd, size_t* len) +{ + size_t slen = strlen(str); + size_t sz = gldns_b32_pton_calculate_size(slen); + int n; + if(*len < 1+sz) + return GLDNS_WIREPARSE_ERR_BUFFER_TOO_SMALL; + rd[0] = (uint8_t)sz; + n = gldns_b32_pton_extended_hex(str, slen, rd+1, *len-1); + if(n < 0) + return GLDNS_WIREPARSE_ERR_SYNTAX_B32_EXT; + *len = (size_t)n+1; + return GLDNS_WIREPARSE_ERR_OK; +} + +int gldns_str2wire_hex_buf(const char* str, uint8_t* rd, size_t* len) +{ + const char* s = str; + size_t dlen = 0; /* number of hexdigits parsed */ + while(*s) { + if(isspace(*s)) { + s++; + continue; + } + if(!isxdigit(*s)) + return RET_ERR(GLDNS_WIREPARSE_ERR_SYNTAX_HEX, s-str); + if(*len < dlen/2 + 1) + return RET_ERR(GLDNS_WIREPARSE_ERR_BUFFER_TOO_SMALL, + s-str); + if((dlen&1)==0) + rd[dlen/2] = (uint8_t)gldns_hexdigit_to_int(*s++) * 16; + else rd[dlen/2] += (uint8_t)gldns_hexdigit_to_int(*s++); + dlen++; + } + if((dlen&1)!=0) + return RET_ERR(GLDNS_WIREPARSE_ERR_SYNTAX_HEX, s-str); + *len = dlen/2; + return GLDNS_WIREPARSE_ERR_OK; +} + +int gldns_str2wire_nsec_buf(const char* str, uint8_t* rd, size_t* len) +{ + const char *delim = "\n\t "; + char token[64]; /* for a type name */ + size_t type_count = 0; + int block; + size_t used = 0; + uint16_t maxtype = 0; + uint8_t typebits[8192]; /* 65536 bits */ + uint8_t window_in_use[256]; + + /* string in buffer */ + gldns_buffer strbuf; + gldns_buffer_init_frm_data(&strbuf, (uint8_t*)str, strlen(str)); + + /* parse the types */ + memset(typebits, 0, sizeof(typebits)); + memset(window_in_use, 0, sizeof(window_in_use)); + while(gldns_buffer_remaining(&strbuf) > 0 && + gldns_bget_token(&strbuf, token, delim, sizeof(token)) != -1) { + uint16_t t = gldns_get_rr_type_by_name(token); + if(token[0] == 0) + continue; + if(t == 0 && strcmp(token, "TYPE0") != 0) + return RET_ERR(GLDNS_WIREPARSE_ERR_SYNTAX_TYPE, + gldns_buffer_position(&strbuf)); + typebits[t/8] |= (0x80>>(t%8)); + window_in_use[t/256] = 1; + type_count++; + if(t > maxtype) maxtype = t; + } + + /* empty NSEC bitmap */ + if(type_count == 0) { + *len = 0; + return GLDNS_WIREPARSE_ERR_OK; + } + + /* encode windows {u8 windowblock, u8 bitmaplength, 0-32u8 bitmap}, + * block is 0-255 upper octet of types, length if 0-32. */ + for(block = 0; block <= (int)maxtype/256; block++) { + int i, blocklen = 0; + if(!window_in_use[block]) + continue; + for(i=0; i<32; i++) { + if(typebits[block*32+i] != 0) + blocklen = i+1; + } + if(blocklen == 0) + continue; /* empty window should have been !in_use */ + if(used+blocklen+2 > *len) + return GLDNS_WIREPARSE_ERR_BUFFER_TOO_SMALL; + rd[used+0] = (uint8_t)block; + rd[used+1] = (uint8_t)blocklen; + for(i=0; iid); + } else { + int s = gldns_str2wire_int16_buf(str, rd, len); + if(s) return s; + if(gldns_read_uint16(rd) == 0) + return GLDNS_WIREPARSE_ERR_CERT_BAD_ALGORITHM; + } + *len = 2; + return GLDNS_WIREPARSE_ERR_OK; +} + +/* An alg field can either be specified as a 8 bits number + * or by its symbolic name. Handle both */ +int gldns_str2wire_alg_buf(const char* str, uint8_t* rd, size_t* len) +{ + gldns_lookup_table *lt = gldns_lookup_by_name(gldns_algorithms, str); + if(*len < 1) + return GLDNS_WIREPARSE_ERR_BUFFER_TOO_SMALL; + if(lt) { + rd[0] = (uint8_t)lt->id; + *len = 1; + } else { + /* try as-is (a number) */ + return gldns_str2wire_int8_buf(str, rd, len); + } + return GLDNS_WIREPARSE_ERR_OK; +} + +int gldns_str2wire_time_buf(const char* str, uint8_t* rd, size_t* len) +{ + /* convert a time YYYYDDMMHHMMSS to wireformat */ + struct tm tm; + if(*len < 4) + return GLDNS_WIREPARSE_ERR_BUFFER_TOO_SMALL; + + /* Try to scan the time... */ + memset(&tm, 0, sizeof(tm)); + if (strlen(str) == 14 && sscanf(str, "%4d%2d%2d%2d%2d%2d", + &tm.tm_year, &tm.tm_mon, &tm.tm_mday, &tm.tm_hour, + &tm.tm_min, &tm.tm_sec) == 6) { + tm.tm_year -= 1900; + tm.tm_mon--; + /* Check values */ + if (tm.tm_year < 70) + return GLDNS_WIREPARSE_ERR_SYNTAX_TIME; + if (tm.tm_mon < 0 || tm.tm_mon > 11) + return GLDNS_WIREPARSE_ERR_SYNTAX_TIME; + if (tm.tm_mday < 1 || tm.tm_mday > 31) + return GLDNS_WIREPARSE_ERR_SYNTAX_TIME; + if (tm.tm_hour < 0 || tm.tm_hour > 23) + return GLDNS_WIREPARSE_ERR_SYNTAX_TIME; + if (tm.tm_min < 0 || tm.tm_min > 59) + return GLDNS_WIREPARSE_ERR_SYNTAX_TIME; + if (tm.tm_sec < 0 || tm.tm_sec > 59) + return GLDNS_WIREPARSE_ERR_SYNTAX_TIME; + + gldns_write_uint32(rd, gldns_mktime_from_utc(&tm)); + } else { + /* handle it as 32 bits timestamp */ + char *end; + uint32_t l = (uint32_t)strtol((char*)str, &end, 10); + if(*end != 0) + return RET_ERR(GLDNS_WIREPARSE_ERR_SYNTAX_TIME, + end-(char*)str); + gldns_write_uint32(rd, l); + } + *len = 4; + return GLDNS_WIREPARSE_ERR_OK; +} + +int gldns_str2wire_period_buf(const char* str, uint8_t* rd, size_t* len) +{ + const char* end; + uint32_t p = gldns_str2period(str, &end); + if(*end != 0) + return RET_ERR(GLDNS_WIREPARSE_ERR_SYNTAX_PERIOD, end-str); + if(*len < 4) + return GLDNS_WIREPARSE_ERR_BUFFER_TOO_SMALL; + gldns_write_uint32(rd, p); + *len = 4; + return GLDNS_WIREPARSE_ERR_OK; +} + +/** read "[.][mM]" into mantissa exponent format for LOC type */ +static int +loc_parse_cm(char* my_str, char** endstr, uint8_t* m, uint8_t* e) +{ + uint32_t meters = 0, cm = 0, val; + while (isblank(*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); + } + if (meters >= 1) { + *e = 2; + val = meters; + } else { + *e = 0; + val = cm; + } + while(val >= 10) { + (*e)++; + val /= 10; + } + *m = (uint8_t)val; + + if (*e > 9) + return 0; + if (*my_str == 'm' || *my_str == 'M') { + my_str++; + } + *endstr = my_str; + return 1; +} + +int gldns_str2wire_loc_buf(const char* str, uint8_t* rd, size_t* len) +{ + uint32_t latitude = 0; + uint32_t longitude = 0; + uint32_t altitude = 0; + + uint32_t equator = (uint32_t)1<<31; /* 2**31 */ + + /* only support version 0 */ + uint32_t h = 0; + uint32_t m = 0; + uint8_t size_b = 1, size_e = 2; + uint8_t horiz_pre_b = 1, horiz_pre_e = 6; + uint8_t vert_pre_b = 1, vert_pre_e = 3; + + double s = 0.0; + int northerness; + int easterness; + + char *my_str = (char *) str; + + if (isdigit((int) *my_str)) { + h = (uint32_t) strtol(my_str, &my_str, 10); + } else { + return GLDNS_WIREPARSE_ERR_INVALID_STR; + } + + while (isblank((int) *my_str)) { + my_str++; + } + + if (isdigit((int) *my_str)) { + m = (uint32_t) strtol(my_str, &my_str, 10); + } else if (*my_str == 'N' || *my_str == 'S') { + goto north; + } else { + return GLDNS_WIREPARSE_ERR_INVALID_STR; + } + + while (isblank((int) *my_str)) { + my_str++; + } + + if (isdigit((int) *my_str)) { + s = strtod(my_str, &my_str); + } + + /* skip blanks before norterness */ + while (isblank((int) *my_str)) { + my_str++; + } + +north: + if (*my_str == 'N') { + northerness = 1; + } else if (*my_str == 'S') { + northerness = 0; + } else { + return GLDNS_WIREPARSE_ERR_INVALID_STR; + } + + my_str++; + + /* store number */ + s = 1000.0 * s; + /* add a little to make floor in conversion a round */ + s += 0.0005; + latitude = (uint32_t) s; + latitude += 1000 * 60 * m; + latitude += 1000 * 60 * 60 * h; + if (northerness) { + latitude = equator + latitude; + } else { + latitude = equator - latitude; + } + while (isblank(*my_str)) { + my_str++; + } + + if (isdigit((int) *my_str)) { + h = (uint32_t) strtol(my_str, &my_str, 10); + } else { + return GLDNS_WIREPARSE_ERR_INVALID_STR; + } + + while (isblank((int) *my_str)) { + my_str++; + } + + if (isdigit((int) *my_str)) { + m = (uint32_t) strtol(my_str, &my_str, 10); + } else if (*my_str == 'E' || *my_str == 'W') { + goto east; + } else { + return GLDNS_WIREPARSE_ERR_INVALID_STR; + } + + while (isblank(*my_str)) { + my_str++; + } + + if (isdigit((int) *my_str)) { + s = strtod(my_str, &my_str); + } + + /* skip blanks before easterness */ + while (isblank(*my_str)) { + my_str++; + } + +east: + if (*my_str == 'E') { + easterness = 1; + } else if (*my_str == 'W') { + easterness = 0; + } else { + return GLDNS_WIREPARSE_ERR_INVALID_STR; + } + + my_str++; + + /* store number */ + s *= 1000.0; + /* add a little to make floor in conversion a round */ + s += 0.0005; + longitude = (uint32_t) s; + longitude += 1000 * 60 * m; + longitude += 1000 * 60 * 60 * h; + + if (easterness) { + longitude += equator; + } else { + longitude = equator - longitude; + } + + altitude = (uint32_t)(strtod(my_str, &my_str)*100.0 + + 10000000.0 + 0.5); + if (*my_str == 'm' || *my_str == 'M') { + my_str++; + } + + if (strlen(my_str) > 0) { + if(!loc_parse_cm(my_str, &my_str, &size_b, &size_e)) + return GLDNS_WIREPARSE_ERR_INVALID_STR; + } + + if (strlen(my_str) > 0) { + if(!loc_parse_cm(my_str, &my_str, &horiz_pre_b, &horiz_pre_e)) + return GLDNS_WIREPARSE_ERR_INVALID_STR; + } + + if (strlen(my_str) > 0) { + if(!loc_parse_cm(my_str, &my_str, &vert_pre_b, &vert_pre_e)) + return GLDNS_WIREPARSE_ERR_INVALID_STR; + } + + if(*len < 16) + return GLDNS_WIREPARSE_ERR_BUFFER_TOO_SMALL; + rd[0] = 0; + rd[1] = ((size_b << 4) & 0xf0) | (size_e & 0x0f); + rd[2] = ((horiz_pre_b << 4) & 0xf0) | (horiz_pre_e & 0x0f); + rd[3] = ((vert_pre_b << 4) & 0xf0) | (vert_pre_e & 0x0f); + gldns_write_uint32(rd + 4, latitude); + gldns_write_uint32(rd + 8, longitude); + gldns_write_uint32(rd + 12, altitude); + *len = 16; + return GLDNS_WIREPARSE_ERR_OK; +} + +int gldns_str2wire_wks_buf(const char* str, uint8_t* rd, size_t* len) +{ + int rd_len = 1; + int have_proto = 0; + char token[50], proto_str[50]; + gldns_buffer strbuf; + gldns_buffer_init_frm_data(&strbuf, (uint8_t*)str, strlen(str)); + proto_str[0]=0; + + /* check we have one byte for proto */ + if(*len < 1) + return GLDNS_WIREPARSE_ERR_BUFFER_TOO_SMALL; + + while(gldns_bget_token(&strbuf, token, "\t\n ", sizeof(token)) > 0) { + if(!have_proto) { + struct protoent *p = getprotobyname(token); + have_proto = 1; + if(p) rd[0] = (uint8_t)p->p_proto; + else rd[0] = (uint8_t)atoi(token); + (void)strlcpy(proto_str, token, sizeof(proto_str)); + } else { + int serv_port; + struct servent *serv = getservbyname(token, proto_str); + if(serv) serv_port=(int)ntohs((uint16_t)serv->s_port); + else { + serv_port = atoi(token); + if(serv_port == 0 && strcmp(token, "0") != 0) { +#ifdef HAVE_ENDSERVENT + endservent(); +#endif +#ifdef HAVE_ENDPROTOENT + endprotoent(); +#endif + return RET_ERR(GLDNS_WIREPARSE_ERR_SYNTAX, + gldns_buffer_position(&strbuf)); + } + if(serv_port < 0 || serv_port > 65535) { +#ifdef HAVE_ENDSERVENT + endservent(); +#endif +#ifdef HAVE_ENDPROTOENT + endprotoent(); +#endif + return RET_ERR(GLDNS_WIREPARSE_ERR_SYNTAX, + gldns_buffer_position(&strbuf)); + } + } + if(rd_len < 1+serv_port/8+1) { + /* bitmap is larger, init new bytes at 0 */ + if(*len < 1+(size_t)serv_port/8+1) { +#ifdef HAVE_ENDSERVENT + endservent(); +#endif +#ifdef HAVE_ENDPROTOENT + endprotoent(); +#endif + return RET_ERR( + GLDNS_WIREPARSE_ERR_BUFFER_TOO_SMALL, + gldns_buffer_position(&strbuf)); + } + memset(rd+rd_len, 0, 1+(size_t)serv_port/8+1-rd_len); + rd_len = 1+serv_port/8+1; + } + rd[1+ serv_port/8] |= (1 << (7 - serv_port % 8)); + } + } + *len = (size_t)rd_len; + +#ifdef HAVE_ENDSERVENT + endservent(); +#endif +#ifdef HAVE_ENDPROTOENT + endprotoent(); +#endif + return GLDNS_WIREPARSE_ERR_OK; +} + +int gldns_str2wire_nsap_buf(const char* str, uint8_t* rd, size_t* len) +{ + const char* s = str; + size_t slen; + size_t dlen = 0; /* number of hexdigits parsed */ + + /* just a hex string with optional dots? */ + if (s[0] != '0' || s[1] != 'x') + return GLDNS_WIREPARSE_ERR_INVALID_STR; + s += 2; + slen = strlen(s); + if(slen > GLDNS_MAX_RDFLEN*2) + return GLDNS_WIREPARSE_ERR_LABEL_OVERFLOW; + while(*s) { + if(isspace(*s) || *s == '.') { + s++; + continue; + } + if(!isxdigit(*s)) + return RET_ERR(GLDNS_WIREPARSE_ERR_SYNTAX_HEX, s-str); + if(*len < dlen/2 + 1) + return RET_ERR(GLDNS_WIREPARSE_ERR_BUFFER_TOO_SMALL, + s-str); + if((dlen&1)==0) + rd[dlen/2] = (uint8_t)gldns_hexdigit_to_int(*s++) * 16; + else rd[dlen/2] += gldns_hexdigit_to_int(*s++); + dlen++; + } + if((dlen&1)!=0) + return RET_ERR(GLDNS_WIREPARSE_ERR_SYNTAX_HEX, s-str); + *len = dlen/2; + return GLDNS_WIREPARSE_ERR_OK; +} + +int gldns_str2wire_atma_buf(const char* str, uint8_t* rd, size_t* len) +{ + const char* s = str; + size_t slen = strlen(str); + size_t dlen = 0; /* number of hexdigits parsed */ + + /* just a hex string with optional dots? */ + /* notimpl e.164 format */ + if(slen > GLDNS_MAX_RDFLEN*2) + return GLDNS_WIREPARSE_ERR_LABEL_OVERFLOW; + while(*s) { + if(isspace(*s) || *s == '.') { + s++; + continue; + } + if(!isxdigit(*s)) + return RET_ERR(GLDNS_WIREPARSE_ERR_SYNTAX_HEX, s-str); + if(*len < dlen/2 + 1) + return RET_ERR(GLDNS_WIREPARSE_ERR_BUFFER_TOO_SMALL, + s-str); + if((dlen&1)==0) + rd[dlen/2] = (uint8_t)gldns_hexdigit_to_int(*s++) * 16; + else rd[dlen/2] += gldns_hexdigit_to_int(*s++); + dlen++; + } + if((dlen&1)!=0) + return RET_ERR(GLDNS_WIREPARSE_ERR_SYNTAX_HEX, s-str); + *len = dlen/2; + return GLDNS_WIREPARSE_ERR_OK; +} + +int gldns_str2wire_ipseckey_buf(const char* str, uint8_t* rd, size_t* len) +{ + size_t gwlen = 0, keylen = 0; + int s; + uint8_t gwtype; + char token[512]; + gldns_buffer strbuf; + gldns_buffer_init_frm_data(&strbuf, (uint8_t*)str, strlen(str)); + + if(*len < 3) + return GLDNS_WIREPARSE_ERR_BUFFER_TOO_SMALL; + /* precedence */ + if(gldns_bget_token(&strbuf, token, "\t\n ", sizeof(token)) <= 0) + return RET_ERR(GLDNS_WIREPARSE_ERR_INVALID_STR, + gldns_buffer_position(&strbuf)); + rd[0] = (uint8_t)atoi(token); + /* gateway_type */ + if(gldns_bget_token(&strbuf, token, "\t\n ", sizeof(token)) <= 0) + return RET_ERR(GLDNS_WIREPARSE_ERR_INVALID_STR, + gldns_buffer_position(&strbuf)); + rd[1] = (uint8_t)atoi(token); + gwtype = rd[1]; + /* algorithm */ + if(gldns_bget_token(&strbuf, token, "\t\n ", sizeof(token)) <= 0) + return RET_ERR(GLDNS_WIREPARSE_ERR_INVALID_STR, + gldns_buffer_position(&strbuf)); + rd[2] = (uint8_t)atoi(token); + + /* gateway */ + if(gldns_bget_token(&strbuf, token, "\t\n ", sizeof(token)) <= 0) + return RET_ERR(GLDNS_WIREPARSE_ERR_INVALID_STR, + gldns_buffer_position(&strbuf)); + if(gwtype == 0) { + /* NOGATEWAY */ + if(strcmp(token, ".") != 0) + return RET_ERR(GLDNS_WIREPARSE_ERR_INVALID_STR, + gldns_buffer_position(&strbuf)); + gwlen = 0; + } else if(gwtype == 1) { + /* IP4 */ + gwlen = *len - 3; + s = gldns_str2wire_a_buf(token, rd+3, &gwlen); + if(s) return RET_ERR_SHIFT(s, gldns_buffer_position(&strbuf)); + } else if(gwtype == 2) { + /* IP6 */ + gwlen = *len - 3; + s = gldns_str2wire_aaaa_buf(token, rd+3, &gwlen); + if(s) return RET_ERR_SHIFT(s, gldns_buffer_position(&strbuf)); + } else if(gwtype == 3) { + /* DNAME */ + gwlen = *len - 3; + s = gldns_str2wire_dname_buf(token, rd+3, &gwlen); + if(s) return RET_ERR_SHIFT(s, gldns_buffer_position(&strbuf)); + } else { + /* unknown gateway type */ + return RET_ERR(GLDNS_WIREPARSE_ERR_INVALID_STR, + gldns_buffer_position(&strbuf)); + } + /* double check for size */ + if(*len < 3 + gwlen) + return RET_ERR(GLDNS_WIREPARSE_ERR_BUFFER_TOO_SMALL, + gldns_buffer_position(&strbuf)); + + /* publickey in remainder of strbuf */ + keylen = *len - 3 - gwlen; + s = gldns_str2wire_b64_buf((const char*)gldns_buffer_current(&strbuf), + rd+3+gwlen, &keylen); + if(s) return RET_ERR_SHIFT(s, gldns_buffer_position(&strbuf)); + + *len = 3 + gwlen + keylen; + return GLDNS_WIREPARSE_ERR_OK; +} + +int gldns_str2wire_nsec3_salt_buf(const char* str, uint8_t* rd, size_t* len) +{ + int i, salt_length_str = (int)strlen(str); + if (salt_length_str == 1 && str[0] == '-') { + salt_length_str = 0; + } else if (salt_length_str % 2 != 0) { + return GLDNS_WIREPARSE_ERR_SYNTAX_HEX; + } + if (salt_length_str > 512) + return GLDNS_WIREPARSE_ERR_SYNTAX_HEX; + if(*len < 1+(size_t)salt_length_str / 2) + return GLDNS_WIREPARSE_ERR_BUFFER_TOO_SMALL; + rd[0] = (uint8_t) (salt_length_str / 2); + for (i = 0; i < salt_length_str; i += 2) { + if (isxdigit((int)str[i]) && isxdigit((int)str[i+1])) { + rd[1+i/2] = (uint8_t)(gldns_hexdigit_to_int(str[i])*16 + + gldns_hexdigit_to_int(str[i+1])); + } else { + return RET_ERR(GLDNS_WIREPARSE_ERR_SYNTAX_HEX, i); + } + } + *len = 1 + (size_t)rd[0]; + return GLDNS_WIREPARSE_ERR_OK; +} + +int gldns_str2wire_ilnp64_buf(const char* str, uint8_t* rd, size_t* len) +{ + unsigned int a, b, c, d; + uint16_t shorts[4]; + int l; + if(*len < sizeof(shorts)) + return GLDNS_WIREPARSE_ERR_BUFFER_TOO_SMALL; + + if (sscanf(str, "%4x:%4x:%4x:%4x%n", &a, &b, &c, &d, &l) != 4 || + l != (int)strlen(str) || /* more data to read */ + strpbrk(str, "+-") /* signed hexes */ + ) + return GLDNS_WIREPARSE_ERR_SYNTAX_ILNP64; + shorts[0] = htons(a); + shorts[1] = htons(b); + shorts[2] = htons(c); + shorts[3] = htons(d); + memmove(rd, &shorts, sizeof(shorts)); + *len = sizeof(shorts); + return GLDNS_WIREPARSE_ERR_OK; +} + +int gldns_str2wire_eui48_buf(const char* str, uint8_t* rd, size_t* len) +{ + unsigned int a, b, c, d, e, f; + int l; + + if(*len < 6) + return GLDNS_WIREPARSE_ERR_BUFFER_TOO_SMALL; + if (sscanf(str, "%2x-%2x-%2x-%2x-%2x-%2x%n", + &a, &b, &c, &d, &e, &f, &l) != 6 || + l != (int)strlen(str)) + return GLDNS_WIREPARSE_ERR_SYNTAX_EUI48; + rd[0] = a; + rd[1] = b; + rd[2] = c; + rd[3] = d; + rd[4] = e; + rd[5] = f; + *len = 6; + return GLDNS_WIREPARSE_ERR_OK; +} + +int gldns_str2wire_eui64_buf(const char* str, uint8_t* rd, size_t* len) +{ + unsigned int a, b, c, d, e, f, g, h; + int l; + + if(*len < 8) + return GLDNS_WIREPARSE_ERR_BUFFER_TOO_SMALL; + if (sscanf(str, "%2x-%2x-%2x-%2x-%2x-%2x-%2x-%2x%n", + &a, &b, &c, &d, &e, &f, &g, &h, &l) != 8 || + l != (int)strlen(str)) + return GLDNS_WIREPARSE_ERR_SYNTAX_EUI64; + rd[0] = a; + rd[1] = b; + rd[2] = c; + rd[3] = d; + rd[4] = e; + rd[5] = f; + rd[6] = g; + rd[7] = h; + *len = 8; + return GLDNS_WIREPARSE_ERR_OK; +} + +int gldns_str2wire_tag_buf(const char* str, uint8_t* rd, size_t* len) +{ + size_t slen = strlen(str); + const char* ptr; + + if (slen > 255) + return GLDNS_WIREPARSE_ERR_SYNTAX_TAG; + if(*len < slen+1) + return GLDNS_WIREPARSE_ERR_BUFFER_TOO_SMALL; + for (ptr = str; *ptr; ptr++) { + if(!isalnum(*ptr)) + return RET_ERR(GLDNS_WIREPARSE_ERR_SYNTAX_TAG, ptr-str); + } + rd[0] = slen; + memmove(rd+1, str, slen); + *len = slen+1; + return GLDNS_WIREPARSE_ERR_OK; +} + +int gldns_str2wire_long_str_buf(const char* str, uint8_t* rd, size_t* len) +{ + uint8_t ch = 0; + const char* pstr = str; + size_t length = 0; + + /* Fill data with parsed bytes */ + while (gldns_parse_char(&ch, &pstr)) { + if(*len < length+1) + return GLDNS_WIREPARSE_ERR_BUFFER_TOO_SMALL; + rd[length++] = ch; + } + if(!pstr) + return GLDNS_WIREPARSE_ERR_SYNTAX_BAD_ESCAPE; + *len = length; + return GLDNS_WIREPARSE_ERR_OK; +} + +int gldns_str2wire_hip_buf(const char* str, uint8_t* rd, size_t* len) +{ + char* s, *end; + int e; + size_t hitlen, pklen = 0; + /* presentation format: + * pk-algo HIThex pubkeybase64 + * wireformat: + * hitlen[1byte] pkalgo[1byte] pubkeylen[2byte] [hit] [pubkey] */ + if(*len < 4) + return GLDNS_WIREPARSE_ERR_BUFFER_TOO_SMALL; + + /* read PK algorithm */ + rd[1] = (uint8_t)strtol((char*)str, &s, 10); + if(*s != ' ') + return RET_ERR(GLDNS_WIREPARSE_ERR_SYNTAX_INT, s-(char*)str); + s++; + while(*s == ' ') + s++; + + /* read HIT hex tag */ + /* zero terminate the tag (replace later) */ + end = strchr(s, ' '); + if(!end) return RET_ERR(GLDNS_WIREPARSE_ERR_SYNTAX, s-(char*)str); + *end = 0; + hitlen = *len - 4; + if((e = gldns_str2wire_hex_buf(s, rd+4, &hitlen)) != 0) { + *end = ' '; + return RET_ERR_SHIFT(e, s-(char*)str); + } + if(hitlen > 255) { + *end = ' '; + return RET_ERR(GLDNS_WIREPARSE_ERR_LABEL_OVERFLOW, s-(char*)str+255*2); + } + rd[0] = (uint8_t)hitlen; + *end = ' '; + s = end+1; + + /* read pubkey base64 sequence */ + pklen = *len - 4 - hitlen; + if((e = gldns_str2wire_b64_buf(s, rd+4+hitlen, &pklen)) != 0) + return RET_ERR_SHIFT(e, s-(char*)str); + if(pklen > 65535) + return RET_ERR(GLDNS_WIREPARSE_ERR_LABEL_OVERFLOW, s-(char*)str+65535); + gldns_write_uint16(rd+2, pklen); + + *len = 4 + hitlen + pklen; + return GLDNS_WIREPARSE_ERR_OK; +} + +int gldns_str2wire_int16_data_buf(const char* str, uint8_t* rd, size_t* len) +{ + size_t sz = gldns_b64_pton_calculate_size(strlen(str)); + int n; + if(*len < sz+2) + return GLDNS_WIREPARSE_ERR_BUFFER_TOO_SMALL; + if(sz > 65535) + return GLDNS_WIREPARSE_ERR_LABEL_OVERFLOW; + n = gldns_b64_pton(str, rd+2, (*len)-2); + if(n < 0) + return GLDNS_WIREPARSE_ERR_SYNTAX_B64; + gldns_write_uint16(rd, (uint16_t)n); + *len = (size_t)n; + return GLDNS_WIREPARSE_ERR_OK; +} diff --git a/src/gldns/str2wire.h b/src/gldns/str2wire.h new file mode 100644 index 00000000..f892f6d1 --- /dev/null +++ b/src/gldns/str2wire.h @@ -0,0 +1,541 @@ +/** + * str2wire.h - read txt presentation of RRs + * + * (c) NLnet Labs, 2005-2006 + * + * See the file LICENSE for the license + */ + +/** + * \file + * + * Parses text to wireformat. + */ + +#ifndef GLDNS_STR2WIRE_H +#define GLDNS_STR2WIRE_H + +/* include rrdef for MAX_DOMAINLEN constant */ +#include "gldns/rrdef.h" + +#ifdef __cplusplus +extern "C" { +#endif +struct gldns_struct_lookup_table; + +/** buffer to read an RR, cannot be larger than 64K because of packet size */ +#define GLDNS_RR_BUF_SIZE 65535 /* bytes */ +#define GLDNS_DEFAULT_TTL 3600 + +/* + * To convert class and type to string see + * gldns_get_rr_class_by_name(str) + * gldns_get_rr_type_by_name(str) + * from rrdef.h + */ + +/** + * Convert text string into dname wireformat, mallocless, with user buffer. + * @param str: the text string with the domain name. + * @param buf: the result buffer, suggested size GLDNS_MAX_DOMAINLEN+1 + * @param len: length of the buffer on input, length of the result on output. + * @return 0 on success, otherwise an error. + */ +int gldns_str2wire_dname_buf(const char* str, uint8_t* buf, size_t* len); + +/** + * Same as gldns_str2wire_dname_buf, but concatenates origin if the domain + * name is relative (does not end in '.'). + * @param str: the text string with the domain name. + * @param buf: the result buffer, suggested size GLDNS_MAX_DOMAINLEN+1 + * @param len: length of the buffer on input, length of the result on output. + * @param origin: the origin to append or NULL (nothing is appended). + * @param origin_len: length of origin. + * @return 0 on success, otherwise an error. + */ +int gldns_str2wire_dname_buf_origin(const char* str, uint8_t* buf, size_t* len, + uint8_t* origin, size_t origin_len); + +/** + * Convert text string into dname wireformat + * @param str: the text string with the domain name. + * @param len: returned length of wireformat. + * @return wireformat dname (malloced) or NULL on failure. + */ +uint8_t* gldns_str2wire_dname(const char* str, size_t* len); + +/** + * Convert text RR to wireformat, with user buffer. + * @param str: the RR data in text presentation format. + * @param rr: the buffer where the result is stored into. This buffer has + * the wire-dname(uncompressed), type, class, ttl, rdatalen, rdata. + * These values are probably not aligned, and in network format. + * Use the gldns_wirerr_get_xxx functions to access them safely. + * buffer size GLDNS_RR_BUF_SIZE is suggested. + * @param len: on input the length of the buffer, on output the amount of + * the buffer used for the rr. + * @param dname_len: if non-NULL, filled with the dname length as result. + * Because after the dname you find the type, class, ttl, rdatalen, rdata. + * @param default_ttl: TTL used if no TTL available. + * @param origin: used for origin dname (if not NULL) + * @param origin_len: length of origin. + * @param prev: used for prev_rr dname (if not NULL) + * @param prev_len: length of prev. + * @return 0 on success, an error on failure. + */ +int gldns_str2wire_rr_buf(const char* str, uint8_t* rr, size_t* len, + size_t* dname_len, uint32_t default_ttl, uint8_t* origin, + size_t origin_len, uint8_t* prev, size_t prev_len); + +/** + * Same as gldns_str2wire_rr_buf, but there is no rdata, it returns an RR + * with zero rdata and no ttl. It has name, type, class. + * You can access those with the gldns_wirerr_get_type and class functions. + * @param str: the RR data in text presentation format. + * @param rr: the buffer where the result is stored into. + * @param len: on input the length of the buffer, on output the amount of + * the buffer used for the rr. + * @param dname_len: if non-NULL, filled with the dname length as result. + * Because after the dname you find the type, class, ttl, rdatalen, rdata. + * @param origin: used for origin dname (if not NULL) + * @param origin_len: length of origin. + * @param prev: used for prev_rr dname (if not NULL) + * @param prev_len: length of prev. + * @return 0 on success, an error on failure. + */ +int gldns_str2wire_rr_question_buf(const char* str, uint8_t* rr, size_t* len, + size_t* dname_len, uint8_t* origin, size_t origin_len, uint8_t* prev, + size_t prev_len); + +/** + * Get the type of the RR. + * @param rr: the RR in wire format. + * @param len: rr length. + * @param dname_len: dname length to skip. + * @return type in host byteorder + */ +uint16_t gldns_wirerr_get_type(uint8_t* rr, size_t len, size_t dname_len); + +/** + * Get the class of the RR. + * @param rr: the RR in wire format. + * @param len: rr length. + * @param dname_len: dname length to skip. + * @return class in host byteorder + */ +uint16_t gldns_wirerr_get_class(uint8_t* rr, size_t len, size_t dname_len); + +/** + * Get the ttl of the RR. + * @param rr: the RR in wire format. + * @param len: rr length. + * @param dname_len: dname length to skip. + * @return ttl in host byteorder + */ +uint32_t gldns_wirerr_get_ttl(uint8_t* rr, size_t len, size_t dname_len); + +/** + * Get the rdata length of the RR. + * @param rr: the RR in wire format. + * @param len: rr length. + * @param dname_len: dname length to skip. + * @return rdata length in host byteorder + * If the rdata length is larger than the rr-len allows, it is truncated. + * So, that it is safe to read the data length returned + * from this function from the rdata pointer of gldns_wirerr_get_rdata. + */ +uint16_t gldns_wirerr_get_rdatalen(uint8_t* rr, size_t len, size_t dname_len); + +/** + * Get the rdata pointer of the RR. + * @param rr: the RR in wire format. + * @param len: rr length. + * @param dname_len: dname length to skip. + * @return rdata pointer + */ +uint8_t* gldns_wirerr_get_rdata(uint8_t* rr, size_t len, size_t dname_len); + +/** + * Get the rdata pointer of the RR. prefixed with rdata length. + * @param rr: the RR in wire format. + * @param len: rr length. + * @param dname_len: dname length to skip. + * @return pointer to rdatalength, followed by the rdata. + */ +uint8_t* gldns_wirerr_get_rdatawl(uint8_t* rr, size_t len, size_t dname_len); + +/** + * Parse result codes + */ +#define GLDNS_WIREPARSE_MASK 0x0fff +#define GLDNS_WIREPARSE_SHIFT 12 +#define GLDNS_WIREPARSE_ERROR(e) ((e)&GLDNS_WIREPARSE_MASK) +#define GLDNS_WIREPARSE_OFFSET(e) (((e)&~GLDNS_WIREPARSE_MASK)>>GLDNS_WIREPARSE_SHIFT) +/* use lookuptable to get error string, gldns_wireparse_errors */ +#define GLDNS_WIREPARSE_ERR_OK 0 +#define GLDNS_WIREPARSE_ERR_GENERAL 342 +#define GLDNS_WIREPARSE_ERR_DOMAINNAME_OVERFLOW 343 +#define GLDNS_WIREPARSE_ERR_DOMAINNAME_UNDERFLOW 344 +#define GLDNS_WIREPARSE_ERR_BUFFER_TOO_SMALL 345 +#define GLDNS_WIREPARSE_ERR_LABEL_OVERFLOW 346 +#define GLDNS_WIREPARSE_ERR_EMPTY_LABEL 347 +#define GLDNS_WIREPARSE_ERR_SYNTAX_BAD_ESCAPE 348 +#define GLDNS_WIREPARSE_ERR_SYNTAX 349 +#define GLDNS_WIREPARSE_ERR_SYNTAX_TTL 350 +#define GLDNS_WIREPARSE_ERR_SYNTAX_TYPE 351 +#define GLDNS_WIREPARSE_ERR_SYNTAX_CLASS 352 +#define GLDNS_WIREPARSE_ERR_SYNTAX_RDATA 353 +#define GLDNS_WIREPARSE_ERR_SYNTAX_MISSING_VALUE 354 +#define GLDNS_WIREPARSE_ERR_INVALID_STR 355 +#define GLDNS_WIREPARSE_ERR_SYNTAX_B64 356 +#define GLDNS_WIREPARSE_ERR_SYNTAX_B32_EXT 357 +#define GLDNS_WIREPARSE_ERR_SYNTAX_HEX 358 +#define GLDNS_WIREPARSE_ERR_CERT_BAD_ALGORITHM 359 +#define GLDNS_WIREPARSE_ERR_SYNTAX_TIME 360 +#define GLDNS_WIREPARSE_ERR_SYNTAX_PERIOD 361 +#define GLDNS_WIREPARSE_ERR_SYNTAX_ILNP64 362 +#define GLDNS_WIREPARSE_ERR_SYNTAX_EUI48 363 +#define GLDNS_WIREPARSE_ERR_SYNTAX_EUI64 364 +#define GLDNS_WIREPARSE_ERR_SYNTAX_TAG 365 +#define GLDNS_WIREPARSE_ERR_NOT_IMPL 366 +#define GLDNS_WIREPARSE_ERR_SYNTAX_INT 367 +#define GLDNS_WIREPARSE_ERR_SYNTAX_IP4 368 +#define GLDNS_WIREPARSE_ERR_SYNTAX_IP6 369 +#define GLDNS_WIREPARSE_ERR_SYNTAX_INTEGER_OVERFLOW 370 +#define GLDNS_WIREPARSE_ERR_INCLUDE 371 +#define GLDNS_WIREPARSE_ERR_PARENTHESIS 372 + +/** + * Get reference to a constant string for the (parse) error. + * @param e: error return value + * @return string. + */ +const char* gldns_get_errorstr_parse(int e); + +/** + * wire parse state for parsing files + */ +struct gldns_file_parse_state { + /** the origin domain name, if len!=0. uncompressed wireformat */ + uint8_t origin[GLDNS_MAX_DOMAINLEN+1]; + /** length of origin domain name, in bytes. 0 if not set. */ + size_t origin_len; + /** the previous domain name, if len!=0. uncompressed wireformat*/ + uint8_t prev_rr[GLDNS_MAX_DOMAINLEN+1]; + /** length of the previous domain name, in bytes. 0 if not set. */ + size_t prev_rr_len; + /** default TTL, this is used if the text does not specify a TTL, + * host byteorder */ + uint32_t default_ttl; + /** line number information */ + int lineno; +}; + +/** + * Read one RR from zonefile with buffer for the data. + * @param in: file that is read from (one RR, multiple lines if it spans them). + * @param rr: this is malloced by the user and the result is stored here, + * if an RR is read. If no RR is read this is signalled with the + * return len set to 0 (for ORIGIN, TTL directives). + * @param len: on input, the length of the rr buffer. on output the rr len. + * Buffer size of 64k should be enough. + * @param dname_len: returns the length of the dname initial part of the rr. + * @param parse_state: pass a pointer to user-allocated struct. + * Contents are maintained by this function. + * If you pass NULL then ORIGIN and TTL directives are not honored. + * You can start out with a particular origin by pre-filling it. + * otherwise, zero the structure before passing it. + * lineno is incremented when a newline is passed by the parser, + * you should initialize it at 1 at the start of the file. + * @return 0 on success, error on failure. + */ +int gldns_fp2wire_rr_buf(FILE* in, uint8_t* rr, size_t* len, size_t* dname_len, + struct gldns_file_parse_state* parse_state); + +/** + * Convert one rdf in rdata to wireformat and parse from string. + * @param str: the text to convert for this rdata element. + * @param rd: rdata buffer for the wireformat. + * @param len: length of rd buffer on input, used length on output. + * @param rdftype: the type of the rdf. + * @return 0 on success, error on failure. + */ +int gldns_str2wire_rdf_buf(const char* str, uint8_t* rd, size_t* len, + gldns_rdf_type rdftype); + +/** + * Convert rdf of type GLDNS_RDF_TYPE_INT8 from string to wireformat. + * @param str: the text to convert for this rdata element. + * @param rd: rdata buffer for the wireformat. + * @param len: length of rd buffer on input, used length on output. + * @return 0 on success, error on failure. + */ +int gldns_str2wire_int8_buf(const char* str, uint8_t* rd, size_t* len); + +/** + * Convert rdf of type GLDNS_RDF_TYPE_INT16 from string to wireformat. + * @param str: the text to convert for this rdata element. + * @param rd: rdata buffer for the wireformat. + * @param len: length of rd buffer on input, used length on output. + * @return 0 on success, error on failure. + */ +int gldns_str2wire_int16_buf(const char* str, uint8_t* rd, size_t* len); + +/** + * Convert rdf of type GLDNS_RDF_TYPE_INT32 from string to wireformat. + * @param str: the text to convert for this rdata element. + * @param rd: rdata buffer for the wireformat. + * @param len: length of rd buffer on input, used length on output. + * @return 0 on success, error on failure. + */ +int gldns_str2wire_int32_buf(const char* str, uint8_t* rd, size_t* len); + +/** + * Convert rdf of type GLDNS_RDF_TYPE_A from string to wireformat. + * @param str: the text to convert for this rdata element. + * @param rd: rdata buffer for the wireformat. + * @param len: length of rd buffer on input, used length on output. + * @return 0 on success, error on failure. + */ +int gldns_str2wire_a_buf(const char* str, uint8_t* rd, size_t* len); + +/** + * Convert rdf of type GLDNS_RDF_TYPE_AAAA from string to wireformat. + * @param str: the text to convert for this rdata element. + * @param rd: rdata buffer for the wireformat. + * @param len: length of rd buffer on input, used length on output. + * @return 0 on success, error on failure. + */ +int gldns_str2wire_aaaa_buf(const char* str, uint8_t* rd, size_t* len); + +/** + * Convert rdf of type GLDNS_RDF_TYPE_STR from string to wireformat. + * @param str: the text to convert for this rdata element. + * @param rd: rdata buffer for the wireformat. + * @param len: length of rd buffer on input, used length on output. + * @return 0 on success, error on failure. + */ +int gldns_str2wire_str_buf(const char* str, uint8_t* rd, size_t* len); + +/** + * Convert rdf of type GLDNS_RDF_TYPE_APL from string to wireformat. + * @param str: the text to convert for this rdata element. + * @param rd: rdata buffer for the wireformat. + * @param len: length of rd buffer on input, used length on output. + * @return 0 on success, error on failure. + */ +int gldns_str2wire_apl_buf(const char* str, uint8_t* rd, size_t* len); + +/** + * Convert rdf of type GLDNS_RDF_TYPE_B64 from string to wireformat. + * @param str: the text to convert for this rdata element. + * @param rd: rdata buffer for the wireformat. + * @param len: length of rd buffer on input, used length on output. + * @return 0 on success, error on failure. + */ +int gldns_str2wire_b64_buf(const char* str, uint8_t* rd, size_t* len); + +/** + * Convert rdf of type GLDNS_RDF_TYPE_B32_EXT from string to wireformat. + * And also GLDNS_RDF_TYPE_NSEC3_NEXT_OWNER. + * @param str: the text to convert for this rdata element. + * @param rd: rdata buffer for the wireformat. + * @param len: length of rd buffer on input, used length on output. + * @return 0 on success, error on failure. + */ +int gldns_str2wire_b32_ext_buf(const char* str, uint8_t* rd, size_t* len); + +/** + * Convert rdf of type GLDNS_RDF_TYPE_HEX from string to wireformat. + * @param str: the text to convert for this rdata element. + * @param rd: rdata buffer for the wireformat. + * @param len: length of rd buffer on input, used length on output. + * @return 0 on success, error on failure. + */ +int gldns_str2wire_hex_buf(const char* str, uint8_t* rd, size_t* len); + +/** + * Convert rdf of type GLDNS_RDF_TYPE_NSEC from string to wireformat. + * @param str: the text to convert for this rdata element. + * @param rd: rdata buffer for the wireformat. + * @param len: length of rd buffer on input, used length on output. + * @return 0 on success, error on failure. + */ +int gldns_str2wire_nsec_buf(const char* str, uint8_t* rd, size_t* len); + +/** + * Convert rdf of type GLDNS_RDF_TYPE_TYPE from string to wireformat. + * @param str: the text to convert for this rdata element. + * @param rd: rdata buffer for the wireformat. + * @param len: length of rd buffer on input, used length on output. + * @return 0 on success, error on failure. + */ +int gldns_str2wire_type_buf(const char* str, uint8_t* rd, size_t* len); + +/** + * Convert rdf of type GLDNS_RDF_TYPE_CLASS from string to wireformat. + * @param str: the text to convert for this rdata element. + * @param rd: rdata buffer for the wireformat. + * @param len: length of rd buffer on input, used length on output. + * @return 0 on success, error on failure. + */ +int gldns_str2wire_class_buf(const char* str, uint8_t* rd, size_t* len); + +/** + * Convert rdf of type GLDNS_RDF_TYPE_CERT_ALG from string to wireformat. + * @param str: the text to convert for this rdata element. + * @param rd: rdata buffer for the wireformat. + * @param len: length of rd buffer on input, used length on output. + * @return 0 on success, error on failure. + */ +int gldns_str2wire_cert_alg_buf(const char* str, uint8_t* rd, size_t* len); + +/** + * Convert rdf of type GLDNS_RDF_TYPE_ALG from string to wireformat. + * @param str: the text to convert for this rdata element. + * @param rd: rdata buffer for the wireformat. + * @param len: length of rd buffer on input, used length on output. + * @return 0 on success, error on failure. + */ +int gldns_str2wire_alg_buf(const char* str, uint8_t* rd, size_t* len); + +/** + * Convert rdf of type GLDNS_RDF_TYPE_TIME from string to wireformat. + * @param str: the text to convert for this rdata element. + * @param rd: rdata buffer for the wireformat. + * @param len: length of rd buffer on input, used length on output. + * @return 0 on success, error on failure. + */ +int gldns_str2wire_time_buf(const char* str, uint8_t* rd, size_t* len); + +/** + * Convert rdf of type GLDNS_RDF_TYPE_PERIOD from string to wireformat. + * @param str: the text to convert for this rdata element. + * @param rd: rdata buffer for the wireformat. + * @param len: length of rd buffer on input, used length on output. + * @return 0 on success, error on failure. + */ +int gldns_str2wire_period_buf(const char* str, uint8_t* rd, size_t* len); + +/** + * Convert rdf of type GLDNS_RDF_TYPE_LOC from string to wireformat. + * @param str: the text to convert for this rdata element. + * @param rd: rdata buffer for the wireformat. + * @param len: length of rd buffer on input, used length on output. + * @return 0 on success, error on failure. + */ +int gldns_str2wire_loc_buf(const char* str, uint8_t* rd, size_t* len); + +/** + * Convert rdf of type GLDNS_RDF_TYPE_WKS from string to wireformat. + * @param str: the text to convert for this rdata element. + * @param rd: rdata buffer for the wireformat. + * @param len: length of rd buffer on input, used length on output. + * @return 0 on success, error on failure. + */ +int gldns_str2wire_wks_buf(const char* str, uint8_t* rd, size_t* len); + +/** + * Convert rdf of type GLDNS_RDF_TYPE_NSAP from string to wireformat. + * @param str: the text to convert for this rdata element. + * @param rd: rdata buffer for the wireformat. + * @param len: length of rd buffer on input, used length on output. + * @return 0 on success, error on failure. + */ +int gldns_str2wire_nsap_buf(const char* str, uint8_t* rd, size_t* len); + +/** + * Convert rdf of type GLDNS_RDF_TYPE_ATMA from string to wireformat. + * @param str: the text to convert for this rdata element. + * @param rd: rdata buffer for the wireformat. + * @param len: length of rd buffer on input, used length on output. + * @return 0 on success, error on failure. + */ +int gldns_str2wire_atma_buf(const char* str, uint8_t* rd, size_t* len); + +/** + * Convert rdf of type GLDNS_RDF_TYPE_IPSECKEY from string to wireformat. + * @param str: the text to convert for this rdata element. + * @param rd: rdata buffer for the wireformat. + * @param len: length of rd buffer on input, used length on output. + * @return 0 on success, error on failure. + */ +int gldns_str2wire_ipseckey_buf(const char* str, uint8_t* rd, size_t* len); + +/** + * Convert rdf of type GLDNS_RDF_TYPE_NSEC3_SALT from string to wireformat. + * @param str: the text to convert for this rdata element. + * @param rd: rdata buffer for the wireformat. + * @param len: length of rd buffer on input, used length on output. + * @return 0 on success, error on failure. + */ +int gldns_str2wire_nsec3_salt_buf(const char* str, uint8_t* rd, size_t* len); + +/** + * Convert rdf of type GLDNS_RDF_TYPE_ILNP64 from string to wireformat. + * @param str: the text to convert for this rdata element. + * @param rd: rdata buffer for the wireformat. + * @param len: length of rd buffer on input, used length on output. + * @return 0 on success, error on failure. + */ +int gldns_str2wire_ilnp64_buf(const char* str, uint8_t* rd, size_t* len); + +/** + * Convert rdf of type GLDNS_RDF_TYPE_EUI48 from string to wireformat. + * @param str: the text to convert for this rdata element. + * @param rd: rdata buffer for the wireformat. + * @param len: length of rd buffer on input, used length on output. + * @return 0 on success, error on failure. + */ +int gldns_str2wire_eui48_buf(const char* str, uint8_t* rd, size_t* len); + +/** + * Convert rdf of type GLDNS_RDF_TYPE_EUI64 from string to wireformat. + * @param str: the text to convert for this rdata element. + * @param rd: rdata buffer for the wireformat. + * @param len: length of rd buffer on input, used length on output. + * @return 0 on success, error on failure. + */ +int gldns_str2wire_eui64_buf(const char* str, uint8_t* rd, size_t* len); + +/** + * Convert rdf of type GLDNS_RDF_TYPE_TAG from string to wireformat. + * @param str: the text to convert for this rdata element. + * @param rd: rdata buffer for the wireformat. + * @param len: length of rd buffer on input, used length on output. + * @return 0 on success, error on failure. + */ +int gldns_str2wire_tag_buf(const char* str, uint8_t* rd, size_t* len); + +/** + * Convert rdf of type GLDNS_RDF_TYPE_LONG_STR from string to wireformat. + * @param str: the text to convert for this rdata element. + * @param rd: rdata buffer for the wireformat. + * @param len: length of rd buffer on input, used length on output. + * @return 0 on success, error on failure. + */ +int gldns_str2wire_long_str_buf(const char* str, uint8_t* rd, size_t* len); + +/** + * Convert rdf of type GLDNS_RDF_TYPE_HIP from string to wireformat. + * @param str: the text to convert for this rdata element. + * @param rd: rdata buffer for the wireformat. + * @param len: length of rd buffer on input, used length on output. + * @return 0 on success, error on failure. + */ +int gldns_str2wire_hip_buf(const char* str, uint8_t* rd, size_t* len); + +/** + * Convert rdf of type GLDNS_RDF_TYPE_INT16_DATA from string to wireformat. + * @param str: the text to convert for this rdata element. + * @param rd: rdata buffer for the wireformat. + * @param len: length of rd buffer on input, used length on output. + * @return 0 on success, error on failure. + */ +int gldns_str2wire_int16_data_buf(const char* str, uint8_t* rd, size_t* len); + +#ifdef __cplusplus +} +#endif + +#endif /* GLDNS_STR2WIRE_H */ diff --git a/src/gldns/wire2str.c b/src/gldns/wire2str.c new file mode 100644 index 00000000..ca1381a2 --- /dev/null +++ b/src/gldns/wire2str.c @@ -0,0 +1,1967 @@ +/* + * wire2str.c + * + * conversion routines from the wire format + * to the presentation format (strings) + * + * (c) NLnet Labs, 2004-2006 + * + * See the file LICENSE for the license + */ +/** + * \file + * + * Contains functions to translate the wireformat to text + * representation, as well as functions to print them. + */ +#include "config.h" +#include "gldns/wire2str.h" +#include "gldns/str2wire.h" +#include "gldns/rrdef.h" +#include "gldns/pkthdr.h" +#include "gldns/parseutil.h" +#include "gldns/sbuffer.h" +#include "gldns/keyraw.h" +#ifdef HAVE_TIME_H +#include +#endif +#include +#include +#include +#ifdef HAVE_NETDB_H +#include +#endif + +/* lookup tables for standard DNS stuff */ +/* Taken from RFC 2535, section 7. */ +static gldns_lookup_table gldns_algorithms_data[] = { + { GLDNS_RSAMD5, "RSAMD5" }, + { GLDNS_DH, "DH" }, + { GLDNS_DSA, "DSA" }, + { GLDNS_ECC, "ECC" }, + { GLDNS_RSASHA1, "RSASHA1" }, + { GLDNS_DSA_NSEC3, "DSA-NSEC3-SHA1" }, + { GLDNS_RSASHA1_NSEC3, "RSASHA1-NSEC3-SHA1" }, + { GLDNS_RSASHA256, "RSASHA256"}, + { GLDNS_RSASHA512, "RSASHA512"}, + { GLDNS_ECC_GOST, "ECC-GOST"}, + { GLDNS_ECDSAP256SHA256, "ECDSAP256SHA256"}, + { GLDNS_ECDSAP384SHA384, "ECDSAP384SHA384"}, + { GLDNS_INDIRECT, "INDIRECT" }, + { GLDNS_PRIVATEDNS, "PRIVATEDNS" }, + { GLDNS_PRIVATEOID, "PRIVATEOID" }, + { 0, NULL } +}; +gldns_lookup_table* gldns_algorithms = gldns_algorithms_data; + +/* hash algorithms in DS record */ +static gldns_lookup_table gldns_hashes_data[] = { + { GLDNS_SHA1, "SHA1" }, + { GLDNS_SHA256, "SHA256" }, + { GLDNS_HASH_GOST, "HASH-GOST" }, + { GLDNS_SHA384, "SHA384" }, + { 0, NULL } +}; +gldns_lookup_table* gldns_hashes = gldns_hashes_data; + +/* Taken from RFC 4398 */ +static gldns_lookup_table gldns_cert_algorithms_data[] = { + { GLDNS_CERT_PKIX, "PKIX" }, + { GLDNS_CERT_SPKI, "SPKI" }, + { GLDNS_CERT_PGP, "PGP" }, + { GLDNS_CERT_IPKIX, "IPKIX" }, + { GLDNS_CERT_ISPKI, "ISPKI" }, + { GLDNS_CERT_IPGP, "IPGP" }, + { GLDNS_CERT_ACPKIX, "ACPKIX" }, + { GLDNS_CERT_IACPKIX, "IACPKIX" }, + { GLDNS_CERT_URI, "URI" }, + { GLDNS_CERT_OID, "OID" }, + { 0, NULL } +}; +gldns_lookup_table* gldns_cert_algorithms = gldns_cert_algorithms_data; + +/* if these are used elsewhere */ +static gldns_lookup_table gldns_rcodes_data[] = { + { GLDNS_RCODE_NOERROR, "NOERROR" }, + { GLDNS_RCODE_FORMERR, "FORMERR" }, + { GLDNS_RCODE_SERVFAIL, "SERVFAIL" }, + { GLDNS_RCODE_NXDOMAIN, "NXDOMAIN" }, + { GLDNS_RCODE_NOTIMPL, "NOTIMPL" }, + { GLDNS_RCODE_REFUSED, "REFUSED" }, + { GLDNS_RCODE_YXDOMAIN, "YXDOMAIN" }, + { GLDNS_RCODE_YXRRSET, "YXRRSET" }, + { GLDNS_RCODE_NXRRSET, "NXRRSET" }, + { GLDNS_RCODE_NOTAUTH, "NOTAUTH" }, + { GLDNS_RCODE_NOTZONE, "NOTZONE" }, + { 0, NULL } +}; +gldns_lookup_table* gldns_rcodes = gldns_rcodes_data; + +static gldns_lookup_table gldns_opcodes_data[] = { + { GLDNS_PACKET_QUERY, "QUERY" }, + { GLDNS_PACKET_IQUERY, "IQUERY" }, + { GLDNS_PACKET_STATUS, "STATUS" }, + { GLDNS_PACKET_NOTIFY, "NOTIFY" }, + { GLDNS_PACKET_UPDATE, "UPDATE" }, + { 0, NULL } +}; +gldns_lookup_table* gldns_opcodes = gldns_opcodes_data; + +static gldns_lookup_table gldns_wireparse_errors_data[] = { + { GLDNS_WIREPARSE_ERR_OK, "no parse error" }, + { GLDNS_WIREPARSE_ERR_GENERAL, "parse error" }, + { GLDNS_WIREPARSE_ERR_DOMAINNAME_OVERFLOW, "Domainname length overflow" }, + { GLDNS_WIREPARSE_ERR_DOMAINNAME_UNDERFLOW, "Domainname length underflow (zero length)" }, + { GLDNS_WIREPARSE_ERR_BUFFER_TOO_SMALL, "buffer too small" }, + { GLDNS_WIREPARSE_ERR_LABEL_OVERFLOW, "Label length overflow" }, + { GLDNS_WIREPARSE_ERR_EMPTY_LABEL, "Empty label" }, + { GLDNS_WIREPARSE_ERR_SYNTAX_BAD_ESCAPE, "Syntax error, bad escape sequence" }, + { GLDNS_WIREPARSE_ERR_SYNTAX, "Syntax error, could not parse the RR" }, + { GLDNS_WIREPARSE_ERR_SYNTAX_TTL, "Syntax error, could not parse the RR's TTL" }, + { GLDNS_WIREPARSE_ERR_SYNTAX_TYPE, "Syntax error, could not parse the RR's type" }, + { GLDNS_WIREPARSE_ERR_SYNTAX_CLASS, "Syntax error, could not parse the RR's class" }, + { GLDNS_WIREPARSE_ERR_SYNTAX_RDATA, "Syntax error, could not parse the RR's rdata" }, + { GLDNS_WIREPARSE_ERR_SYNTAX_MISSING_VALUE, "Syntax error, value expected" }, + { GLDNS_WIREPARSE_ERR_INVALID_STR, "Conversion error, string expected" }, + { GLDNS_WIREPARSE_ERR_SYNTAX_B64, "Conversion error, b64 encoding expected" }, + { GLDNS_WIREPARSE_ERR_SYNTAX_B32_EXT, "Conversion error, b32 ext encoding expected" }, + { GLDNS_WIREPARSE_ERR_SYNTAX_HEX, "Conversion error, hex encoding expected" }, + { GLDNS_WIREPARSE_ERR_CERT_BAD_ALGORITHM, "Bad algorithm type for CERT record" }, + { GLDNS_WIREPARSE_ERR_SYNTAX_TIME, "Conversion error, time encoding expected" }, + { GLDNS_WIREPARSE_ERR_SYNTAX_PERIOD, "Conversion error, time period encoding expected" }, + { GLDNS_WIREPARSE_ERR_SYNTAX_ILNP64, "Conversion error, 4 colon separated hex numbers expected" }, + { GLDNS_WIREPARSE_ERR_SYNTAX_EUI48, + "Conversion error, 6 two character hex numbers " + "separated by dashes expected (i.e. xx-xx-xx-xx-xx-xx" }, + { GLDNS_WIREPARSE_ERR_SYNTAX_EUI64, + "Conversion error, 8 two character hex numbers " + "separated by dashes expected (i.e. xx-xx-xx-xx-xx-xx-xx-xx" }, + { GLDNS_WIREPARSE_ERR_SYNTAX_TAG, + "Conversion error, a non-zero sequence of US-ASCII letters " + "and numbers in lower case expected" }, + { GLDNS_WIREPARSE_ERR_NOT_IMPL, "not implemented" }, + { GLDNS_WIREPARSE_ERR_SYNTAX_INT, "Conversion error, integer expected" }, + { GLDNS_WIREPARSE_ERR_SYNTAX_IP4, "Conversion error, ip4 addr expected" }, + { GLDNS_WIREPARSE_ERR_SYNTAX_IP6, "Conversion error, ip6 addr expected" }, + { GLDNS_WIREPARSE_ERR_SYNTAX_INTEGER_OVERFLOW, "Syntax error, integer overflow" }, + { GLDNS_WIREPARSE_ERR_INCLUDE, "$INCLUDE directive was seen in the zone" }, + { GLDNS_WIREPARSE_ERR_PARENTHESIS, "Parse error, parenthesis mismatch" }, + { 0, NULL } +}; +gldns_lookup_table* gldns_wireparse_errors = gldns_wireparse_errors_data; + +static gldns_lookup_table gldns_edns_flags_data[] = { + { 3600, "do"}, + { 0, NULL} +}; +gldns_lookup_table* gldns_edns_flags = gldns_edns_flags_data; + +static gldns_lookup_table gldns_edns_options_data[] = { + { 1, "LLQ" }, + { 2, "UL" }, + { 3, "NSID" }, + /* 4 draft-cheshire-edns0-owner-option */ + { 5, "DAU" }, + { 6, "DHU" }, + { 7, "N3U" }, + { 8, "edns-client-subnet" }, + { 0, NULL} +}; +gldns_lookup_table* gldns_edns_options = gldns_edns_options_data; + +char* gldns_wire2str_pkt(uint8_t* data, size_t len) +{ + size_t slen = (size_t)gldns_wire2str_pkt_buf(data, len, NULL, 0); + char* result = (char*)malloc(slen+1); + if(!result) return NULL; + gldns_wire2str_pkt_buf(data, len, result, slen+1); + return result; +} + +char* gldns_wire2str_rr(uint8_t* rr, size_t len) +{ + size_t slen = (size_t)gldns_wire2str_rr_buf(rr, len, NULL, 0); + char* result = (char*)malloc(slen+1); + if(!result) return NULL; + gldns_wire2str_rr_buf(rr, len, result, slen+1); + return result; +} + +char* gldns_wire2str_type(uint16_t rrtype) +{ + char buf[16]; + gldns_wire2str_type_buf(rrtype, buf, sizeof(buf)); + return strdup(buf); +} + +char* gldns_wire2str_class(uint16_t rrclass) +{ + char buf[16]; + gldns_wire2str_class_buf(rrclass, buf, sizeof(buf)); + return strdup(buf); +} + +char* gldns_wire2str_dname(uint8_t* dname, size_t dname_len) +{ + size_t slen=(size_t)gldns_wire2str_dname_buf(dname, dname_len, NULL, 0); + char* result = (char*)malloc(slen+1); + if(!result) return NULL; + gldns_wire2str_dname_buf(dname, dname_len, result, slen+1); + return result; +} + +char* gldns_wire2str_rcode(int rcode) +{ + char buf[16]; + gldns_wire2str_rcode_buf(rcode, buf, sizeof(buf)); + return strdup(buf); +} + +int gldns_wire2str_pkt_buf(uint8_t* d, size_t dlen, char* s, size_t slen) +{ + /* use arguments as temporary variables */ + return gldns_wire2str_pkt_scan(&d, &dlen, &s, &slen); +} + +int gldns_wire2str_rr_buf(uint8_t* d, size_t dlen, char* s, size_t slen) +{ + /* use arguments as temporary variables */ + return gldns_wire2str_rr_scan(&d, &dlen, &s, &slen, NULL, 0); +} + +int gldns_wire2str_rdata_buf(uint8_t* rdata, size_t rdata_len, char* str, + size_t str_len, uint16_t rrtype) +{ + /* use arguments as temporary variables */ + return gldns_wire2str_rdata_scan(&rdata, &rdata_len, &str, &str_len, + rrtype, NULL, 0); +} + +int gldns_wire2str_rr_unknown_buf(uint8_t* d, size_t dlen, char* s, size_t slen) +{ + /* use arguments as temporary variables */ + return gldns_wire2str_rr_unknown_scan(&d, &dlen, &s, &slen, NULL, 0); +} + +int gldns_wire2str_rr_comment_buf(uint8_t* rr, size_t rrlen, size_t dname_len, + char* s, size_t slen) +{ + uint16_t rrtype = gldns_wirerr_get_type(rr, rrlen, dname_len); + return gldns_wire2str_rr_comment_print(&s, &slen, rr, rrlen, dname_len, + rrtype); +} + +int gldns_wire2str_type_buf(uint16_t rrtype, char* s, size_t slen) +{ + /* use arguments as temporary variables */ + return gldns_wire2str_type_print(&s, &slen, rrtype); +} + +int gldns_wire2str_class_buf(uint16_t rrclass, char* s, size_t slen) +{ + /* use arguments as temporary variables */ + return gldns_wire2str_class_print(&s, &slen, rrclass); +} + +int gldns_wire2str_rcode_buf(int rcode, char* s, size_t slen) +{ + /* use arguments as temporary variables */ + return gldns_wire2str_rcode_print(&s, &slen, rcode); +} + +int gldns_wire2str_dname_buf(uint8_t* d, size_t dlen, char* s, size_t slen) +{ + /* use arguments as temporary variables */ + return gldns_wire2str_dname_scan(&d, &dlen, &s, &slen, NULL, 0); +} + +int gldns_str_vprint(char** str, size_t* slen, const char* format, va_list args) +{ + int w = vsnprintf(*str, *slen, format, args); + if(w < 0) { + /* error in printout */ + return 0; + } else if((size_t)w >= *slen) { + *str = NULL; /* we do not want str to point outside of buffer*/ + *slen = 0; + } else { + *str += w; + *slen -= w; + } + return w; +} + +int gldns_str_print(char** str, size_t* slen, const char* format, ...) +{ + int w; + va_list args; + va_start(args, format); + w = gldns_str_vprint(str, slen, format, args); + va_end(args); + return w; +} + +/** print hex format into text buffer for specified length */ +static int print_hex_buf(char** s, size_t* slen, uint8_t* buf, size_t len) +{ + const char* hex = "0123456789ABCDEF"; + size_t i; + for(i=0; i>4], + hex[buf[i]&0x0f]); + } + return (int)len*2; +} + +/** print remainder of buffer in hex format with prefixed text */ +static int print_remainder_hex(const char* pref, uint8_t** d, size_t* dlen, + char** s, size_t* slen) +{ + int w = 0; + w += gldns_str_print(s, slen, "%s", pref); + w += print_hex_buf(s, slen, *d, *dlen); + *d += *dlen; + *dlen = 0; + return w; +} + +int gldns_wire2str_pkt_scan(uint8_t** d, size_t* dlen, char** s, size_t* slen) +{ + int w = 0; + unsigned qdcount, ancount, nscount, arcount, i; + uint8_t* pkt = *d; + size_t pktlen = *dlen; + if(*dlen >= GLDNS_HEADER_SIZE) { + qdcount = (unsigned)GLDNS_QDCOUNT(*d); + ancount = (unsigned)GLDNS_ANCOUNT(*d); + nscount = (unsigned)GLDNS_NSCOUNT(*d); + arcount = (unsigned)GLDNS_ARCOUNT(*d); + } else { + qdcount = ancount = nscount = arcount = 0; + } + w += gldns_wire2str_header_scan(d, dlen, s, slen); + w += gldns_str_print(s, slen, "\n"); + w += gldns_str_print(s, slen, ";; QUESTION SECTION:\n"); + for(i=0; i 0) { + w += print_remainder_hex(";; trailing garbage 0x", + d, dlen, s, slen); + w += gldns_str_print(s, slen, "\n"); + } + return w; +} + +/** scan type, class and ttl and printout, for rr */ +static int gldns_rr_tcttl_scan(uint8_t** d, size_t* dl, char** s, size_t* sl) +{ + int w = 0; + uint16_t t, c; + uint32_t ttl; + if(*dl < 8) { + if(*dl < 4) + return w + print_remainder_hex("; Error malformed 0x", + d, dl, s, sl); + /* these print values or 0x.. if none left */ + t = gldns_read_uint16(*d); + c = gldns_read_uint16((*d)+2); + (*d)+=4; + (*dl)-=4; + w += gldns_wire2str_class_print(s, sl, c); + w += gldns_str_print(s, sl, "\t"); + w += gldns_wire2str_type_print(s, sl, t); + if(*dl == 0) + return w + gldns_str_print(s, sl, "; Error no ttl"); + return w + print_remainder_hex( + "; Error malformed ttl 0x", d, dl, s, sl); + } + t = gldns_read_uint16(*d); + c = gldns_read_uint16((*d)+2); + ttl = gldns_read_uint32((*d)+4); + (*d)+=8; + (*dl)-=8; + w += gldns_str_print(s, sl, "%lu\t", (unsigned long)ttl); + w += gldns_wire2str_class_print(s, sl, c); + w += gldns_str_print(s, sl, "\t"); + w += gldns_wire2str_type_print(s, sl, t); + return w; +} + +int gldns_wire2str_rr_scan(uint8_t** d, size_t* dlen, char** s, size_t* slen, + uint8_t* pkt, size_t pktlen) +{ + int w = 0; + uint8_t* rr = *d; + size_t rrlen = *dlen, dname_off, rdlen, ordlen; + uint16_t rrtype = 0; + + if(*dlen >= 3 && (*d)[0]==0 && + gldns_read_uint16((*d)+1)==GLDNS_RR_TYPE_OPT) { + /* perform EDNS OPT processing */ + return gldns_wire2str_edns_scan(d, dlen, s, slen, pkt, pktlen); + } + + /* try to scan the rdata with pretty-printing, but if that fails, then + * scan the rdata as an unknown RR type */ + w += gldns_wire2str_dname_scan(d, dlen, s, slen, pkt, pktlen); + w += gldns_str_print(s, slen, "\t"); + dname_off = rrlen-(*dlen); + if(*dlen == 4) { + /* like a question-RR */ + uint16_t t = gldns_read_uint16(*d); + uint16_t c = gldns_read_uint16((*d)+2); + (*d)+=4; + (*dlen)-=4; + w += gldns_wire2str_class_print(s, slen, c); + w += gldns_str_print(s, slen, "\t"); + w += gldns_wire2str_type_print(s, slen, t); + w += gldns_str_print(s, slen, " ; Error no ttl,rdata\n"); + return w; + } + if(*dlen < 8) { + if(*dlen == 0) + return w + gldns_str_print(s, slen, ";Error missing RR\n"); + w += print_remainder_hex(";Error partial RR 0x", d, dlen, s, slen); + return w + gldns_str_print(s, slen, "\n"); + } + rrtype = gldns_read_uint16(*d); + w += gldns_rr_tcttl_scan(d, dlen, s, slen); + w += gldns_str_print(s, slen, "\t"); + + /* rdata */ + if(*dlen < 2) { + if(*dlen == 0) + return w + gldns_str_print(s, slen, ";Error missing rdatalen\n"); + w += print_remainder_hex(";Error missing rdatalen 0x", + d, dlen, s, slen); + return w + gldns_str_print(s, slen, "\n"); + } + rdlen = gldns_read_uint16(*d); + ordlen = rdlen; + (*d)+=2; + (*dlen)-=2; + if(*dlen < rdlen) { + w += gldns_str_print(s, slen, "\\# %u ", (unsigned)rdlen); + if(*dlen == 0) + return w + gldns_str_print(s, slen, ";Error missing rdata\n"); + w += print_remainder_hex(";Error partial rdata 0x", d, dlen, s, slen); + return w + gldns_str_print(s, slen, "\n"); + } + w += gldns_wire2str_rdata_scan(d, &rdlen, s, slen, rrtype, pkt, pktlen); + (*dlen) -= (ordlen-rdlen); + + /* default comment */ + w += gldns_wire2str_rr_comment_print(s, slen, rr, rrlen, dname_off, + rrtype); + w += gldns_str_print(s, slen, "\n"); + return w; +} + +int gldns_wire2str_rrquestion_scan(uint8_t** d, size_t* dlen, char** s, + size_t* slen, uint8_t* pkt, size_t pktlen) +{ + int w = 0; + uint16_t t, c; + w += gldns_wire2str_dname_scan(d, dlen, s, slen, pkt, pktlen); + w += gldns_str_print(s, slen, "\t"); + if(*dlen < 4) { + if(*dlen == 0) + return w + gldns_str_print(s, slen, "Error malformed\n"); + w += print_remainder_hex("Error malformed 0x", d, dlen, s, slen); + return w + gldns_str_print(s, slen, "\n"); + } + t = gldns_read_uint16(*d); + c = gldns_read_uint16((*d)+2); + (*d)+=4; + (*dlen)-=4; + w += gldns_wire2str_class_print(s, slen, c); + w += gldns_str_print(s, slen, "\t"); + w += gldns_wire2str_type_print(s, slen, t); + w += gldns_str_print(s, slen, "\n"); + return w; +} + +int gldns_wire2str_rr_unknown_scan(uint8_t** d, size_t* dlen, char** s, + size_t* slen, uint8_t* pkt, size_t pktlen) +{ + size_t rdlen, ordlen; + int w = 0; + w += gldns_wire2str_dname_scan(d, dlen, s, slen, pkt, pktlen); + w += gldns_str_print(s, slen, "\t"); + w += gldns_rr_tcttl_scan(d, dlen, s, slen); + w += gldns_str_print(s, slen, "\t"); + if(*dlen < 2) { + if(*dlen == 0) + return w + gldns_str_print(s, slen, ";Error missing rdatalen\n"); + w += print_remainder_hex(";Error missing rdatalen 0x", + d, dlen, s, slen); + return w + gldns_str_print(s, slen, "\n"); + } + rdlen = gldns_read_uint16(*d); + ordlen = rdlen; + (*d) += 2; + (*dlen) -= 2; + if(*dlen < rdlen) { + w += gldns_str_print(s, slen, "\\# %u ", (unsigned)rdlen); + if(*dlen == 0) + return w + gldns_str_print(s, slen, ";Error missing rdata\n"); + w += print_remainder_hex(";Error partial rdata 0x", d, dlen, s, slen); + return w + gldns_str_print(s, slen, "\n"); + } + w += gldns_wire2str_rdata_unknown_scan(d, &rdlen, s, slen); + (*dlen) -= (ordlen-rdlen); + w += gldns_str_print(s, slen, "\n"); + return w; +} + +/** print rr comment for type DNSKEY */ +static int rr_comment_dnskey(char** s, size_t* slen, uint8_t* rr, + size_t rrlen, size_t dname_off) +{ + size_t rdlen; + uint8_t* rdata; + int flags, w = 0; + if(rrlen < dname_off + 10) return 0; + rdlen = gldns_read_uint16(rr+dname_off+8); + if(rrlen < dname_off + 10 + rdlen) return 0; + rdata = rr + dname_off + 10; + flags = (int)gldns_read_uint16(rdata); + w += gldns_str_print(s, slen, " ;{"); + + /* id */ + w += gldns_str_print(s, slen, "id = %u", + gldns_calc_keytag_raw(rdata, rdlen)); + + /* flags */ + if((flags&GLDNS_KEY_ZONE_KEY)) { + if((flags&GLDNS_KEY_SEP_KEY)) + w += gldns_str_print(s, slen, " (ksk)"); + else w += gldns_str_print(s, slen, " (zsk)"); + } + + /* keysize */ + if(rdlen > 4) { + w += gldns_str_print(s, slen, ", "); + w += gldns_str_print(s, slen, "size = %db", + (int)gldns_rr_dnskey_key_size_raw( + (unsigned char*)rdata+4, rdlen-4, (int)(rdata[3]))); + } + + w += gldns_str_print(s, slen, "}"); + return w; +} + +/** print rr comment for type RRSIG */ +static int rr_comment_rrsig(char** s, size_t* slen, uint8_t* rr, + size_t rrlen, size_t dname_off) +{ + size_t rdlen; + uint8_t* rdata; + if(rrlen < dname_off + 10) return 0; + rdlen = gldns_read_uint16(rr+dname_off+8); + if(rrlen < dname_off + 10 + rdlen) return 0; + rdata = rr + dname_off + 10; + if(rdlen < 18) return 0; + return gldns_str_print(s, slen, " ;{id = %d}", + (int)gldns_read_uint16(rdata+16)); +} + +/** print rr comment for type NSEC3 */ +static int rr_comment_nsec3(char** s, size_t* slen, uint8_t* rr, + size_t rrlen, size_t dname_off) +{ + size_t rdlen; + uint8_t* rdata; + int w = 0; + if(rrlen < dname_off + 10) return 0; + rdlen = gldns_read_uint16(rr+dname_off+8); + if(rrlen < dname_off + 10 + rdlen) return 0; + rdata = rr + dname_off + 10; + if(rdlen < 2) return 0; + if((rdata[1] & GLDNS_NSEC3_VARS_OPTOUT_MASK)) + w += gldns_str_print(s, slen, " ;{flags: optout}"); + return w; +} + +int gldns_wire2str_rr_comment_print(char** s, size_t* slen, uint8_t* rr, + size_t rrlen, size_t dname_off, uint16_t rrtype) +{ + if(rrtype == GLDNS_RR_TYPE_DNSKEY) { + return rr_comment_dnskey(s, slen, rr, rrlen, dname_off); + } else if(rrtype == GLDNS_RR_TYPE_RRSIG) { + return rr_comment_rrsig(s, slen, rr, rrlen, dname_off); + } else if(rrtype == GLDNS_RR_TYPE_NSEC3) { + return rr_comment_nsec3(s, slen, rr, rrlen, dname_off); + } + return 0; +} + +int gldns_wire2str_header_scan(uint8_t** d, size_t* dlen, char** s, + size_t* slen) +{ + int w = 0; + int opcode, rcode; + w += gldns_str_print(s, slen, ";; ->>HEADER<<- "); + if(*dlen == 0) + return w+gldns_str_print(s, slen, "Error empty packet"); + if(*dlen < 4) + return w+print_remainder_hex("Error header too short 0x", d, dlen, s, slen); + opcode = (int)GLDNS_OPCODE_WIRE(*d); + rcode = (int)GLDNS_RCODE_WIRE(*d); + w += gldns_str_print(s, slen, "opcode: "); + w += gldns_wire2str_opcode_print(s, slen, opcode); + w += gldns_str_print(s, slen, ", "); + w += gldns_str_print(s, slen, "rcode: "); + w += gldns_wire2str_rcode_print(s, slen, rcode); + w += gldns_str_print(s, slen, ", "); + w += gldns_str_print(s, slen, "id: %d\n", (int)GLDNS_ID_WIRE(*d)); + w += gldns_str_print(s, slen, ";; flags:"); + if(GLDNS_QR_WIRE(*d)) w += gldns_str_print(s, slen, " qr"); + if(GLDNS_AA_WIRE(*d)) w += gldns_str_print(s, slen, " aa"); + if(GLDNS_TC_WIRE(*d)) w += gldns_str_print(s, slen, " tc"); + if(GLDNS_RD_WIRE(*d)) w += gldns_str_print(s, slen, " rd"); + if(GLDNS_CD_WIRE(*d)) w += gldns_str_print(s, slen, " cd"); + if(GLDNS_RA_WIRE(*d)) w += gldns_str_print(s, slen, " ra"); + if(GLDNS_AD_WIRE(*d)) w += gldns_str_print(s, slen, " ad"); + if(GLDNS_Z_WIRE(*d)) w += gldns_str_print(s, slen, " z"); + w += gldns_str_print(s, slen, " ; "); + if(*dlen < GLDNS_HEADER_SIZE) + return w+print_remainder_hex("Error header too short 0x", d, dlen, s, slen); + w += gldns_str_print(s, slen, "QUERY: %d, ", (int)GLDNS_QDCOUNT(*d)); + w += gldns_str_print(s, slen, "ANSWER: %d, ", (int)GLDNS_ANCOUNT(*d)); + w += gldns_str_print(s, slen, "AUTHORITY: %d, ", (int)GLDNS_NSCOUNT(*d)); + w += gldns_str_print(s, slen, "ADDITIONAL: %d ", (int)GLDNS_ARCOUNT(*d)); + *d += GLDNS_HEADER_SIZE; + *dlen -= GLDNS_HEADER_SIZE; + return w; +} + +int gldns_wire2str_rdata_scan(uint8_t** d, size_t* dlen, char** s, + size_t* slen, uint16_t rrtype, uint8_t* pkt, size_t pktlen) +{ + /* try to prettyprint, but if that fails, use unknown format */ + uint8_t* origd = *d; + char* origs = *s; + size_t origdlen = *dlen, origslen = *slen; + uint16_t r_cnt, r_max; + gldns_rdf_type rdftype; + int w = 0, n; + + const gldns_rr_descriptor *desc = gldns_rr_descript(rrtype); + if(!desc) /* unknown format */ + return gldns_wire2str_rdata_unknown_scan(d, dlen, s, slen); + /* dlen equals the rdatalen for the rdata */ + + r_max = gldns_rr_descriptor_maximum(desc); + for(r_cnt=0; r_cnt < r_max; r_cnt++) { + if(*dlen == 0) { + if(r_cnt < gldns_rr_descriptor_minimum(desc)) + goto failed; + break; /* nothing more to print */ + } + rdftype = gldns_rr_descriptor_field_type(desc, r_cnt); + if(r_cnt != 0) + w += gldns_str_print(s, slen, " "); + n = gldns_wire2str_rdf_scan(d, dlen, s, slen, rdftype, + pkt, pktlen); + if(n == -1) { + failed: + /* failed, use unknown format */ + *d = origd; *s = origs; + *dlen = origdlen; *slen = origslen; + return gldns_wire2str_rdata_unknown_scan(d, dlen, + s, slen); + } + w += n; + } + return w; +} + +int gldns_wire2str_rdata_unknown_scan(uint8_t** d, size_t* dlen, char** s, + size_t* slen) +{ + int w = 0; + + /* print length */ + w += gldns_str_print(s, slen, "\\# %u", (unsigned)*dlen); + + /* print rdlen in hex */ + if(*dlen != 0) + w += gldns_str_print(s, slen, " "); + w += print_hex_buf(s, slen, *d, *dlen); + (*d) += *dlen; + (*dlen) = 0; + return w; +} + +/** print and escape one character for a domain dname */ +static int dname_char_print(char** s, size_t* slen, uint8_t c) +{ + if(c == '.' || c == ';' || c == '(' || c == ')' || c == '\\') + return gldns_str_print(s, slen, "\\%c", c); + else if(!(isascii((int)c) && isgraph((int)c))) + return gldns_str_print(s, slen, "\\%03u", (unsigned)c); + /* plain printout */ + if(*slen) { + **s = (char)c; + (*s)++; + (*slen)--; + } + return 1; +} + +int gldns_wire2str_dname_scan(uint8_t** d, size_t* dlen, char** s, size_t* slen, + uint8_t* pkt, size_t pktlen) +{ + int w = 0; + /* spool labels onto the string, use compression if its there */ + uint8_t* pos = *d; + unsigned i, counter=0; + const unsigned maxcompr = 1000; /* loop detection, max compr ptrs */ + int in_buf = 1; + if(*dlen == 0) return gldns_str_print(s, slen, "ErrorMissingDname"); + if(*pos == 0) { + (*d)++; + (*dlen)--; + return gldns_str_print(s, slen, "."); + } + while(*pos) { + /* read label length */ + uint8_t labellen = *pos++; + if(in_buf) { (*d)++; (*dlen)--; } + + /* find out what sort of label we have */ + if((labellen&0xc0) == 0xc0) { + /* compressed */ + uint16_t target = 0; + if(in_buf && *dlen == 0) + return w + gldns_str_print(s, slen, + "ErrorPartialDname"); + else if(!in_buf && pos+1 > pkt+pktlen) + return w + gldns_str_print(s, slen, + "ErrorPartialDname"); + target = ((labellen&0x3f)<<8) | *pos; + if(in_buf) { (*d)++; (*dlen)--; } + /* move to target, if possible */ + if(!pkt || target >= pktlen) + return w + gldns_str_print(s, slen, + "ErrorComprPtrOutOfBounds"); + if(counter++ > maxcompr) + return w + gldns_str_print(s, slen, + "ErrorComprPtrLooped"); + in_buf = 0; + pos = pkt+target; + continue; + } else if((labellen&0xc0)) { + /* notimpl label type */ + w += gldns_str_print(s, slen, + "ErrorLABELTYPE%xIsUnknown", + (int)(labellen&0xc0)); + return w; + } + + /* spool label characters, end with '.' */ + if(in_buf && *dlen < labellen) labellen = *dlen; + else if(!in_buf && pos+labellen > pkt+pktlen) + labellen = (uint8_t)(pkt + pktlen - pos); + for(i=0; i<(unsigned)labellen; i++) { + w += dname_char_print(s, slen, *pos++); + } + if(in_buf) { + (*d) += labellen; + (*dlen) -= labellen; + if(*dlen == 0) break; + } + w += gldns_str_print(s, slen, "."); + } + /* skip over final root label */ + if(in_buf && *dlen > 0) { (*d)++; (*dlen)--; } + /* in case we printed no labels, terminate dname */ + if(w == 0) w += gldns_str_print(s, slen, "."); + return w; +} + +int gldns_wire2str_opcode_print(char** s, size_t* slen, int opcode) +{ + gldns_lookup_table *lt = gldns_lookup_by_id(gldns_opcodes, opcode); + if (lt && lt->name) { + return gldns_str_print(s, slen, "%s", lt->name); + } + return gldns_str_print(s, slen, "OPCODE%u", (unsigned)opcode); +} + +int gldns_wire2str_rcode_print(char** s, size_t* slen, int rcode) +{ + gldns_lookup_table *lt = gldns_lookup_by_id(gldns_rcodes, rcode); + if (lt && lt->name) { + return gldns_str_print(s, slen, "%s", lt->name); + } + return gldns_str_print(s, slen, "RCODE%u", (unsigned)rcode); +} + +int gldns_wire2str_class_print(char** s, size_t* slen, uint16_t rrclass) +{ + gldns_lookup_table *lt = gldns_lookup_by_id(gldns_rr_classes, + (int)rrclass); + if (lt && lt->name) { + return gldns_str_print(s, slen, "%s", lt->name); + } + return gldns_str_print(s, slen, "CLASS%u", (unsigned)rrclass); +} + +int gldns_wire2str_type_print(char** s, size_t* slen, uint16_t rrtype) +{ + const gldns_rr_descriptor *descriptor = gldns_rr_descript(rrtype); + if (descriptor && descriptor->_name) { + return gldns_str_print(s, slen, "%s", descriptor->_name); + } + return gldns_str_print(s, slen, "TYPE%u", (unsigned)rrtype); +} + +int gldns_wire2str_edns_option_code_print(char** s, size_t* slen, + uint16_t opcode) +{ + gldns_lookup_table *lt = gldns_lookup_by_id(gldns_edns_options, + (int)opcode); + if (lt && lt->name) { + return gldns_str_print(s, slen, "%s", lt->name); + } + return gldns_str_print(s, slen, "OPT%u", (unsigned)opcode); +} + +int gldns_wire2str_class_scan(uint8_t** d, size_t* dlen, char** s, size_t* slen) +{ + uint16_t c; + if(*dlen == 0) return 0; + if(*dlen < 2) return print_remainder_hex("Error malformed 0x", d, dlen, s, slen); + c = gldns_read_uint16(*d); + (*d)+=2; + (*dlen)-=2; + return gldns_wire2str_class_print(s, slen, c); +} + +int gldns_wire2str_type_scan(uint8_t** d, size_t* dlen, char** s, size_t* slen) +{ + uint16_t t; + if(*dlen == 0) return 0; + if(*dlen < 2) return print_remainder_hex("Error malformed 0x", d, dlen, s, slen); + t = gldns_read_uint16(*d); + (*d)+=2; + (*dlen)-=2; + return gldns_wire2str_type_print(s, slen, t); +} + +int gldns_wire2str_ttl_scan(uint8_t** d, size_t* dlen, char** s, size_t* slen) +{ + uint32_t ttl; + if(*dlen == 0) return 0; + if(*dlen < 4) return print_remainder_hex("Error malformed 0x", d, dlen, s, slen); + ttl = gldns_read_uint32(*d); + (*d)+=4; + (*dlen)-=4; + return gldns_str_print(s, slen, "%u", (unsigned)ttl); +} + +int gldns_wire2str_rdf_scan(uint8_t** d, size_t* dlen, char** s, size_t* slen, + int rdftype, uint8_t* pkt, size_t pktlen) +{ + if(*dlen == 0) return 0; + switch(rdftype) { + case GLDNS_RDF_TYPE_NONE: + return 0; + case GLDNS_RDF_TYPE_DNAME: + return gldns_wire2str_dname_scan(d, dlen, s, slen, pkt, pktlen); + case GLDNS_RDF_TYPE_INT8: + return gldns_wire2str_int8_scan(d, dlen, s, slen); + case GLDNS_RDF_TYPE_INT16: + return gldns_wire2str_int16_scan(d, dlen, s, slen); + case GLDNS_RDF_TYPE_INT32: + return gldns_wire2str_int32_scan(d, dlen, s, slen); + case GLDNS_RDF_TYPE_PERIOD: + return gldns_wire2str_period_scan(d, dlen, s, slen); + case GLDNS_RDF_TYPE_TSIGTIME: + return gldns_wire2str_tsigtime_scan(d, dlen, s, slen); + case GLDNS_RDF_TYPE_A: + return gldns_wire2str_a_scan(d, dlen, s, slen); + case GLDNS_RDF_TYPE_AAAA: + return gldns_wire2str_aaaa_scan(d, dlen, s, slen); + case GLDNS_RDF_TYPE_STR: + return gldns_wire2str_str_scan(d, dlen, s, slen); + case GLDNS_RDF_TYPE_APL: + return gldns_wire2str_apl_scan(d, dlen, s, slen); + case GLDNS_RDF_TYPE_B32_EXT: + return gldns_wire2str_b32_ext_scan(d, dlen, s, slen); + case GLDNS_RDF_TYPE_B64: + return gldns_wire2str_b64_scan(d, dlen, s, slen); + case GLDNS_RDF_TYPE_HEX: + return gldns_wire2str_hex_scan(d, dlen, s, slen); + case GLDNS_RDF_TYPE_NSEC: + return gldns_wire2str_nsec_scan(d, dlen, s, slen); + case GLDNS_RDF_TYPE_NSEC3_SALT: + return gldns_wire2str_nsec3_salt_scan(d, dlen, s, slen); + case GLDNS_RDF_TYPE_TYPE: + return gldns_wire2str_type_scan(d, dlen, s, slen); + case GLDNS_RDF_TYPE_CLASS: + return gldns_wire2str_class_scan(d, dlen, s, slen); + case GLDNS_RDF_TYPE_CERT_ALG: + return gldns_wire2str_cert_alg_scan(d, dlen, s, slen); + case GLDNS_RDF_TYPE_ALG: + return gldns_wire2str_alg_scan(d, dlen, s, slen); + case GLDNS_RDF_TYPE_UNKNOWN: + return gldns_wire2str_unknown_scan(d, dlen, s, slen); + case GLDNS_RDF_TYPE_TIME: + return gldns_wire2str_time_scan(d, dlen, s, slen); + case GLDNS_RDF_TYPE_LOC: + return gldns_wire2str_loc_scan(d, dlen, s, slen); + case GLDNS_RDF_TYPE_WKS: + case GLDNS_RDF_TYPE_SERVICE: + return gldns_wire2str_wks_scan(d, dlen, s, slen); + case GLDNS_RDF_TYPE_NSAP: + return gldns_wire2str_nsap_scan(d, dlen, s, slen); + case GLDNS_RDF_TYPE_ATMA: + return gldns_wire2str_atma_scan(d, dlen, s, slen); + case GLDNS_RDF_TYPE_IPSECKEY: + return gldns_wire2str_ipseckey_scan(d, dlen, s, slen, pkt, + pktlen); + case GLDNS_RDF_TYPE_HIP: + return gldns_wire2str_hip_scan(d, dlen, s, slen); + case GLDNS_RDF_TYPE_INT16_DATA: + return gldns_wire2str_int16_data_scan(d, dlen, s, slen); + case GLDNS_RDF_TYPE_NSEC3_NEXT_OWNER: + return gldns_wire2str_b32_ext_scan(d, dlen, s, slen); + case GLDNS_RDF_TYPE_ILNP64: + return gldns_wire2str_ilnp64_scan(d, dlen, s, slen); + case GLDNS_RDF_TYPE_EUI48: + return gldns_wire2str_eui48_scan(d, dlen, s, slen); + case GLDNS_RDF_TYPE_EUI64: + return gldns_wire2str_eui64_scan(d, dlen, s, slen); + case GLDNS_RDF_TYPE_TAG: + return gldns_wire2str_tag_scan(d, dlen, s, slen); + case GLDNS_RDF_TYPE_LONG_STR: + return gldns_wire2str_long_str_scan(d, dlen, s, slen); + } + /* unknown rdf type */ + return -1; +} + +int gldns_wire2str_int8_scan(uint8_t** d, size_t* dl, char** s, size_t* sl) +{ + int w; + if(*dl < 1) return -1; + w = gldns_str_print(s, sl, "%u", (unsigned)**d); + (*d)++; + (*dl)--; + return w; +} + +int gldns_wire2str_int16_scan(uint8_t** d, size_t* dl, char** s, size_t* sl) +{ + int w; + if(*dl < 2) return -1; + w = gldns_str_print(s, sl, "%lu", (unsigned long)gldns_read_uint16(*d)); + (*d)+=2; + (*dl)-=2; + return w; +} + +int gldns_wire2str_int32_scan(uint8_t** d, size_t* dl, char** s, size_t* sl) +{ + int w; + if(*dl < 4) return -1; + w = gldns_str_print(s, sl, "%lu", (unsigned long)gldns_read_uint32(*d)); + (*d)+=4; + (*dl)-=4; + return w; +} + +int gldns_wire2str_period_scan(uint8_t** d, size_t* dl, char** s, size_t* sl) +{ + int w; + if(*dl < 4) return -1; + w = gldns_str_print(s, sl, "%u", (unsigned)gldns_read_uint32(*d)); + (*d)+=4; + (*dl)-=4; + return w; +} + +int gldns_wire2str_tsigtime_scan(uint8_t** d, size_t* dl, char** s, size_t* sl) +{ + /* tsigtime is 48 bits network order unsigned integer */ + int w; + uint64_t tsigtime = 0; + uint64_t d0, d1, d2, d3, d4, d5; + if(*dl < 6) return -1; + d0 = (*d)[0]; /* cast to uint64 for shift operations */ + d1 = (*d)[1]; + d2 = (*d)[2]; + d3 = (*d)[3]; + d4 = (*d)[4]; + d5 = (*d)[5]; + tsigtime = (d0<<40) | (d1<<32) | (d2<<24) | (d3<<16) | (d4<<8) | d5; +#ifndef USE_WINSOCK + w = gldns_str_print(s, sl, "%llu", (long long)tsigtime); +#else + w = gldns_str_print(s, sl, "%I64u", (long long)tsigtime); +#endif + (*d)+=6; + (*dl)-=6; + return w; +} + +int gldns_wire2str_a_scan(uint8_t** d, size_t* dl, char** s, size_t* sl) +{ + char buf[32]; + int w; + if(*dl < 4) return -1; + if(!inet_ntop(AF_INET, *d, buf, (socklen_t)sizeof(buf))) + return -1; + w = gldns_str_print(s, sl, "%s", buf); + (*d)+=4; + (*dl)-=4; + return w; +} + +int gldns_wire2str_aaaa_scan(uint8_t** d, size_t* dl, char** s, size_t* sl) +{ +#ifdef AF_INET6 + char buf[64]; + int w; + if(*dl < 16) return -1; + if(!inet_ntop(AF_INET6, *d, buf, (socklen_t)sizeof(buf))) + return -1; + w = gldns_str_print(s, sl, "%s", buf); + (*d)+=16; + (*dl)-=16; + return w; +#else + return -1; +#endif +} + +/** printout escaped TYPE_STR character */ +static int str_char_print(char** s, size_t* sl, uint8_t c) +{ + if(isprint((int)c) || c == '\t') { + if(c == '\"' || c == '\\') + return gldns_str_print(s, sl, "\\%c", c); + if(*sl) { + **s = (char)c; + (*s)++; + (*sl)--; + } + return 1; + } + return gldns_str_print(s, sl, "\\%03u", (unsigned)c); +} + +int gldns_wire2str_str_scan(uint8_t** d, size_t* dl, char** s, size_t* sl) +{ + int w = 0; + size_t i, len; + if(*dl < 1) return -1; + len = **d; + if(*dl < 1+len) return -1; + (*d)++; + (*dl)--; + w += gldns_str_print(s, sl, "\""); + for(i=0; i 0) + w += gldns_str_print(s, sl, "."); + if(i < (int)adflength) + w += gldns_str_print(s, sl, "%d", (*d)[4+i]); + else w += gldns_str_print(s, sl, "0"); + } + } else if(family == GLDNS_APL_IP6) { + /* check if prefix <128 ? */ + /* address is variable length 0 - 16 */ + for(i=0; i<16; i++) { + if(i%2 == 0 && i>0) + w += gldns_str_print(s, sl, ":"); + if(i < (int)adflength) + w += gldns_str_print(s, sl, "%02x", (*d)[4+i]); + else w += gldns_str_print(s, sl, "00"); + } + } + w += gldns_str_print(s, sl, "/%u", (unsigned)prefix); + (*d) += 4+adflength; + (*dl) -= 4+adflength; + return w; +} + +int gldns_wire2str_b32_ext_scan(uint8_t** d, size_t* dl, char** s, size_t* sl) +{ + size_t datalen; + size_t sz; + if(*dl < 1) return -1; + datalen = (*d)[0]; + if(*dl < 1+datalen) return -1; + sz = gldns_b32_ntop_calculate_size(datalen); + if(*sl < sz+1) { + (*d) += datalen+1; + (*dl) -= (datalen+1); + return (int)sz; /* out of space really, but would need buffer + in order to truncate the output */ + } + gldns_b32_ntop_extended_hex((*d)+1, datalen, *s, *sl); + (*d) += datalen+1; + (*dl) -= (datalen+1); + (*s) += sz; + (*sl) -= sz; + return (int)sz; +} + +/** scan number of bytes from wire into b64 presentation format */ +static int gldns_wire2str_b64_scan_num(uint8_t** d, size_t* dl, char** s, + size_t* sl, size_t num) +{ + /* b64_ntop_calculate size includes null at the end */ + size_t sz = gldns_b64_ntop_calculate_size(num)-1; + if(*sl < sz+1) { + (*d) += num; + (*dl) -= num; + return (int)sz; /* out of space really, but would need buffer + in order to truncate the output */ + } + gldns_b64_ntop(*d, num, *s, *sl); + (*d) += num; + (*dl) -= num; + (*s) += sz; + (*sl) -= sz; + return (int)sz; +} + +int gldns_wire2str_b64_scan(uint8_t** d, size_t* dl, char** s, size_t* sl) +{ + return gldns_wire2str_b64_scan_num(d, dl, s, sl, *dl); +} + +int gldns_wire2str_hex_scan(uint8_t** d, size_t* dl, char** s, size_t* sl) +{ + return print_remainder_hex("", d, dl, s, sl); +} + +int gldns_wire2str_nsec_scan(uint8_t** d, size_t* dl, char** s, size_t* sl) +{ + uint8_t* p = *d; + size_t pl = *dl; + unsigned i, bit, window, block_len; + uint16_t t; + int w = 0; + + /* check for errors */ + while(pl) { + if(pl < 2) return -1; + block_len = (unsigned)p[1]; + if(pl < 2+block_len) return -1; + p += block_len+2; + pl -= block_len+2; + } + + /* do it */ + p = *d; + pl = *dl; + while(pl) { + if(pl < 2) return -1; /* cannot happen */ + window = (unsigned)p[0]; + block_len = (unsigned)p[1]; + if(pl < 2+block_len) return -1; /* cannot happen */ + p += 2; + for(i=0; i>bit))) { + if(w) w += gldns_str_print(s, sl, " "); + w += gldns_wire2str_type_print(s, sl, + t+bit); + } + } + } + p += block_len; + pl -= block_len+2; + } + (*d) += *dl; + (*dl) = 0; + return w; +} + +int gldns_wire2str_nsec3_salt_scan(uint8_t** d, size_t* dl, char** s, size_t* sl) +{ + size_t salt_len; + int w; + if(*dl < 1) return -1; + salt_len = (size_t)(*d)[0]; + if(*dl < 1+salt_len) return -1; + (*d)++; + (*dl)--; + if(salt_len == 0) { + return gldns_str_print(s, sl, "-"); + } + w = print_hex_buf(s, sl, *d, salt_len); + (*dl)-=salt_len; + (*d)+=salt_len; + return w; +} + +int gldns_wire2str_cert_alg_scan(uint8_t** d, size_t* dl, char** s, size_t* sl) +{ + gldns_lookup_table *lt; + int data, w; + if(*dl < 2) return -1; + data = (int)gldns_read_uint16(*d); + lt = gldns_lookup_by_id(gldns_cert_algorithms, data); + if(lt && lt->name) + w = gldns_str_print(s, sl, "%s", lt->name); + else w = gldns_str_print(s, sl, "%d", data); + (*dl)-=2; + (*d)+=2; + return w; +} + +int gldns_wire2str_alg_scan(uint8_t** d, size_t* dl, char** s, size_t* sl) +{ + /* don't use algorithm mnemonics in the presentation format + * this kind of got sneaked into the rfc's */ + return gldns_wire2str_int8_scan(d, dl, s, sl); +} + +int gldns_wire2str_unknown_scan(uint8_t** d, size_t* dl, char** s, size_t* sl) +{ + return gldns_wire2str_rdata_unknown_scan(d, dl, s, sl); +} + +int gldns_wire2str_time_scan(uint8_t** d, size_t* dl, char** s, size_t* sl) +{ + /* create a YYYYMMDDHHMMSS string if possible */ + struct tm tm; + char date_buf[16]; + uint32_t t; + memset(&tm, 0, sizeof(tm)); + if(*dl < 4) return -1; + t = gldns_read_uint32(*d); + date_buf[15]=0; + if(gldns_serial_arithmitics_gmtime_r(t, time(NULL), &tm) && + strftime(date_buf, 15, "%Y%m%d%H%M%S", &tm)) { + (*d) += 4; + (*dl) -= 4; + return gldns_str_print(s, sl, "%s", date_buf); + } + return -1; +} + +static int +loc_cm_print(char** str, size_t* sl, uint8_t mantissa, uint8_t exponent) +{ + int w = 0; + uint8_t i; + /* is it 0. ? */ + if(exponent < 2) { + if(exponent == 1) + mantissa *= 10; + return gldns_str_print(str, sl, "0.%02ld", (long)mantissa); + } + /* always */ + w += gldns_str_print(str, sl, "%d", (int)mantissa); + for(i=0; i equator) { + northerness = 'N'; + latitude = latitude - equator; + } else { + northerness = 'S'; + latitude = equator - latitude; + } + h = latitude / (1000 * 60 * 60); + latitude = latitude % (1000 * 60 * 60); + m = latitude / (1000 * 60); + latitude = latitude % (1000 * 60); + s = (double) latitude / 1000.0; + w += gldns_str_print(str, sl, "%02u %02u %06.3f %c ", + h, m, s, northerness); + + if (longitude > equator) { + easterness = 'E'; + longitude = longitude - equator; + } else { + easterness = 'W'; + longitude = equator - longitude; + } + h = longitude / (1000 * 60 * 60); + longitude = longitude % (1000 * 60 * 60); + m = longitude / (1000 * 60); + longitude = longitude % (1000 * 60); + s = (double) longitude / (1000.0); + w += gldns_str_print(str, sl, "%02u %02u %06.3f %c ", + h, m, s, easterness); + + s = ((double) altitude) / 100; + s -= 100000; + + if(altitude%100 != 0) + w += gldns_str_print(str, sl, "%.2f", s); + else + w += gldns_str_print(str, sl, "%.0f", s); + + w += gldns_str_print(str, sl, "m "); + + w += loc_cm_print(str, sl, (size & 0xf0) >> 4, size & 0x0f); + w += gldns_str_print(str, sl, "m "); + + w += loc_cm_print(str, sl, (horizontal_precision & 0xf0) >> 4, + horizontal_precision & 0x0f); + w += gldns_str_print(str, sl, "m "); + + w += loc_cm_print(str, sl, (vertical_precision & 0xf0) >> 4, + vertical_precision & 0x0f); + w += gldns_str_print(str, sl, "m"); + + (*d)+=16; + (*dl)-=16; + return w; +} + +int gldns_wire2str_wks_scan(uint8_t** d, size_t* dl, char** s, size_t* sl) +{ + /* protocol, followed by bitmap of services */ + const char* proto_name = NULL; + struct protoent *protocol; + struct servent *service; + uint8_t protocol_nr; + int bit, port, w = 0; + size_t i; + /* we cannot print with strings because they + * are not portable, the presentation format may + * not be able to be read in on another computer. */ + int print_symbols = 0; + + /* protocol */ + if(*dl < 1) return -1; + protocol_nr = (*d)[0]; + (*d)++; + (*dl)--; + protocol = getprotobynumber((int)protocol_nr); + if(protocol && (protocol->p_name != NULL)) { + w += gldns_str_print(s, sl, "%s", protocol->p_name); + proto_name = protocol->p_name; + } else { + w += gldns_str_print(s, sl, "%u", (unsigned)protocol_nr); + } + + for(i=0; i<*dl; i++) { + if((*d)[i] == 0) + continue; + for(bit=0; bit<8; bit++) { + if(!(((*d)[i])&(0x80>>bit))) + continue; + port = (int)i*8 + bit; + + if(!print_symbols) + service = NULL; + else + service = getservbyport( + (int)htons((uint16_t)port), proto_name); + if(service && service->s_name) + w += gldns_str_print(s, sl, " %s", + service->s_name); + else w += gldns_str_print(s, sl, " %u", + (unsigned)port); + } + } + +#ifdef HAVE_ENDSERVENT + endservent(); +#endif +#ifdef HAVE_ENDPROTOENT + endprotoent(); +#endif + (*d) += *dl; + (*dl) = 0; + return w; +} + +int gldns_wire2str_nsap_scan(uint8_t** d, size_t* dl, char** s, size_t* sl) +{ + return print_remainder_hex("0x", d, dl, s, sl); +} + +int gldns_wire2str_atma_scan(uint8_t** d, size_t* dl, char** s, size_t* sl) +{ + return print_remainder_hex("", d, dl, s, sl); +} + +/* internal scan routine that can modify arguments on failure */ +static int gldns_wire2str_ipseckey_scan_internal(uint8_t** d, size_t* dl, + char** s, size_t* sl, uint8_t* pkt, size_t pktlen) +{ + /* http://www.ietf.org/internet-drafts/draft-ietf-ipseckey-rr-12.txt*/ + uint8_t precedence, gateway_type, algorithm; + int w = 0; + + if(*dl < 3) return -1; + precedence = (*d)[0]; + gateway_type = (*d)[1]; + algorithm = (*d)[2]; + if(gateway_type > 3) + return -1; /* unknown */ + (*d)+=3; + (*dl)-=3; + w += gldns_str_print(s, sl, "%d %d %d ", + (int)precedence, (int)gateway_type, (int)algorithm); + + switch(gateway_type) { + case 0: /* no gateway */ + w += gldns_str_print(s, sl, "."); + break; + case 1: /* ip4 */ + w += gldns_wire2str_a_scan(d, dl, s, sl); + break; + case 2: /* ip6 */ + w += gldns_wire2str_aaaa_scan(d, dl, s, sl); + break; + case 3: /* dname */ + w += gldns_wire2str_dname_scan(d, dl, s, sl, pkt, pktlen); + break; + default: /* unknown */ + return -1; + } + + if(*dl < 1) + return -1; + w += gldns_str_print(s, sl, " "); + w += gldns_wire2str_b64_scan_num(d, dl, s, sl, *dl); + return w; +} + +int gldns_wire2str_ipseckey_scan(uint8_t** d, size_t* dl, char** s, size_t* sl, + uint8_t* pkt, size_t pktlen) +{ + uint8_t* od = *d; + char* os = *s; + size_t odl = *dl, osl = *sl; + int w=gldns_wire2str_ipseckey_scan_internal(d, dl, s, sl, pkt, pktlen); + if(w == -1) { + *d = od; + *s = os; + *dl = odl; + *sl = osl; + return -1; + } + return w; +} + +int gldns_wire2str_hip_scan(uint8_t** d, size_t* dl, char** s, size_t* sl) +{ + int w; + uint8_t algo, hitlen; + uint16_t pklen; + + /* read lengths */ + if(*dl < 4) + return -1; + hitlen = (*d)[0]; + algo = (*d)[1]; + pklen = gldns_read_uint16((*d)+2); + if(*dl < (size_t)4 + (size_t)hitlen + (size_t)pklen) + return -1; + + /* write: algo hit pubkey */ + w = gldns_str_print(s, sl, "%u ", (unsigned)algo); + w += print_hex_buf(s, sl, (*d)+4, hitlen); + w += gldns_str_print(s, sl, " "); + (*d)+=4+hitlen; + (*dl)-= (4+hitlen); + w += gldns_wire2str_b64_scan_num(d, dl, s, sl, pklen); + return w; +} + +int gldns_wire2str_int16_data_scan(uint8_t** d, size_t* dl, char** s, size_t* sl) +{ + uint16_t n; + if(*dl < 2) + return -1; + n = gldns_read_uint16(*d); + if(*dl < 2+(size_t)n) + return -1; + (*d)+=2; + (*dl)-=2; + return gldns_wire2str_b64_scan_num(d, dl, s, sl, n); +} + +int gldns_wire2str_nsec3_next_owner_scan(uint8_t** d, size_t* dl, char** s, + size_t* sl) +{ + return gldns_wire2str_b32_ext_scan(d, dl, s, sl); +} + +int gldns_wire2str_ilnp64_scan(uint8_t** d, size_t* dl, char** s, size_t* sl) +{ + int w; + if(*dl < 8) + return -1; + w = gldns_str_print(s, sl, "%.4x:%.4x:%.4x:%.4x", + gldns_read_uint16(*d), gldns_read_uint16((*d)+2), + gldns_read_uint16((*d)+4), gldns_read_uint16((*d)+6)); + (*d)+=8; + (*dl)-=8; + return w; +} + +int gldns_wire2str_eui48_scan(uint8_t** d, size_t* dl, char** s, size_t* sl) +{ + int w; + if(*dl < 6) + return -1; + w = gldns_str_print(s, sl, "%.2x-%.2x-%.2x-%.2x-%.2x-%.2x", + (*d)[0], (*d)[1], (*d)[2], (*d)[3], (*d)[4], (*d)[5]); + (*d)+=6; + (*dl)-=6; + return w; +} + +int gldns_wire2str_eui64_scan(uint8_t** d, size_t* dl, char** s, size_t* sl) +{ + int w; + if(*dl < 8) + return -1; + w = gldns_str_print(s, sl, "%.2x-%.2x-%.2x-%.2x-%.2x-%.2x-%.2x-%.2x", + (*d)[0], (*d)[1], (*d)[2], (*d)[3], (*d)[4], (*d)[5], + (*d)[6], (*d)[7]); + (*d)+=8; + (*dl)-=8; + return w; +} + +int gldns_wire2str_tag_scan(uint8_t** d, size_t* dl, char** s, size_t* sl) +{ + size_t i, n; + int w = 0; + if(*dl < 1) + return -1; + n = (size_t)((*d)[0]); + if(*dl < 1+n) + return -1; + for(i=0; iname) + w += gldns_str_print(s, sl, " %s", lt->name); + else w += gldns_str_print(s, sl, " %d", (int)data[i]); + } + return w; +} + +int gldns_wire2str_edns_dhu_print(char** s, size_t* sl, uint8_t* data, + size_t len) +{ + gldns_lookup_table *lt; + size_t i; + int w = 0; + for(i=0; iname) + w += gldns_str_print(s, sl, " %s", lt->name); + else w += gldns_str_print(s, sl, " %d", (int)data[i]); + } + return w; +} + +int gldns_wire2str_edns_n3u_print(char** s, size_t* sl, uint8_t* data, + size_t len) +{ + size_t i; + int w = 0; + for(i=0; i 4) { + w += gldns_str_print(s, sl, "trailingdata:"); + w += print_hex_buf(s, sl, data+4+4, len-4-4); + w += gldns_str_print(s, sl, " "); + len = 4+4; + } + memmove(ip4, data+4, len-4); + if(!inet_ntop(AF_INET, ip4, buf, (socklen_t)sizeof(buf))) { + w += gldns_str_print(s, sl, "ip4ntoperror "); + w += print_hex_buf(s, sl, data+4+4, len-4-4); + } else { + w += gldns_str_print(s, sl, "%s", buf); + } + } else if(family == 2) { + /* IP6 */ + char buf[64]; + uint8_t ip6[16]; + memset(ip6, 0, sizeof(ip6)); + if(len-4 > 16) { + w += gldns_str_print(s, sl, "trailingdata:"); + w += print_hex_buf(s, sl, data+4+16, len-4-16); + w += gldns_str_print(s, sl, " "); + len = 4+16; + } + memmove(ip6, data+4, len-4); +#ifdef AF_INET6 + if(!inet_ntop(AF_INET6, ip6, buf, (socklen_t)sizeof(buf))) { + w += gldns_str_print(s, sl, "ip6ntoperror "); + w += print_hex_buf(s, sl, data+4+4, len-4-4); + } else { + w += gldns_str_print(s, sl, "%s", buf); + } +#else + w += print_hex_buf(s, sl, data+4+4, len-4-4); +#endif + } else { + /* unknown */ + w += gldns_str_print(s, sl, "family %d ", + (int)family); + w += print_hex_buf(s, sl, data, len); + } + w += gldns_str_print(s, sl, "/%d scope /%d", (int)source, (int)scope); + return w; +} + +int gldns_wire2str_edns_option_print(char** s, size_t* sl, + uint16_t option_code, uint8_t* optdata, size_t optlen) +{ + int w = 0; + w += gldns_wire2str_edns_option_code_print(s, sl, option_code); + w += gldns_str_print(s, sl, ": "); + switch(option_code) { + case GLDNS_EDNS_LLQ: + w += gldns_wire2str_edns_llq_print(s, sl, optdata, optlen); + break; + case GLDNS_EDNS_UL: + w += gldns_wire2str_edns_ul_print(s, sl, optdata, optlen); + break; + case GLDNS_EDNS_NSID: + w += gldns_wire2str_edns_nsid_print(s, sl, optdata, optlen); + break; + case GLDNS_EDNS_DAU: + w += gldns_wire2str_edns_dau_print(s, sl, optdata, optlen); + break; + case GLDNS_EDNS_DHU: + w += gldns_wire2str_edns_dhu_print(s, sl, optdata, optlen); + break; + case GLDNS_EDNS_N3U: + w += gldns_wire2str_edns_n3u_print(s, sl, optdata, optlen); + break; + case GLDNS_EDNS_CLIENT_SUBNET: + w += gldns_wire2str_edns_subnet_print(s, sl, optdata, optlen); + break; + default: + /* unknown option code */ + w += print_hex_buf(s, sl, optdata, optlen); + break; + } + return w; +} + +/** print the edns options to string */ +static int +print_edns_opts(char** s, size_t* sl, uint8_t* rdata, size_t rdatalen) +{ + uint16_t option_code, option_len; + int w = 0; + while(rdatalen > 0) { + /* option name */ + if(rdatalen < 4) { + w += gldns_str_print(s, sl, " ; malformed: "); + w += print_hex_buf(s, sl, rdata, rdatalen); + return w; + } + option_code = gldns_read_uint16(rdata); + option_len = gldns_read_uint16(rdata+2); + rdata += 4; + rdatalen -= 4; + + /* option value */ + if(rdatalen < (size_t)option_len) { + w += gldns_str_print(s, sl, " ; malformed "); + w += gldns_wire2str_edns_option_code_print(s, sl, + option_code); + w += gldns_str_print(s, sl, ": "); + w += print_hex_buf(s, sl, rdata, rdatalen); + return w; + } + w += gldns_str_print(s, sl, " ; "); + w += gldns_wire2str_edns_option_print(s, sl, option_code, + rdata, option_len); + rdata += option_len; + rdatalen -= option_len; + } + return w; +} + +int gldns_wire2str_edns_scan(uint8_t** data, size_t* data_len, char** str, + size_t* str_len, uint8_t* pkt, size_t pktlen) +{ + int w = 0; + uint8_t ext_rcode, edns_version; + uint16_t udpsize, edns_bits, rdatalen; + w += gldns_str_print(str, str_len, "; EDNS:"); + + /* some input checks, domain name */ + if(*data_len < 1+10) + return w + print_remainder_hex("Error malformed 0x", + data, data_len, str, str_len); + if(*data[0] != 0) { + return w + print_remainder_hex("Error nonrootdname 0x", + data, data_len, str, str_len); + } + (*data)++; + (*data_len)--; + + /* check type and read fixed contents */ + if(gldns_read_uint16((*data)) != GLDNS_RR_TYPE_OPT) { + return w + print_remainder_hex("Error nottypeOPT 0x", + data, data_len, str, str_len); + } + udpsize = gldns_read_uint16((*data)+2); + ext_rcode = (*data)[4]; + edns_version = (*data)[5]; + edns_bits = gldns_read_uint16((*data)+6); + rdatalen = gldns_read_uint16((*data)+8); + (*data)+=10; + (*data_len)-=10; + + w += gldns_str_print(str, str_len, " version: %u;", + (unsigned)edns_version); + w += gldns_str_print(str, str_len, " flags:"); + if((edns_bits & GLDNS_EDNS_MASK_DO_BIT)) + w += gldns_str_print(str, str_len, " do"); + /* the extended rcode is the value set, shifted four bits, + * and or'd with the original rcode */ + if(ext_rcode) { + int rc = ((int)ext_rcode)<<4; + if(pkt && pktlen >= GLDNS_HEADER_SIZE) + rc |= GLDNS_RCODE_WIRE(pkt); + w += gldns_str_print(str, str_len, " ; ext-rcode: %d", rc); + } + w += gldns_str_print(str, str_len, " ; udp: %u", (unsigned)udpsize); + + if(rdatalen) { + if(*data_len < rdatalen) { + w += gldns_str_print(str, str_len, + " ; Error EDNS rdata too short; "); + rdatalen = *data_len; + } + w += print_edns_opts(str, str_len, *data, rdatalen); + (*data) += rdatalen; + (*data_len) -= rdatalen; + } + w += gldns_str_print(str, str_len, "\n"); + return w; +} diff --git a/src/gldns/wire2str.h b/src/gldns/wire2str.h new file mode 100644 index 00000000..050fb8e7 --- /dev/null +++ b/src/gldns/wire2str.h @@ -0,0 +1,984 @@ +/** + * wire2str.h - txt presentation of RRs + * + * (c) NLnet Labs, 2005-2006 + * + * See the file LICENSE for the license + */ + +/** + * \file + * + * Contains functions to translate the wireformat to text + * representation, as well as functions to print them. + */ + +#ifndef GLDNS_WIRE2STR_H +#define GLDNS_WIRE2STR_H + +#ifdef __cplusplus +extern "C" { +#endif +struct gldns_struct_lookup_table; + +/* lookup tables for standard DNS stuff */ +/** Taken from RFC 2535, section 7. */ +extern struct gldns_struct_lookup_table* gldns_algorithms; +/** DS record hash algorithms */ +extern struct gldns_struct_lookup_table* gldns_hashes; +/** Taken from RFC 2538, section 2.1. */ +extern struct gldns_struct_lookup_table* gldns_cert_algorithms; +/** Response codes */ +extern struct gldns_struct_lookup_table* gldns_rcodes; +/** Operation codes */ +extern struct gldns_struct_lookup_table* gldns_opcodes; +/** EDNS flags */ +extern struct gldns_struct_lookup_table* gldns_edns_flags; +/** EDNS option codes */ +extern struct gldns_struct_lookup_table* gldns_edns_options; +/** error string from wireparse */ +extern struct gldns_struct_lookup_table* gldns_wireparse_errors; + +/** + * Convert wireformat packet to a string representation + * @param data: wireformat packet data (starting at ID bytes). + * @param len: length of packet. + * @return string(malloced) or NULL on failure. + */ +char* gldns_wire2str_pkt(uint8_t* data, size_t len); + +/** + * Convert wireformat RR to a string representation. + * @param rr: the wireformat RR, in uncompressed form. Starts at the domain + * name start, ends with the rdata of the RR. + * @param len: length of the rr wireformat. + * @return string(malloced) or NULL on failure. + */ +char* gldns_wire2str_rr(uint8_t* rr, size_t len); + +/** + * Conver wire dname to a string. + * @param dname: the dname in uncompressed wireformat. + * @param dname_len: length of the dname. + * @return string or NULL on failure. + */ +char* gldns_wire2str_dname(uint8_t* dname, size_t dname_len); + +/** + * Convert wire RR type to a string, 'MX', 'TYPE1234'... + * @param rrtype: the RR type in host order. + * @return malloced string with the RR type or NULL on malloc failure. + */ +char* gldns_wire2str_type(uint16_t rrtype); + +/** + * Convert wire RR class to a string, 'IN', 'CLASS1'. + * @param rrclass: the RR class in host order. + * @return malloced string with the RR class or NULL on malloc failure. + */ +char* gldns_wire2str_class(uint16_t rrclass); + +/** + * Convert wire packet rcode to a string, 'NOERROR', 'NXDOMAIN'... + * @param rcode: as integer, host order + * @return malloced string with the rcode or NULL on malloc failure. + */ +char* gldns_wire2str_rcode(int rcode); + +/** + * Print to string, move string along for next content. With va_list. + * @param str: string buffer. Adjusted at end to after the output. + * @param slen: length of the string buffer. Adjusted at end. + * @param format: printf format string. + * @param args: arguments for printf. + * @return number of characters needed. Can be larger than slen. + */ +int gldns_str_vprint(char** str, size_t* slen, const char* format, va_list args); + +/** + * Print to string, move string along for next content. + * @param str: string buffer. Adjusted at end to after the output. + * @param slen: length of the string buffer. Adjusted at end. + * @param format: printf format string and arguments for it. + * @return number of characters needed. Can be larger than slen. + */ +int gldns_str_print(char** str, size_t* slen, const char* format, ...) + ATTR_FORMAT(printf, 3, 4); + +/** + * Convert wireformat packet to a string representation with user buffer + * It appends every RR with default comments. + * For more formatter options use the function: TBD(TODO) + * @param data: wireformat packet data (starting at ID bytes). + * @param data_len: length of packet. + * @param str: the string buffer for the output. + * If you pass NULL as the str the return value of the function is + * the str_len you need for the entire packet. It does not include + * the 0 byte at the end. + * @param str_len: the size of the string buffer. If more is needed, it'll + * silently truncate the output to fit in the buffer. + * @return the number of characters for this element, excluding zerobyte. + * Is larger than str_len if output was truncated. + */ +int gldns_wire2str_pkt_buf(uint8_t* data, size_t data_len, char* str, + size_t str_len); + +/** + * Scan wireformat packet to a string representation with user buffer + * It appends every RR with default comments. + * For more formatter options use the function: TBD(TODO) + * @param data: wireformat packet data (starting at ID bytes). + * @param data_len: length of packet. + * @param str: the string buffer for the output. + * @param str_len: the size of the string buffer. + * @return number of characters for string. + * returns the number of characters that are needed (except terminating null), + * so it may return a value larger than str_len. + * On error you get less output (i.e. shorter output in str (null terminated)) + * On exit the data, data_len, str and str_len values are adjusted to move them + * from their original position along the input and output for the content + * that has been consumed (and produced) by this function. If the end of the + * output string is reached, *str_len is set to 0. The output string is null + * terminated (shortening the output if necessary). If the end of the input + * is reached *data_len is set to 0. + */ +int gldns_wire2str_pkt_scan(uint8_t** data, size_t* data_len, char** str, + size_t* str_len); + +/** + * Scan wireformat rr to string, with user buffers. It shifts the arguments + * to move along (see gldns_wire2str_pkt_scan). + * @param data: wireformat data. + * @param data_len: length of data buffer. + * @param str: string buffer. + * @param str_len: length of string buffer. + * @param pkt: packet for decompression, if NULL no decompression. + * @param pktlen: length of packet buffer. + * @return number of characters (except null) needed to print. + */ +int gldns_wire2str_rr_scan(uint8_t** data, size_t* data_len, char** str, + size_t* str_len, uint8_t* pkt, size_t pktlen); + +/** + * Scan wireformat question rr to string, with user buffers. + * It shifts the arguments to move along (see gldns_wire2str_pkt_scan). + * @param data: wireformat data. + * @param data_len: length of data buffer. + * @param str: string buffer. + * @param str_len: length of string buffer. + * @param pkt: packet for decompression, if NULL no decompression. + * @param pktlen: length of packet buffer. + * @return number of characters (except null) needed to print. + */ +int gldns_wire2str_rrquestion_scan(uint8_t** data, size_t* data_len, char** str, + size_t* str_len, uint8_t* pkt, size_t pktlen); + +/** + * Scan wireformat RR to string in unknown RR format, with user buffers. + * It shifts the arguments to move along (see gldns_wire2str_pkt_scan). + * @param data: wireformat data. + * @param data_len: length of data buffer. + * @param str: string buffer. + * @param str_len: length of string buffer. + * @param pkt: packet for decompression, if NULL no decompression. + * @param pktlen: length of packet buffer. + * @return number of characters (except null) needed to print. + */ +int gldns_wire2str_rr_unknown_scan(uint8_t** data, size_t* data_len, char** str, + size_t* str_len, uint8_t* pkt, size_t pktlen); + +/** + * Print to string the RR-information comment in default format, + * with user buffers. Moves string along. + * @param str: string buffer. + * @param str_len: length of string buffer. + * @param rr: wireformat data. + * @param rrlen: length of data buffer. + * @param dname_off: offset in buffer behind owner dname, the compressed size + * of the owner name. + * @param rrtype: type of the RR, host format. + * @return number of characters (except null) needed to print. + */ +int gldns_wire2str_rr_comment_print(char** str, size_t* str_len, uint8_t* rr, + size_t rrlen, size_t dname_off, uint16_t rrtype); + +/** + * Scan wireformat packet header to string, with user buffers. + * It shifts the arguments to move along (see gldns_wire2str_pkt_scan). + * @param data: wireformat data. + * @param data_len: length of data buffer. + * @param str: string buffer. + * @param str_len: length of string buffer. + * @return number of characters (except null) needed to print. + */ +int gldns_wire2str_header_scan(uint8_t** data, size_t* data_len, char** str, + size_t* str_len); + +/** + * Scan wireformat rdata to string, with user buffers. + * It shifts the arguments to move along (see gldns_wire2str_pkt_scan). + * @param data: wireformat data. + * @param data_len: length of data buffer. The length of the rdata in the + * buffer. The rdatalen itself has already been scanned, the data + * points to the rdata after the rdatalen. + * @param str: string buffer. + * @param str_len: length of string buffer. + * @param rrtype: RR type of Rdata, host format. + * @param pkt: packet for decompression, if NULL no decompression. + * @param pktlen: length of packet buffer. + * @return number of characters (except null) needed to print. + */ +int gldns_wire2str_rdata_scan(uint8_t** data, size_t* data_len, char** str, + size_t* str_len, uint16_t rrtype, uint8_t* pkt, size_t pktlen); + +/** + * Scan wireformat rdata to string in unknown format, with user buffers. + * It shifts the arguments to move along (see gldns_wire2str_pkt_scan). + * @param data: wireformat data. + * @param data_len: length of data buffer, the length of the rdata in buffer. + * @param str: string buffer. + * @param str_len: length of string buffer. + * @return number of characters (except null) needed to print. + */ +int gldns_wire2str_rdata_unknown_scan(uint8_t** data, size_t* data_len, + char** str, size_t* str_len); + +/** + * Scan wireformat domain name to string, with user buffers. + * It shifts the arguments to move along (see gldns_wire2str_pkt_scan). + * @param data: wireformat data. + * @param data_len: length of data buffer. + * @param str: string buffer. + * @param str_len: length of string buffer. + * @param pkt: packet for decompression, if NULL no decompression. + * @param pktlen: length of packet buffer. + * @return number of characters (except null) needed to print. + */ +int gldns_wire2str_dname_scan(uint8_t** data, size_t* data_len, char** str, + size_t* str_len, uint8_t* pkt, size_t pktlen); + +/** + * Scan wireformat rr type to string, with user buffers. + * It shifts the arguments to move along (see gldns_wire2str_pkt_scan). + * @param data: wireformat data. + * @param data_len: length of data buffer. + * @param str: string buffer. + * @param str_len: length of string buffer. + * @return number of characters (except null) needed to print. + */ +int gldns_wire2str_type_scan(uint8_t** data, size_t* data_len, char** str, + size_t* str_len); + +/** + * Scan wireformat rr class to string, with user buffers. + * It shifts the arguments to move along (see gldns_wire2str_pkt_scan). + * @param data: wireformat data. + * @param data_len: length of data buffer. + * @param str: string buffer. + * @param str_len: length of string buffer. + * @return number of characters (except null) needed to print. + */ +int gldns_wire2str_class_scan(uint8_t** data, size_t* data_len, char** str, + size_t* str_len); + +/** + * Scan wireformat rr ttl to string, with user buffers. + * It shifts the arguments to move along (see gldns_wire2str_pkt_scan). + * @param data: wireformat data. + * @param data_len: length of data buffer. + * @param str: string buffer. + * @param str_len: length of string buffer. + * @return number of characters (except null) needed to print. + */ +int gldns_wire2str_ttl_scan(uint8_t** data, size_t* data_len, char** str, + size_t* str_len); + + +/** + * Print host format rr type to string. Moves string along, user buffers. + * @param str: string buffer. + * @param str_len: length of string buffer. + * @param rrtype: host format rr type. + * @return number of characters (except null) needed to print. + */ +int gldns_wire2str_type_print(char** str, size_t* str_len, uint16_t rrtype); + +/** + * Print host format rr class to string. Moves string along, user buffers. + * @param str: string buffer. + * @param str_len: length of string buffer. + * @param rrclass: host format rr class. + * @return number of characters (except null) needed to print. + */ +int gldns_wire2str_class_print(char** str, size_t* str_len, uint16_t rrclass); + +/** + * Print host format rcode to string. Moves string along, user buffers. + * @param str: string buffer. + * @param str_len: length of string buffer. + * @param rcode: host format rcode number. + * @return number of characters (except null) needed to print. + */ +int gldns_wire2str_rcode_print(char** str, size_t* str_len, int rcode); + +/** + * Print host format opcode to string. Moves string along, user buffers. + * @param str: string buffer. + * @param str_len: length of string buffer. + * @param opcode: host format opcode number. + * @return number of characters (except null) needed to print. + */ +int gldns_wire2str_opcode_print(char** str, size_t* str_len, int opcode); + +/** + * Print host format EDNS0 option to string. Moves string along, user buffers. + * @param str: string buffer. + * @param str_len: length of string buffer. + * @param opcode: host format option number. + * @return number of characters (except null) needed to print. + */ +int gldns_wire2str_edns_option_code_print(char** str, size_t* str_len, + uint16_t opcode); + +/** + * Convert RR to string presentation format, on one line. User buffer. + * @param rr: wireformat RR data + * @param rr_len: length of the rr wire data. + * @param str: the string buffer to write to. + * If you pass NULL as the str, the return value of the function is + * the str_len you need for the entire packet. It does not include + * the 0 byte at the end. + * @param str_len: the size of the string buffer. If more is needed, it'll + * silently truncate the output to fit in the buffer. + * @return the number of characters for this element, excluding zerobyte. + * Is larger than str_len if output was truncated. + */ +int gldns_wire2str_rr_buf(uint8_t* rr, size_t rr_len, char* str, + size_t str_len); + +/** + * 3597 printout of an RR in unknown rr format. + * There are more format and comment options available for printout + * with the function: TBD(TODO) + * @param rr: wireformat RR data + * @param rr_len: length of the rr wire data. + * @param str: the string buffer to write to. + * If you pass NULL as the str, the return value of the function is + * the str_len you need for the entire rr. It does not include + * the 0 byte at the end. + * @param str_len: the size of the string buffer. If more is needed, it'll + * silently truncate the output to fit in the buffer. + * @return the number of characters for this element, excluding zerobyte. + * Is larger than str_len if output was truncated. + */ +int gldns_wire2str_rr_unknown_buf(uint8_t* rr, size_t rr_len, char* str, + size_t str_len); + +/** + * This creates the comment to print after the RR. ; keytag=... , and other + * basic comments for RRs. + * There are more format and comment options available for printout + * with the function: TBD(TODO) + * @param rr: wireformat RR data + * @param rr_len: length of the rr wire data. + * @param dname_len: length of the dname in front of the RR. + * @param str: the string buffer to write to. + * If you pass NULL as the str, the return value of the function is + * the str_len you need for the entire comment. It does not include + * the 0 byte at the end. + * @param str_len: the size of the string buffer. If more is needed, it'll + * silently truncate the output to fit in the buffer. + * @return the number of characters for this element, excluding zerobyte. + * Is larger than str_len if output was truncated. + */ +int gldns_wire2str_rr_comment_buf(uint8_t* rr, size_t rr_len, size_t dname_len, + char* str, size_t str_len); + +/** + * Convert RDATA to string presentation format, on one line. User buffer. + * @param rdata: wireformat rdata part of an RR. + * @param rdata_len: length of the rr wire data. + * @param str: the string buffer to write to. + * If you pass NULL as the str, the return value of the function is + * the str_len you need for the entire packet. It does not include + * the 0 byte at the end. + * @param str_len: the size of the string buffer. If more is needed, it'll + * silently truncate the output to fit in the buffer. + * @param rrtype: rr type of the data + * @return the number of characters for this element, excluding zerobyte. + * Is larger than str_len if output was truncated. + */ +int gldns_wire2str_rdata_buf(uint8_t* rdata, size_t rdata_len, char* str, + size_t str_len, uint16_t rrtype); + +/** + * Convert wire RR type to a string, 'MX', 'TYPE12'. With user buffer. + * @param rrtype: the RR type in host order. + * @param str: the string to write to. + * @param len: length of str. + * @return the number of characters for this element, excluding zerobyte. + * Is larger than str_len if output was truncated. + */ +int gldns_wire2str_type_buf(uint16_t rrtype, char* str, size_t len); + +/** + * Convert wire RR class to a string, 'IN', 'CLASS12'. With user buffer. + * @param rrclass: the RR class in host order. + * @param str: the string to write to. + * @param len: length of str. + * @return the number of characters for this element, excluding zerobyte. + * Is larger than str_len if output was truncated. + */ +int gldns_wire2str_class_buf(uint16_t rrclass, char* str, size_t len); + +/** + * Convert wire RR rcode to a string, 'NOERROR', 'NXDOMAIN'. With user buffer. + * @param rcode: rcode as integer in host order + * @param str: the string to write to. + * @param len: length of str. + * @return the number of characters for this element, excluding zerobyte. + * Is larger than str_len if output was truncated. + */ +int gldns_wire2str_rcode_buf(int rcode, char* str, size_t len); + +/** + * Convert wire dname to a string, "example.com.". With user buffer. + * @param dname: the dname in uncompressed wireformat. + * @param dname_len: length of the dname. + * @param str: the string to write to. + * @param len: length of string. + * @return the number of characters for this element, excluding zerobyte. + * Is larger than str_len if output was truncated. + */ +int gldns_wire2str_dname_buf(uint8_t* dname, size_t dname_len, char* str, + size_t len); + +/** + * Scan wireformat rdf field to string, with user buffers. + * It shifts the arguments to move along (see gldns_wire2str_pkt_scan). + * @param data: wireformat data. + * @param data_len: length of data buffer. + * @param str: string buffer. + * @param str_len: length of string buffer. + * @param rdftype: the type of the rdata field, enum gldns_rdf_type. + * @param pkt: packet for decompression, if NULL no decompression. + * @param pktlen: length of packet buffer. + * @return number of characters (except null) needed to print. + * Can return -1 on failure. + */ +int gldns_wire2str_rdf_scan(uint8_t** data, size_t* data_len, char** str, + size_t* str_len, int rdftype, uint8_t* pkt, size_t pktlen); + +/** + * Scan wireformat int8 field to string, with user buffers. + * It shifts the arguments to move along (see gldns_wire2str_pkt_scan). + * @param data: wireformat data. + * @param data_len: length of data buffer. + * @param str: string buffer. + * @param str_len: length of string buffer. + * @return number of characters (except null) needed to print. + * Can return -1 on failure. + */ +int gldns_wire2str_int8_scan(uint8_t** data, size_t* data_len, char** str, + size_t* str_len); + +/** + * Scan wireformat int16 field to string, with user buffers. + * It shifts the arguments to move along (see gldns_wire2str_pkt_scan). + * @param data: wireformat data. + * @param data_len: length of data buffer. + * @param str: string buffer. + * @param str_len: length of string buffer. + * @return number of characters (except null) needed to print. + * Can return -1 on failure. + */ +int gldns_wire2str_int16_scan(uint8_t** data, size_t* data_len, char** str, + size_t* str_len); + +/** + * Scan wireformat int32 field to string, with user buffers. + * It shifts the arguments to move along (see gldns_wire2str_pkt_scan). + * @param data: wireformat data. + * @param data_len: length of data buffer. + * @param str: string buffer. + * @param str_len: length of string buffer. + * @return number of characters (except null) needed to print. + * Can return -1 on failure. + */ +int gldns_wire2str_int32_scan(uint8_t** data, size_t* data_len, char** str, + size_t* str_len); + +/** + * Scan wireformat period field to string, with user buffers. + * It shifts the arguments to move along (see gldns_wire2str_pkt_scan). + * @param data: wireformat data. + * @param data_len: length of data buffer. + * @param str: string buffer. + * @param str_len: length of string buffer. + * @return number of characters (except null) needed to print. + * Can return -1 on failure. + */ +int gldns_wire2str_period_scan(uint8_t** data, size_t* data_len, char** str, + size_t* str_len); + +/** + * Scan wireformat tsigtime field to string, with user buffers. + * It shifts the arguments to move along (see gldns_wire2str_pkt_scan). + * @param data: wireformat data. + * @param data_len: length of data buffer. + * @param str: string buffer. + * @param str_len: length of string buffer. + * @return number of characters (except null) needed to print. + * Can return -1 on failure. + */ +int gldns_wire2str_tsigtime_scan(uint8_t** data, size_t* data_len, char** str, + size_t* str_len); + +/** + * Scan wireformat ip4 A field to string, with user buffers. + * It shifts the arguments to move along (see gldns_wire2str_pkt_scan). + * @param data: wireformat data. + * @param data_len: length of data buffer. + * @param str: string buffer. + * @param str_len: length of string buffer. + * @return number of characters (except null) needed to print. + * Can return -1 on failure. + */ +int gldns_wire2str_a_scan(uint8_t** data, size_t* data_len, char** str, + size_t* str_len); + +/** + * Scan wireformat ip6 AAAA field to string, with user buffers. + * It shifts the arguments to move along (see gldns_wire2str_pkt_scan). + * @param data: wireformat data. + * @param data_len: length of data buffer. + * @param str: string buffer. + * @param str_len: length of string buffer. + * @return number of characters (except null) needed to print. + * Can return -1 on failure. + */ +int gldns_wire2str_aaaa_scan(uint8_t** data, size_t* data_len, char** str, + size_t* str_len); + +/** + * Scan wireformat str field to string, with user buffers. + * It shifts the arguments to move along (see gldns_wire2str_pkt_scan). + * @param data: wireformat data. + * @param data_len: length of data buffer. + * @param str: string buffer. + * @param str_len: length of string buffer. + * @return number of characters (except null) needed to print. + * Can return -1 on failure. + */ +int gldns_wire2str_str_scan(uint8_t** data, size_t* data_len, char** str, + size_t* str_len); + +/** + * Scan wireformat apl field to string, with user buffers. + * It shifts the arguments to move along (see gldns_wire2str_pkt_scan). + * @param data: wireformat data. + * @param data_len: length of data buffer. + * @param str: string buffer. + * @param str_len: length of string buffer. + * @return number of characters (except null) needed to print. + * Can return -1 on failure. + */ +int gldns_wire2str_apl_scan(uint8_t** data, size_t* data_len, char** str, + size_t* str_len); + +/** + * Scan wireformat b32_ext field to string, with user buffers. + * It shifts the arguments to move along (see gldns_wire2str_pkt_scan). + * @param data: wireformat data. + * @param data_len: length of data buffer. + * @param str: string buffer. + * @param str_len: length of string buffer. + * @return number of characters (except null) needed to print. + * Can return -1 on failure. + */ +int gldns_wire2str_b32_ext_scan(uint8_t** data, size_t* data_len, char** str, + size_t* str_len); + +/** + * Scan wireformat b64 field to string, with user buffers. + * It shifts the arguments to move along (see gldns_wire2str_pkt_scan). + * @param data: wireformat data. + * @param data_len: length of data buffer. + * @param str: string buffer. + * @param str_len: length of string buffer. + * @return number of characters (except null) needed to print. + * Can return -1 on failure. + */ +int gldns_wire2str_b64_scan(uint8_t** data, size_t* data_len, char** str, + size_t* str_len); + +/** + * Scan wireformat hex field to string, with user buffers. + * It shifts the arguments to move along (see gldns_wire2str_pkt_scan). + * @param data: wireformat data. + * @param data_len: length of data buffer. + * @param str: string buffer. + * @param str_len: length of string buffer. + * @return number of characters (except null) needed to print. + * Can return -1 on failure. + */ +int gldns_wire2str_hex_scan(uint8_t** data, size_t* data_len, char** str, + size_t* str_len); + +/** + * Scan wireformat nsec bitmap field to string, with user buffers. + * It shifts the arguments to move along (see gldns_wire2str_pkt_scan). + * @param data: wireformat data. + * @param data_len: length of data buffer. + * @param str: string buffer. + * @param str_len: length of string buffer. + * @return number of characters (except null) needed to print. + * Can return -1 on failure. + */ +int gldns_wire2str_nsec_scan(uint8_t** data, size_t* data_len, char** str, + size_t* str_len); + +/** + * Scan wireformat nsec3_salt field to string, with user buffers. + * It shifts the arguments to move along (see gldns_wire2str_pkt_scan). + * @param data: wireformat data. + * @param data_len: length of data buffer. + * @param str: string buffer. + * @param str_len: length of string buffer. + * @return number of characters (except null) needed to print. + * Can return -1 on failure. + */ +int gldns_wire2str_nsec3_salt_scan(uint8_t** data, size_t* data_len, char** str, + size_t* str_len); + +/** + * Scan wireformat cert_alg field to string, with user buffers. + * It shifts the arguments to move along (see gldns_wire2str_pkt_scan). + * @param data: wireformat data. + * @param data_len: length of data buffer. + * @param str: string buffer. + * @param str_len: length of string buffer. + * @return number of characters (except null) needed to print. + * Can return -1 on failure. + */ +int gldns_wire2str_cert_alg_scan(uint8_t** data, size_t* data_len, char** str, + size_t* str_len); + +/** + * Scan wireformat alg field to string, with user buffers. + * It shifts the arguments to move along (see gldns_wire2str_pkt_scan). + * @param data: wireformat data. + * @param data_len: length of data buffer. + * @param str: string buffer. + * @param str_len: length of string buffer. + * @return number of characters (except null) needed to print. + * Can return -1 on failure. + */ +int gldns_wire2str_alg_scan(uint8_t** data, size_t* data_len, char** str, + size_t* str_len); + +/** + * Scan wireformat type unknown field to string, with user buffers. + * It shifts the arguments to move along (see gldns_wire2str_pkt_scan). + * @param data: wireformat data. + * @param data_len: length of data buffer. + * @param str: string buffer. + * @param str_len: length of string buffer. + * @return number of characters (except null) needed to print. + * Can return -1 on failure. + */ +int gldns_wire2str_unknown_scan(uint8_t** data, size_t* data_len, char** str, + size_t* str_len); + +/** + * Scan wireformat time field to string, with user buffers. + * It shifts the arguments to move along (see gldns_wire2str_pkt_scan). + * @param data: wireformat data. + * @param data_len: length of data buffer. + * @param str: string buffer. + * @param str_len: length of string buffer. + * @return number of characters (except null) needed to print. + * Can return -1 on failure. + */ +int gldns_wire2str_time_scan(uint8_t** data, size_t* data_len, char** str, + size_t* str_len); + +/** + * Scan wireformat LOC field to string, with user buffers. + * It shifts the arguments to move along (see gldns_wire2str_pkt_scan). + * @param data: wireformat data. + * @param data_len: length of data buffer. + * @param str: string buffer. + * @param str_len: length of string buffer. + * @return number of characters (except null) needed to print. + * Can return -1 on failure. + */ +int gldns_wire2str_loc_scan(uint8_t** data, size_t* data_len, char** str, + size_t* str_len); + +/** + * Scan wireformat WKS field to string, with user buffers. + * It shifts the arguments to move along (see gldns_wire2str_pkt_scan). + * @param data: wireformat data. + * @param data_len: length of data buffer. + * @param str: string buffer. + * @param str_len: length of string buffer. + * @return number of characters (except null) needed to print. + * Can return -1 on failure. + */ +int gldns_wire2str_wks_scan(uint8_t** data, size_t* data_len, char** str, + size_t* str_len); + +/** + * Scan wireformat NSAP field to string, with user buffers. + * It shifts the arguments to move along (see gldns_wire2str_pkt_scan). + * @param data: wireformat data. + * @param data_len: length of data buffer. + * @param str: string buffer. + * @param str_len: length of string buffer. + * @return number of characters (except null) needed to print. + * Can return -1 on failure. + */ +int gldns_wire2str_nsap_scan(uint8_t** data, size_t* data_len, char** str, + size_t* str_len); + +/** + * Scan wireformat ATMA field to string, with user buffers. + * It shifts the arguments to move along (see gldns_wire2str_pkt_scan). + * @param data: wireformat data. + * @param data_len: length of data buffer. + * @param str: string buffer. + * @param str_len: length of string buffer. + * @return number of characters (except null) needed to print. + * Can return -1 on failure. + */ +int gldns_wire2str_atma_scan(uint8_t** data, size_t* data_len, char** str, + size_t* str_len); + +/** + * Scan wireformat IPSECKEY field to string, with user buffers. + * It shifts the arguments to move along (see gldns_wire2str_pkt_scan). + * @param data: wireformat data. + * @param data_len: length of data buffer. + * @param str: string buffer. + * @param str_len: length of string buffer. + * @param pkt: packet for decompression, if NULL no decompression. + * @param pktlen: length of packet buffer. + * @return number of characters (except null) needed to print. + * Can return -1 on failure. + */ +int gldns_wire2str_ipseckey_scan(uint8_t** data, size_t* data_len, char** str, + size_t* str_len, uint8_t* pkt, size_t pktlen); + +/** + * Scan wireformat HIP (algo, HIT, pubkey) field to string, with user buffers. + * It shifts the arguments to move along (see gldns_wire2str_pkt_scan). + * @param data: wireformat data. + * @param data_len: length of data buffer. + * @param str: string buffer. + * @param str_len: length of string buffer. + * @return number of characters (except null) needed to print. + * Can return -1 on failure. + */ +int gldns_wire2str_hip_scan(uint8_t** data, size_t* data_len, char** str, + size_t* str_len); + +/** + * Scan wireformat int16_data field to string, with user buffers. + * It shifts the arguments to move along (see gldns_wire2str_pkt_scan). + * @param data: wireformat data. + * @param data_len: length of data buffer. + * @param str: string buffer. + * @param str_len: length of string buffer. + * @return number of characters (except null) needed to print. + * Can return -1 on failure. + */ +int gldns_wire2str_int16_data_scan(uint8_t** data, size_t* data_len, char** str, + size_t* str_len); + +/** + * Scan wireformat nsec3_next_owner field to string, with user buffers. + * It shifts the arguments to move along (see gldns_wire2str_pkt_scan). + * @param data: wireformat data. + * @param data_len: length of data buffer. + * @param str: string buffer. + * @param str_len: length of string buffer. + * @return number of characters (except null) needed to print. + * Can return -1 on failure. + */ +int gldns_wire2str_nsec3_next_owner_scan(uint8_t** data, size_t* data_len, + char** str, size_t* str_len); + +/** + * Scan wireformat ILNP64 field to string, with user buffers. + * It shifts the arguments to move along (see gldns_wire2str_pkt_scan). + * @param data: wireformat data. + * @param data_len: length of data buffer. + * @param str: string buffer. + * @param str_len: length of string buffer. + * @return number of characters (except null) needed to print. + * Can return -1 on failure. + */ +int gldns_wire2str_ilnp64_scan(uint8_t** data, size_t* data_len, char** str, + size_t* str_len); + +/** + * Scan wireformat EUI48 field to string, with user buffers. + * It shifts the arguments to move along (see gldns_wire2str_pkt_scan). + * @param data: wireformat data. + * @param data_len: length of data buffer. + * @param str: string buffer. + * @param str_len: length of string buffer. + * @return number of characters (except null) needed to print. + * Can return -1 on failure. + */ +int gldns_wire2str_eui48_scan(uint8_t** data, size_t* data_len, char** str, + size_t* str_len); + +/** + * Scan wireformat EUI64 field to string, with user buffers. + * It shifts the arguments to move along (see gldns_wire2str_pkt_scan). + * @param data: wireformat data. + * @param data_len: length of data buffer. + * @param str: string buffer. + * @param str_len: length of string buffer. + * @return number of characters (except null) needed to print. + * Can return -1 on failure. + */ +int gldns_wire2str_eui64_scan(uint8_t** data, size_t* data_len, char** str, + size_t* str_len); + +/** + * Scan wireformat TAG field to string, with user buffers. + * It shifts the arguments to move along (see gldns_wire2str_pkt_scan). + * @param data: wireformat data. + * @param data_len: length of data buffer. + * @param str: string buffer. + * @param str_len: length of string buffer. + * @return number of characters (except null) needed to print. + * Can return -1 on failure. + */ +int gldns_wire2str_tag_scan(uint8_t** data, size_t* data_len, char** str, + size_t* str_len); + +/** + * Scan wireformat long_str field to string, with user buffers. + * It shifts the arguments to move along (see gldns_wire2str_pkt_scan). + * @param data: wireformat data. + * @param data_len: length of data buffer. + * @param str: string buffer. + * @param str_len: length of string buffer. + * @return number of characters (except null) needed to print. + * Can return -1 on failure. + */ +int gldns_wire2str_long_str_scan(uint8_t** data, size_t* data_len, char** str, + size_t* str_len); + +/** + * Print EDNS LLQ option data to string. User buffers, moves string pointers. + * @param str: string buffer. + * @param str_len: length of string buffer. + * @param option_data: buffer with EDNS option code data. + * @param option_len: length of the data for this option. + * @return number of characters (except null) needed to print. + */ +int gldns_wire2str_edns_llq_print(char** str, size_t* str_len, + uint8_t* option_data, size_t option_len); + +/** + * Print EDNS UL option data to string. User buffers, moves string pointers. + * @param str: string buffer. + * @param str_len: length of string buffer. + * @param option_data: buffer with EDNS option code data. + * @param option_len: length of the data for this option. + * @return number of characters (except null) needed to print. + */ +int gldns_wire2str_edns_ul_print(char** str, size_t* str_len, + uint8_t* option_data, size_t option_len); + +/** + * Print EDNS NSID option data to string. User buffers, moves string pointers. + * @param str: string buffer. + * @param str_len: length of string buffer. + * @param option_data: buffer with EDNS option code data. + * @param option_len: length of the data for this option. + * @return number of characters (except null) needed to print. + */ +int gldns_wire2str_edns_nsid_print(char** str, size_t* str_len, + uint8_t* option_data, size_t option_len); + +/** + * Print EDNS DAU option data to string. User buffers, moves string pointers. + * @param str: string buffer. + * @param str_len: length of string buffer. + * @param option_data: buffer with EDNS option code data. + * @param option_len: length of the data for this option. + * @return number of characters (except null) needed to print. + */ +int gldns_wire2str_edns_dau_print(char** str, size_t* str_len, + uint8_t* option_data, size_t option_len); + +/** + * Print EDNS DHU option data to string. User buffers, moves string pointers. + * @param str: string buffer. + * @param str_len: length of string buffer. + * @param option_data: buffer with EDNS option code data. + * @param option_len: length of the data for this option. + * @return number of characters (except null) needed to print. + */ +int gldns_wire2str_edns_dhu_print(char** str, size_t* str_len, + uint8_t* option_data, size_t option_len); + +/** + * Print EDNS N3U option data to string. User buffers, moves string pointers. + * @param str: string buffer. + * @param str_len: length of string buffer. + * @param option_data: buffer with EDNS option code data. + * @param option_len: length of the data for this option. + * @return number of characters (except null) needed to print. + */ +int gldns_wire2str_edns_n3u_print(char** str, size_t* str_len, + uint8_t* option_data, size_t option_len); + +/** + * Print EDNS SUBNET option data to string. User buffers, moves string pointers. + * @param str: string buffer. + * @param str_len: length of string buffer. + * @param option_data: buffer with EDNS option code data. + * @param option_len: length of the data for this option. + * @return number of characters (except null) needed to print. + */ +int gldns_wire2str_edns_subnet_print(char** str, size_t* str_len, + uint8_t* option_data, size_t option_len); + +/** + * Print an EDNS option as OPT: VALUE. User buffers, moves string pointers. + * @param str: string buffer. + * @param str_len: length of string buffer. + * @param option_code: host format EDNS option code. + * @param option_data: buffer with EDNS option code data. + * @param option_len: length of the data for this option. + * @return number of characters (except null) needed to print. + */ +int gldns_wire2str_edns_option_print(char** str, size_t* str_len, + uint16_t option_code, uint8_t* option_data, size_t option_len); + +/** + * Scan wireformat EDNS OPT to string, with user buffers. + * It shifts the arguments to move along (see gldns_wire2str_pkt_scan). + * @param data: wireformat data. + * @param data_len: length of data buffer. + * @param str: string buffer. + * @param str_len: length of string buffer. + * @param pkt: packet with header and other info (may be NULL) + * @param pktlen: length of packet buffer. + * @return number of characters (except null) needed to print. + */ +int gldns_wire2str_edns_scan(uint8_t** data, size_t* data_len, char** str, + size_t* str_len, uint8_t* pkt, size_t pktlen); + +#ifdef __cplusplus +} +#endif + +#endif /* GLDNS_WIRE2STR_H */