1266077Sdes/*
2266077Sdes * wire2str.c
3266077Sdes *
4266077Sdes * conversion routines from the wire format
5266077Sdes * to the presentation format (strings)
6266077Sdes *
7266077Sdes * (c) NLnet Labs, 2004-2006
8266077Sdes *
9266077Sdes * See the file LICENSE for the license
10266077Sdes */
11266077Sdes/**
12266077Sdes * \file
13266077Sdes *
14266077Sdes * Contains functions to translate the wireformat to text
15266077Sdes * representation, as well as functions to print them.
16266077Sdes */
17266077Sdes#include "config.h"
18287915Sdes#include "sldns/wire2str.h"
19287915Sdes#include "sldns/str2wire.h"
20287915Sdes#include "sldns/rrdef.h"
21287915Sdes#include "sldns/pkthdr.h"
22287915Sdes#include "sldns/parseutil.h"
23287915Sdes#include "sldns/sbuffer.h"
24287915Sdes#include "sldns/keyraw.h"
25266077Sdes#ifdef HAVE_TIME_H
26266077Sdes#include <time.h>
27266077Sdes#endif
28266077Sdes#include <sys/time.h>
29266077Sdes#include <stdarg.h>
30266077Sdes#include <ctype.h>
31266077Sdes#ifdef HAVE_NETDB_H
32266077Sdes#include <netdb.h>
33266077Sdes#endif
34266077Sdes
35266077Sdes/* lookup tables for standard DNS stuff  */
36266077Sdes/* Taken from RFC 2535, section 7.  */
37266077Sdesstatic sldns_lookup_table sldns_algorithms_data[] = {
38266077Sdes	{ LDNS_RSAMD5, "RSAMD5" },
39266077Sdes	{ LDNS_DH, "DH" },
40266077Sdes	{ LDNS_DSA, "DSA" },
41266077Sdes	{ LDNS_ECC, "ECC" },
42266077Sdes	{ LDNS_RSASHA1, "RSASHA1" },
43266077Sdes	{ LDNS_DSA_NSEC3, "DSA-NSEC3-SHA1" },
44266077Sdes	{ LDNS_RSASHA1_NSEC3, "RSASHA1-NSEC3-SHA1" },
45266077Sdes	{ LDNS_RSASHA256, "RSASHA256"},
46266077Sdes	{ LDNS_RSASHA512, "RSASHA512"},
47266077Sdes	{ LDNS_ECC_GOST, "ECC-GOST"},
48266077Sdes	{ LDNS_ECDSAP256SHA256, "ECDSAP256SHA256"},
49266077Sdes	{ LDNS_ECDSAP384SHA384, "ECDSAP384SHA384"},
50266077Sdes	{ LDNS_INDIRECT, "INDIRECT" },
51266077Sdes	{ LDNS_PRIVATEDNS, "PRIVATEDNS" },
52266077Sdes	{ LDNS_PRIVATEOID, "PRIVATEOID" },
53266077Sdes	{ 0, NULL }
54266077Sdes};
55266077Sdessldns_lookup_table* sldns_algorithms = sldns_algorithms_data;
56266077Sdes
57266077Sdes/* hash algorithms in DS record */
58266077Sdesstatic sldns_lookup_table sldns_hashes_data[] = {
59266077Sdes	{ LDNS_SHA1, "SHA1" },
60266077Sdes	{ LDNS_SHA256, "SHA256" },
61266077Sdes	{ LDNS_HASH_GOST, "HASH-GOST" },
62266077Sdes	{ LDNS_SHA384, "SHA384" },
63266077Sdes	{ 0, NULL }
64266077Sdes};
65266077Sdessldns_lookup_table* sldns_hashes = sldns_hashes_data;
66266077Sdes
67266077Sdes/* Taken from RFC 4398  */
68266077Sdesstatic sldns_lookup_table sldns_cert_algorithms_data[] = {
69266077Sdes	{ LDNS_CERT_PKIX, "PKIX" },
70266077Sdes	{ LDNS_CERT_SPKI, "SPKI" },
71266077Sdes	{ LDNS_CERT_PGP, "PGP" },
72266077Sdes	{ LDNS_CERT_IPKIX, "IPKIX" },
73266077Sdes	{ LDNS_CERT_ISPKI, "ISPKI" },
74266077Sdes	{ LDNS_CERT_IPGP, "IPGP" },
75266077Sdes	{ LDNS_CERT_ACPKIX, "ACPKIX" },
76266077Sdes	{ LDNS_CERT_IACPKIX, "IACPKIX" },
77266077Sdes	{ LDNS_CERT_URI, "URI" },
78266077Sdes	{ LDNS_CERT_OID, "OID" },
79266077Sdes	{ 0, NULL }
80266077Sdes};
81266077Sdessldns_lookup_table* sldns_cert_algorithms = sldns_cert_algorithms_data;
82266077Sdes
83266077Sdes/* if these are used elsewhere */
84266077Sdesstatic sldns_lookup_table sldns_rcodes_data[] = {
85266077Sdes	{ LDNS_RCODE_NOERROR, "NOERROR" },
86266077Sdes	{ LDNS_RCODE_FORMERR, "FORMERR" },
87266077Sdes	{ LDNS_RCODE_SERVFAIL, "SERVFAIL" },
88266077Sdes	{ LDNS_RCODE_NXDOMAIN, "NXDOMAIN" },
89266077Sdes	{ LDNS_RCODE_NOTIMPL, "NOTIMPL" },
90266077Sdes	{ LDNS_RCODE_REFUSED, "REFUSED" },
91266077Sdes	{ LDNS_RCODE_YXDOMAIN, "YXDOMAIN" },
92266077Sdes	{ LDNS_RCODE_YXRRSET, "YXRRSET" },
93266077Sdes	{ LDNS_RCODE_NXRRSET, "NXRRSET" },
94266077Sdes	{ LDNS_RCODE_NOTAUTH, "NOTAUTH" },
95266077Sdes	{ LDNS_RCODE_NOTZONE, "NOTZONE" },
96266077Sdes	{ 0, NULL }
97266077Sdes};
98266077Sdessldns_lookup_table* sldns_rcodes = sldns_rcodes_data;
99266077Sdes
100266077Sdesstatic sldns_lookup_table sldns_opcodes_data[] = {
101266077Sdes	{ LDNS_PACKET_QUERY, "QUERY" },
102266077Sdes	{ LDNS_PACKET_IQUERY, "IQUERY" },
103266077Sdes	{ LDNS_PACKET_STATUS, "STATUS" },
104266077Sdes	{ LDNS_PACKET_NOTIFY, "NOTIFY" },
105266077Sdes	{ LDNS_PACKET_UPDATE, "UPDATE" },
106266077Sdes	{ 0, NULL }
107266077Sdes};
108266077Sdessldns_lookup_table* sldns_opcodes = sldns_opcodes_data;
109266077Sdes
110266077Sdesstatic sldns_lookup_table sldns_wireparse_errors_data[] = {
111266077Sdes	{ LDNS_WIREPARSE_ERR_OK, "no parse error" },
112266077Sdes	{ LDNS_WIREPARSE_ERR_GENERAL, "parse error" },
113266077Sdes	{ LDNS_WIREPARSE_ERR_DOMAINNAME_OVERFLOW, "Domainname length overflow" },
114266077Sdes	{ LDNS_WIREPARSE_ERR_DOMAINNAME_UNDERFLOW, "Domainname length underflow (zero length)" },
115266077Sdes	{ LDNS_WIREPARSE_ERR_BUFFER_TOO_SMALL, "buffer too small" },
116266077Sdes	{ LDNS_WIREPARSE_ERR_LABEL_OVERFLOW, "Label length overflow" },
117266077Sdes	{ LDNS_WIREPARSE_ERR_EMPTY_LABEL, "Empty label" },
118266077Sdes	{ LDNS_WIREPARSE_ERR_SYNTAX_BAD_ESCAPE, "Syntax error, bad escape sequence" },
119266077Sdes	{ LDNS_WIREPARSE_ERR_SYNTAX, "Syntax error, could not parse the RR" },
120266077Sdes	{ LDNS_WIREPARSE_ERR_SYNTAX_TTL, "Syntax error, could not parse the RR's TTL" },
121266077Sdes	{ LDNS_WIREPARSE_ERR_SYNTAX_TYPE, "Syntax error, could not parse the RR's type" },
122266077Sdes	{ LDNS_WIREPARSE_ERR_SYNTAX_CLASS, "Syntax error, could not parse the RR's class" },
123266077Sdes	{ LDNS_WIREPARSE_ERR_SYNTAX_RDATA, "Syntax error, could not parse the RR's rdata" },
124266077Sdes	{ LDNS_WIREPARSE_ERR_SYNTAX_MISSING_VALUE, "Syntax error, value expected" },
125266077Sdes	{ LDNS_WIREPARSE_ERR_INVALID_STR, "Conversion error, string expected" },
126266077Sdes	{ LDNS_WIREPARSE_ERR_SYNTAX_B64, "Conversion error, b64 encoding expected" },
127266077Sdes	{ LDNS_WIREPARSE_ERR_SYNTAX_B32_EXT, "Conversion error, b32 ext encoding expected" },
128266077Sdes	{ LDNS_WIREPARSE_ERR_SYNTAX_HEX, "Conversion error, hex encoding expected" },
129266077Sdes	{ LDNS_WIREPARSE_ERR_CERT_BAD_ALGORITHM, "Bad algorithm type for CERT record" },
130266077Sdes	{ LDNS_WIREPARSE_ERR_SYNTAX_TIME, "Conversion error, time encoding expected" },
131266077Sdes	{ LDNS_WIREPARSE_ERR_SYNTAX_PERIOD, "Conversion error, time period encoding expected" },
132266077Sdes	{ LDNS_WIREPARSE_ERR_SYNTAX_ILNP64, "Conversion error, 4 colon separated hex numbers expected" },
133266077Sdes	{ LDNS_WIREPARSE_ERR_SYNTAX_EUI48,
134266077Sdes		"Conversion error, 6 two character hex numbers "
135266077Sdes		"separated by dashes expected (i.e. xx-xx-xx-xx-xx-xx" },
136266077Sdes	{ LDNS_WIREPARSE_ERR_SYNTAX_EUI64,
137266077Sdes		"Conversion error, 8 two character hex numbers "
138266077Sdes		"separated by dashes expected (i.e. xx-xx-xx-xx-xx-xx-xx-xx" },
139266077Sdes	{ LDNS_WIREPARSE_ERR_SYNTAX_TAG,
140266077Sdes		"Conversion error, a non-zero sequence of US-ASCII letters "
141266077Sdes		"and numbers in lower case expected" },
142266077Sdes	{ LDNS_WIREPARSE_ERR_NOT_IMPL, "not implemented" },
143266077Sdes	{ LDNS_WIREPARSE_ERR_SYNTAX_INT, "Conversion error, integer expected" },
144266077Sdes	{ LDNS_WIREPARSE_ERR_SYNTAX_IP4, "Conversion error, ip4 addr expected" },
145266077Sdes	{ LDNS_WIREPARSE_ERR_SYNTAX_IP6, "Conversion error, ip6 addr expected" },
146266077Sdes	{ LDNS_WIREPARSE_ERR_SYNTAX_INTEGER_OVERFLOW, "Syntax error, integer overflow" },
147266077Sdes	{ LDNS_WIREPARSE_ERR_INCLUDE, "$INCLUDE directive was seen in the zone" },
148266077Sdes	{ LDNS_WIREPARSE_ERR_PARENTHESIS, "Parse error, parenthesis mismatch" },
149266077Sdes	{ 0, NULL }
150266077Sdes};
151266077Sdessldns_lookup_table* sldns_wireparse_errors = sldns_wireparse_errors_data;
152266077Sdes
153266077Sdesstatic sldns_lookup_table sldns_edns_flags_data[] = {
154266077Sdes	{ 3600, "do"},
155266077Sdes	{ 0, NULL}
156266077Sdes};
157266077Sdessldns_lookup_table* sldns_edns_flags = sldns_edns_flags_data;
158266077Sdes
159266077Sdesstatic sldns_lookup_table sldns_edns_options_data[] = {
160266077Sdes	{ 1, "LLQ" },
161266077Sdes	{ 2, "UL" },
162266077Sdes	{ 3, "NSID" },
163266077Sdes	/* 4 draft-cheshire-edns0-owner-option */
164266077Sdes	{ 5, "DAU" },
165266077Sdes	{ 6, "DHU" },
166266077Sdes	{ 7, "N3U" },
167266077Sdes	{ 8, "edns-client-subnet" },
168266077Sdes	{ 0, NULL}
169266077Sdes};
170266077Sdessldns_lookup_table* sldns_edns_options = sldns_edns_options_data;
171266077Sdes
172266077Sdeschar* sldns_wire2str_pkt(uint8_t* data, size_t len)
173266077Sdes{
174266077Sdes	size_t slen = (size_t)sldns_wire2str_pkt_buf(data, len, NULL, 0);
175266077Sdes	char* result = (char*)malloc(slen+1);
176266077Sdes	if(!result) return NULL;
177266077Sdes	sldns_wire2str_pkt_buf(data, len, result, slen+1);
178266077Sdes	return result;
179266077Sdes}
180266077Sdes
181266077Sdeschar* sldns_wire2str_rr(uint8_t* rr, size_t len)
182266077Sdes{
183266077Sdes	size_t slen = (size_t)sldns_wire2str_rr_buf(rr, len, NULL, 0);
184266077Sdes	char* result = (char*)malloc(slen+1);
185266077Sdes	if(!result) return NULL;
186266077Sdes	sldns_wire2str_rr_buf(rr, len, result, slen+1);
187266077Sdes	return result;
188266077Sdes}
189266077Sdes
190266077Sdeschar* sldns_wire2str_type(uint16_t rrtype)
191266077Sdes{
192266077Sdes	char buf[16];
193266077Sdes	sldns_wire2str_type_buf(rrtype, buf, sizeof(buf));
194266077Sdes	return strdup(buf);
195266077Sdes}
196266077Sdes
197266077Sdeschar* sldns_wire2str_class(uint16_t rrclass)
198266077Sdes{
199266077Sdes	char buf[16];
200266077Sdes	sldns_wire2str_class_buf(rrclass, buf, sizeof(buf));
201266077Sdes	return strdup(buf);
202266077Sdes}
203266077Sdes
204266077Sdeschar* sldns_wire2str_dname(uint8_t* dname, size_t dname_len)
205266077Sdes{
206266077Sdes	size_t slen=(size_t)sldns_wire2str_dname_buf(dname, dname_len, NULL, 0);
207266077Sdes	char* result = (char*)malloc(slen+1);
208266077Sdes	if(!result) return NULL;
209266077Sdes	sldns_wire2str_dname_buf(dname, dname_len, result, slen+1);
210266077Sdes	return result;
211266077Sdes}
212266077Sdes
213266077Sdeschar* sldns_wire2str_rcode(int rcode)
214266077Sdes{
215266077Sdes	char buf[16];
216266077Sdes	sldns_wire2str_rcode_buf(rcode, buf, sizeof(buf));
217266077Sdes	return strdup(buf);
218266077Sdes}
219266077Sdes
220266077Sdesint sldns_wire2str_pkt_buf(uint8_t* d, size_t dlen, char* s, size_t slen)
221266077Sdes{
222266077Sdes	/* use arguments as temporary variables */
223266077Sdes	return sldns_wire2str_pkt_scan(&d, &dlen, &s, &slen);
224266077Sdes}
225266077Sdes
226266077Sdesint sldns_wire2str_rr_buf(uint8_t* d, size_t dlen, char* s, size_t slen)
227266077Sdes{
228266077Sdes	/* use arguments as temporary variables */
229266077Sdes	return sldns_wire2str_rr_scan(&d, &dlen, &s, &slen, NULL, 0);
230266077Sdes}
231266077Sdes
232266077Sdesint sldns_wire2str_rdata_buf(uint8_t* rdata, size_t rdata_len, char* str,
233266077Sdes	size_t str_len, uint16_t rrtype)
234266077Sdes{
235266077Sdes	/* use arguments as temporary variables */
236266077Sdes	return sldns_wire2str_rdata_scan(&rdata, &rdata_len, &str, &str_len,
237266077Sdes		rrtype, NULL, 0);
238266077Sdes}
239266077Sdes
240266077Sdesint sldns_wire2str_rr_unknown_buf(uint8_t* d, size_t dlen, char* s, size_t slen)
241266077Sdes{
242266077Sdes	/* use arguments as temporary variables */
243266077Sdes	return sldns_wire2str_rr_unknown_scan(&d, &dlen, &s, &slen, NULL, 0);
244266077Sdes}
245266077Sdes
246266077Sdesint sldns_wire2str_rr_comment_buf(uint8_t* rr, size_t rrlen, size_t dname_len,
247266077Sdes	char* s, size_t slen)
248266077Sdes{
249266077Sdes	uint16_t rrtype = sldns_wirerr_get_type(rr, rrlen, dname_len);
250266077Sdes	return sldns_wire2str_rr_comment_print(&s, &slen, rr, rrlen, dname_len,
251266077Sdes		rrtype);
252266077Sdes}
253266077Sdes
254266077Sdesint sldns_wire2str_type_buf(uint16_t rrtype, char* s, size_t slen)
255266077Sdes{
256266077Sdes	/* use arguments as temporary variables */
257266077Sdes	return sldns_wire2str_type_print(&s, &slen, rrtype);
258266077Sdes}
259266077Sdes
260266077Sdesint sldns_wire2str_class_buf(uint16_t rrclass, char* s, size_t slen)
261266077Sdes{
262266077Sdes	/* use arguments as temporary variables */
263266077Sdes	return sldns_wire2str_class_print(&s, &slen, rrclass);
264266077Sdes}
265266077Sdes
266266077Sdesint sldns_wire2str_rcode_buf(int rcode, char* s, size_t slen)
267266077Sdes{
268266077Sdes	/* use arguments as temporary variables */
269266077Sdes	return sldns_wire2str_rcode_print(&s, &slen, rcode);
270266077Sdes}
271266077Sdes
272266077Sdesint sldns_wire2str_dname_buf(uint8_t* d, size_t dlen, char* s, size_t slen)
273266077Sdes{
274266077Sdes	/* use arguments as temporary variables */
275266077Sdes	return sldns_wire2str_dname_scan(&d, &dlen, &s, &slen, NULL, 0);
276266077Sdes}
277266077Sdes
278266077Sdesint sldns_str_vprint(char** str, size_t* slen, const char* format, va_list args)
279266077Sdes{
280266077Sdes	int w = vsnprintf(*str, *slen, format, args);
281266077Sdes	if(w < 0) {
282266077Sdes		/* error in printout */
283266077Sdes		return 0;
284266077Sdes	} else if((size_t)w >= *slen) {
285266077Sdes		*str = NULL; /* we do not want str to point outside of buffer*/
286266077Sdes		*slen = 0;
287266077Sdes	} else {
288266077Sdes		*str += w;
289266077Sdes		*slen -= w;
290266077Sdes	}
291266077Sdes	return w;
292266077Sdes}
293266077Sdes
294266077Sdesint sldns_str_print(char** str, size_t* slen, const char* format, ...)
295266077Sdes{
296266077Sdes	int w;
297266077Sdes	va_list args;
298266077Sdes	va_start(args, format);
299266077Sdes	w = sldns_str_vprint(str, slen, format, args);
300266077Sdes	va_end(args);
301266077Sdes	return w;
302266077Sdes}
303266077Sdes
304266077Sdes/** print hex format into text buffer for specified length */
305266077Sdesstatic int print_hex_buf(char** s, size_t* slen, uint8_t* buf, size_t len)
306266077Sdes{
307266077Sdes	const char* hex = "0123456789ABCDEF";
308266077Sdes	size_t i;
309266077Sdes	for(i=0; i<len; i++) {
310266077Sdes		(void)sldns_str_print(s, slen, "%c%c", hex[(buf[i]&0xf0)>>4],
311266077Sdes			hex[buf[i]&0x0f]);
312266077Sdes	}
313266077Sdes	return (int)len*2;
314266077Sdes}
315266077Sdes
316266077Sdes/** print remainder of buffer in hex format with prefixed text */
317266077Sdesstatic int print_remainder_hex(const char* pref, uint8_t** d, size_t* dlen,
318266077Sdes	char** s, size_t* slen)
319266077Sdes{
320266077Sdes	int w = 0;
321266077Sdes	w += sldns_str_print(s, slen, "%s", pref);
322266077Sdes	w += print_hex_buf(s, slen, *d, *dlen);
323266077Sdes	*d += *dlen;
324266077Sdes	*dlen = 0;
325266077Sdes	return w;
326266077Sdes}
327266077Sdes
328266077Sdesint sldns_wire2str_pkt_scan(uint8_t** d, size_t* dlen, char** s, size_t* slen)
329266077Sdes{
330266077Sdes	int w = 0;
331266077Sdes	unsigned qdcount, ancount, nscount, arcount, i;
332266077Sdes	uint8_t* pkt = *d;
333266077Sdes	size_t pktlen = *dlen;
334266077Sdes	if(*dlen >= LDNS_HEADER_SIZE) {
335266077Sdes		qdcount = (unsigned)LDNS_QDCOUNT(*d);
336266077Sdes		ancount = (unsigned)LDNS_ANCOUNT(*d);
337266077Sdes		nscount = (unsigned)LDNS_NSCOUNT(*d);
338266077Sdes		arcount = (unsigned)LDNS_ARCOUNT(*d);
339266077Sdes	} else {
340266077Sdes		qdcount = ancount = nscount = arcount = 0;
341266077Sdes	}
342266077Sdes	w += sldns_wire2str_header_scan(d, dlen, s, slen);
343266077Sdes	w += sldns_str_print(s, slen, "\n");
344266077Sdes	w += sldns_str_print(s, slen, ";; QUESTION SECTION:\n");
345266077Sdes	for(i=0; i<qdcount; i++) {
346266077Sdes		w += sldns_wire2str_rrquestion_scan(d, dlen, s, slen,
347266077Sdes			pkt, pktlen);
348266077Sdes		if(!*dlen) break;
349266077Sdes	}
350266077Sdes	w += sldns_str_print(s, slen, "\n");
351266077Sdes	w += sldns_str_print(s, slen, ";; ANSWER SECTION:\n");
352266077Sdes	for(i=0; i<ancount; i++) {
353266077Sdes		w += sldns_wire2str_rr_scan(d, dlen, s, slen, pkt, pktlen);
354266077Sdes		if(!*dlen) break;
355266077Sdes	}
356266077Sdes	w += sldns_str_print(s, slen, "\n");
357266077Sdes	w += sldns_str_print(s, slen, ";; AUTHORITY SECTION:\n");
358266077Sdes	for(i=0; i<nscount; i++) {
359266077Sdes		w += sldns_wire2str_rr_scan(d, dlen, s, slen, pkt, pktlen);
360266077Sdes		if(!*dlen) break;
361266077Sdes	}
362266077Sdes	w += sldns_str_print(s, slen, "\n");
363266077Sdes	w += sldns_str_print(s, slen, ";; ADDITIONAL SECTION:\n");
364266077Sdes	for(i=0; i<arcount; i++) {
365266077Sdes		w += sldns_wire2str_rr_scan(d, dlen, s, slen, pkt, pktlen);
366266077Sdes		if(!*dlen) break;
367266077Sdes	}
368266077Sdes	/* other fields: WHEN(time), SERVER(IP) not available here. */
369266077Sdes	w += sldns_str_print(s, slen, ";; MSG SIZE  rcvd: %d\n", (int)pktlen);
370266077Sdes	if(*dlen > 0) {
371266077Sdes		w += print_remainder_hex(";; trailing garbage 0x",
372266077Sdes			d, dlen, s, slen);
373266077Sdes		w += sldns_str_print(s, slen, "\n");
374266077Sdes	}
375266077Sdes	return w;
376266077Sdes}
377266077Sdes
378266077Sdes/** scan type, class and ttl and printout, for rr */
379266077Sdesstatic int sldns_rr_tcttl_scan(uint8_t** d, size_t* dl, char** s, size_t* sl)
380266077Sdes{
381266077Sdes	int w = 0;
382266077Sdes	uint16_t t, c;
383266077Sdes	uint32_t ttl;
384266077Sdes	if(*dl < 8) {
385266077Sdes		if(*dl < 4)
386266077Sdes			return w + print_remainder_hex("; Error malformed 0x",
387266077Sdes				d, dl, s, sl);
388266077Sdes		/* these print values or 0x.. if none left */
389266077Sdes		t = sldns_read_uint16(*d);
390266077Sdes		c = sldns_read_uint16((*d)+2);
391266077Sdes		(*d)+=4;
392266077Sdes		(*dl)-=4;
393266077Sdes		w += sldns_wire2str_class_print(s, sl, c);
394266077Sdes		w += sldns_str_print(s, sl, "\t");
395266077Sdes		w += sldns_wire2str_type_print(s, sl, t);
396266077Sdes		if(*dl == 0)
397266077Sdes			return w + sldns_str_print(s, sl, "; Error no ttl");
398266077Sdes		return w + print_remainder_hex(
399266077Sdes			"; Error malformed ttl 0x", d, dl, s, sl);
400266077Sdes	}
401266077Sdes	t = sldns_read_uint16(*d);
402266077Sdes	c = sldns_read_uint16((*d)+2);
403266077Sdes	ttl = sldns_read_uint32((*d)+4);
404266077Sdes	(*d)+=8;
405266077Sdes	(*dl)-=8;
406266077Sdes	w += sldns_str_print(s, sl, "%lu\t", (unsigned long)ttl);
407266077Sdes	w += sldns_wire2str_class_print(s, sl, c);
408266077Sdes	w += sldns_str_print(s, sl, "\t");
409266077Sdes	w += sldns_wire2str_type_print(s, sl, t);
410266077Sdes	return w;
411266077Sdes}
412266077Sdes
413266077Sdesint sldns_wire2str_rr_scan(uint8_t** d, size_t* dlen, char** s, size_t* slen,
414266077Sdes	uint8_t* pkt, size_t pktlen)
415266077Sdes{
416266077Sdes	int w = 0;
417266077Sdes	uint8_t* rr = *d;
418266077Sdes	size_t rrlen = *dlen, dname_off, rdlen, ordlen;
419266077Sdes	uint16_t rrtype = 0;
420266077Sdes
421266077Sdes	if(*dlen >= 3 && (*d)[0]==0 &&
422266077Sdes		sldns_read_uint16((*d)+1)==LDNS_RR_TYPE_OPT) {
423266077Sdes		/* perform EDNS OPT processing */
424266077Sdes		return sldns_wire2str_edns_scan(d, dlen, s, slen, pkt, pktlen);
425266077Sdes	}
426266077Sdes
427266077Sdes	/* try to scan the rdata with pretty-printing, but if that fails, then
428266077Sdes	 * scan the rdata as an unknown RR type */
429266077Sdes	w += sldns_wire2str_dname_scan(d, dlen, s, slen, pkt, pktlen);
430266077Sdes	w += sldns_str_print(s, slen, "\t");
431266077Sdes	dname_off = rrlen-(*dlen);
432266077Sdes	if(*dlen == 4) {
433266077Sdes		/* like a question-RR */
434266077Sdes		uint16_t t = sldns_read_uint16(*d);
435266077Sdes		uint16_t c = sldns_read_uint16((*d)+2);
436266077Sdes		(*d)+=4;
437266077Sdes		(*dlen)-=4;
438266077Sdes		w += sldns_wire2str_class_print(s, slen, c);
439266077Sdes		w += sldns_str_print(s, slen, "\t");
440266077Sdes		w += sldns_wire2str_type_print(s, slen, t);
441266077Sdes		w += sldns_str_print(s, slen, " ; Error no ttl,rdata\n");
442266077Sdes		return w;
443266077Sdes	}
444266077Sdes	if(*dlen < 8) {
445266077Sdes		if(*dlen == 0)
446266077Sdes			return w + sldns_str_print(s, slen, ";Error missing RR\n");
447266077Sdes		w += print_remainder_hex(";Error partial RR 0x", d, dlen, s, slen);
448266077Sdes		return w + sldns_str_print(s, slen, "\n");
449266077Sdes	}
450266077Sdes	rrtype = sldns_read_uint16(*d);
451266077Sdes	w += sldns_rr_tcttl_scan(d, dlen, s, slen);
452266077Sdes	w += sldns_str_print(s, slen, "\t");
453266077Sdes
454266077Sdes	/* rdata */
455266077Sdes	if(*dlen < 2) {
456266077Sdes		if(*dlen == 0)
457266077Sdes			return w + sldns_str_print(s, slen, ";Error missing rdatalen\n");
458266077Sdes		w += print_remainder_hex(";Error missing rdatalen 0x",
459266077Sdes			d, dlen, s, slen);
460266077Sdes		return w + sldns_str_print(s, slen, "\n");
461266077Sdes	}
462266077Sdes	rdlen = sldns_read_uint16(*d);
463266077Sdes	ordlen = rdlen;
464266077Sdes	(*d)+=2;
465266077Sdes	(*dlen)-=2;
466266077Sdes	if(*dlen < rdlen) {
467266077Sdes		w += sldns_str_print(s, slen, "\\# %u ", (unsigned)rdlen);
468266077Sdes		if(*dlen == 0)
469266077Sdes			return w + sldns_str_print(s, slen, ";Error missing rdata\n");
470266077Sdes		w += print_remainder_hex(";Error partial rdata 0x", d, dlen, s, slen);
471266077Sdes		return w + sldns_str_print(s, slen, "\n");
472266077Sdes	}
473266077Sdes	w += sldns_wire2str_rdata_scan(d, &rdlen, s, slen, rrtype, pkt, pktlen);
474266077Sdes	(*dlen) -= (ordlen-rdlen);
475266077Sdes
476266077Sdes	/* default comment */
477266077Sdes	w += sldns_wire2str_rr_comment_print(s, slen, rr, rrlen, dname_off,
478266077Sdes		rrtype);
479266077Sdes	w += sldns_str_print(s, slen, "\n");
480266077Sdes	return w;
481266077Sdes}
482266077Sdes
483266077Sdesint sldns_wire2str_rrquestion_scan(uint8_t** d, size_t* dlen, char** s,
484266077Sdes	size_t* slen, uint8_t* pkt, size_t pktlen)
485266077Sdes{
486266077Sdes	int w = 0;
487266077Sdes	uint16_t t, c;
488266077Sdes	w += sldns_wire2str_dname_scan(d, dlen, s, slen, pkt, pktlen);
489266077Sdes	w += sldns_str_print(s, slen, "\t");
490266077Sdes	if(*dlen < 4) {
491266077Sdes		if(*dlen == 0)
492266077Sdes			return w + sldns_str_print(s, slen, "Error malformed\n");
493266077Sdes		w += print_remainder_hex("Error malformed 0x", d, dlen, s, slen);
494266077Sdes		return w + sldns_str_print(s, slen, "\n");
495266077Sdes	}
496266077Sdes	t = sldns_read_uint16(*d);
497266077Sdes	c = sldns_read_uint16((*d)+2);
498266077Sdes	(*d)+=4;
499266077Sdes	(*dlen)-=4;
500266077Sdes	w += sldns_wire2str_class_print(s, slen, c);
501266077Sdes	w += sldns_str_print(s, slen, "\t");
502266077Sdes	w += sldns_wire2str_type_print(s, slen, t);
503266077Sdes	w += sldns_str_print(s, slen, "\n");
504266077Sdes	return w;
505266077Sdes}
506266077Sdes
507266077Sdesint sldns_wire2str_rr_unknown_scan(uint8_t** d, size_t* dlen, char** s,
508266077Sdes	size_t* slen, uint8_t* pkt, size_t pktlen)
509266077Sdes{
510266077Sdes	size_t rdlen, ordlen;
511266077Sdes	int w = 0;
512266077Sdes	w += sldns_wire2str_dname_scan(d, dlen, s, slen, pkt, pktlen);
513266077Sdes	w += sldns_str_print(s, slen, "\t");
514266077Sdes	w += sldns_rr_tcttl_scan(d, dlen, s, slen);
515266077Sdes	w += sldns_str_print(s, slen, "\t");
516266077Sdes	if(*dlen < 2) {
517266077Sdes		if(*dlen == 0)
518266077Sdes			return w + sldns_str_print(s, slen, ";Error missing rdatalen\n");
519266077Sdes		w += print_remainder_hex(";Error missing rdatalen 0x",
520266077Sdes			d, dlen, s, slen);
521266077Sdes		return w + sldns_str_print(s, slen, "\n");
522266077Sdes	}
523266077Sdes	rdlen = sldns_read_uint16(*d);
524266077Sdes	ordlen = rdlen;
525266077Sdes	(*d) += 2;
526266077Sdes	(*dlen) -= 2;
527266077Sdes	if(*dlen < rdlen) {
528266077Sdes		w += sldns_str_print(s, slen, "\\# %u ", (unsigned)rdlen);
529266077Sdes		if(*dlen == 0)
530266077Sdes			return w + sldns_str_print(s, slen, ";Error missing rdata\n");
531266077Sdes		w += print_remainder_hex(";Error partial rdata 0x", d, dlen, s, slen);
532266077Sdes		return w + sldns_str_print(s, slen, "\n");
533266077Sdes	}
534266077Sdes	w += sldns_wire2str_rdata_unknown_scan(d, &rdlen, s, slen);
535266077Sdes	(*dlen) -= (ordlen-rdlen);
536266077Sdes	w += sldns_str_print(s, slen, "\n");
537266077Sdes	return w;
538266077Sdes}
539266077Sdes
540266077Sdes/** print rr comment for type DNSKEY */
541266077Sdesstatic int rr_comment_dnskey(char** s, size_t* slen, uint8_t* rr,
542266077Sdes	size_t rrlen, size_t dname_off)
543266077Sdes{
544266077Sdes	size_t rdlen;
545266077Sdes	uint8_t* rdata;
546266077Sdes	int flags, w = 0;
547266077Sdes	if(rrlen < dname_off + 10) return 0;
548266077Sdes	rdlen = sldns_read_uint16(rr+dname_off+8);
549266077Sdes	if(rrlen < dname_off + 10 + rdlen) return 0;
550266077Sdes	rdata = rr + dname_off + 10;
551266077Sdes	flags = (int)sldns_read_uint16(rdata);
552266077Sdes	w += sldns_str_print(s, slen, " ;{");
553266077Sdes
554266077Sdes	/* id */
555266077Sdes	w += sldns_str_print(s, slen, "id = %u",
556266077Sdes		sldns_calc_keytag_raw(rdata, rdlen));
557266077Sdes
558266077Sdes	/* flags */
559266077Sdes	if((flags&LDNS_KEY_ZONE_KEY)) {
560266077Sdes		if((flags&LDNS_KEY_SEP_KEY))
561266077Sdes			w += sldns_str_print(s, slen, " (ksk)");
562266077Sdes		else 	w += sldns_str_print(s, slen, " (zsk)");
563266077Sdes	}
564266077Sdes
565266077Sdes	/* keysize */
566266077Sdes	if(rdlen > 4) {
567266077Sdes		w += sldns_str_print(s, slen, ", ");
568266077Sdes		w += sldns_str_print(s, slen, "size = %db",
569266077Sdes			(int)sldns_rr_dnskey_key_size_raw(
570266077Sdes			(unsigned char*)rdata+4, rdlen-4, (int)(rdata[3])));
571266077Sdes	}
572266077Sdes
573266077Sdes	w += sldns_str_print(s, slen, "}");
574266077Sdes	return w;
575266077Sdes}
576266077Sdes
577266077Sdes/** print rr comment for type RRSIG */
578266077Sdesstatic int rr_comment_rrsig(char** s, size_t* slen, uint8_t* rr,
579266077Sdes	size_t rrlen, size_t dname_off)
580266077Sdes{
581266077Sdes	size_t rdlen;
582266077Sdes	uint8_t* rdata;
583266077Sdes	if(rrlen < dname_off + 10) return 0;
584266077Sdes	rdlen = sldns_read_uint16(rr+dname_off+8);
585266077Sdes	if(rrlen < dname_off + 10 + rdlen) return 0;
586266077Sdes	rdata = rr + dname_off + 10;
587266077Sdes	if(rdlen < 18) return 0;
588266077Sdes	return sldns_str_print(s, slen, " ;{id = %d}",
589266077Sdes		(int)sldns_read_uint16(rdata+16));
590266077Sdes}
591266077Sdes
592266077Sdes/** print rr comment for type NSEC3 */
593266077Sdesstatic int rr_comment_nsec3(char** s, size_t* slen, uint8_t* rr,
594266077Sdes	size_t rrlen, size_t dname_off)
595266077Sdes{
596266077Sdes	size_t rdlen;
597266077Sdes	uint8_t* rdata;
598266077Sdes	int w = 0;
599266077Sdes	if(rrlen < dname_off + 10) return 0;
600266077Sdes	rdlen = sldns_read_uint16(rr+dname_off+8);
601266077Sdes	if(rrlen < dname_off + 10 + rdlen) return 0;
602266077Sdes	rdata = rr + dname_off + 10;
603266077Sdes	if(rdlen < 2) return 0;
604266077Sdes	if((rdata[1] & LDNS_NSEC3_VARS_OPTOUT_MASK))
605266077Sdes		w += sldns_str_print(s, slen, " ;{flags: optout}");
606266077Sdes	return w;
607266077Sdes}
608266077Sdes
609266077Sdesint sldns_wire2str_rr_comment_print(char** s, size_t* slen, uint8_t* rr,
610266077Sdes	size_t rrlen, size_t dname_off, uint16_t rrtype)
611266077Sdes{
612266077Sdes	if(rrtype == LDNS_RR_TYPE_DNSKEY) {
613266077Sdes		return rr_comment_dnskey(s, slen, rr, rrlen, dname_off);
614266077Sdes	} else if(rrtype == LDNS_RR_TYPE_RRSIG) {
615266077Sdes		return rr_comment_rrsig(s, slen, rr, rrlen, dname_off);
616266077Sdes	} else if(rrtype == LDNS_RR_TYPE_NSEC3) {
617266077Sdes		return rr_comment_nsec3(s, slen, rr, rrlen, dname_off);
618266077Sdes	}
619266077Sdes	return 0;
620266077Sdes}
621266077Sdes
622266077Sdesint sldns_wire2str_header_scan(uint8_t** d, size_t* dlen, char** s,
623266077Sdes	size_t* slen)
624266077Sdes{
625266077Sdes	int w = 0;
626266077Sdes	int opcode, rcode;
627266077Sdes	w += sldns_str_print(s, slen, ";; ->>HEADER<<- ");
628266077Sdes	if(*dlen == 0)
629266077Sdes		return w+sldns_str_print(s, slen, "Error empty packet");
630266077Sdes	if(*dlen < 4)
631266077Sdes		return w+print_remainder_hex("Error header too short 0x", d, dlen, s, slen);
632266077Sdes	opcode = (int)LDNS_OPCODE_WIRE(*d);
633266077Sdes	rcode = (int)LDNS_RCODE_WIRE(*d);
634266077Sdes	w += sldns_str_print(s, slen, "opcode: ");
635266077Sdes	w += sldns_wire2str_opcode_print(s, slen, opcode);
636266077Sdes	w += sldns_str_print(s, slen, ", ");
637266077Sdes	w += sldns_str_print(s, slen, "rcode: ");
638266077Sdes	w += sldns_wire2str_rcode_print(s, slen, rcode);
639266077Sdes	w += sldns_str_print(s, slen, ", ");
640266077Sdes	w += sldns_str_print(s, slen, "id: %d\n", (int)LDNS_ID_WIRE(*d));
641266077Sdes	w += sldns_str_print(s, slen, ";; flags:");
642266077Sdes	if(LDNS_QR_WIRE(*d)) w += sldns_str_print(s, slen, " qr");
643266077Sdes	if(LDNS_AA_WIRE(*d)) w += sldns_str_print(s, slen, " aa");
644266077Sdes	if(LDNS_TC_WIRE(*d)) w += sldns_str_print(s, slen, " tc");
645266077Sdes	if(LDNS_RD_WIRE(*d)) w += sldns_str_print(s, slen, " rd");
646266077Sdes	if(LDNS_CD_WIRE(*d)) w += sldns_str_print(s, slen, " cd");
647266077Sdes	if(LDNS_RA_WIRE(*d)) w += sldns_str_print(s, slen, " ra");
648266077Sdes	if(LDNS_AD_WIRE(*d)) w += sldns_str_print(s, slen, " ad");
649266077Sdes	if(LDNS_Z_WIRE(*d))  w += sldns_str_print(s, slen, " z");
650266077Sdes	w += sldns_str_print(s, slen, " ; ");
651266077Sdes	if(*dlen < LDNS_HEADER_SIZE)
652266077Sdes		return w+print_remainder_hex("Error header too short 0x", d, dlen, s, slen);
653266077Sdes	w += sldns_str_print(s, slen, "QUERY: %d, ", (int)LDNS_QDCOUNT(*d));
654266077Sdes	w += sldns_str_print(s, slen, "ANSWER: %d, ", (int)LDNS_ANCOUNT(*d));
655266077Sdes	w += sldns_str_print(s, slen, "AUTHORITY: %d, ", (int)LDNS_NSCOUNT(*d));
656266077Sdes	w += sldns_str_print(s, slen, "ADDITIONAL: %d ", (int)LDNS_ARCOUNT(*d));
657266077Sdes	*d += LDNS_HEADER_SIZE;
658266077Sdes	*dlen -= LDNS_HEADER_SIZE;
659266077Sdes	return w;
660266077Sdes}
661266077Sdes
662266077Sdesint sldns_wire2str_rdata_scan(uint8_t** d, size_t* dlen, char** s,
663266077Sdes	size_t* slen, uint16_t rrtype, uint8_t* pkt, size_t pktlen)
664266077Sdes{
665266077Sdes	/* try to prettyprint, but if that fails, use unknown format */
666266077Sdes	uint8_t* origd = *d;
667266077Sdes	char* origs = *s;
668266077Sdes	size_t origdlen = *dlen, origslen = *slen;
669266077Sdes	uint16_t r_cnt, r_max;
670266077Sdes	sldns_rdf_type rdftype;
671266077Sdes	int w = 0, n;
672266077Sdes
673266077Sdes	const sldns_rr_descriptor *desc = sldns_rr_descript(rrtype);
674266077Sdes	if(!desc) /* unknown format */
675266077Sdes		return sldns_wire2str_rdata_unknown_scan(d, dlen, s, slen);
676266077Sdes	/* dlen equals the rdatalen for the rdata */
677266077Sdes
678266077Sdes	r_max = sldns_rr_descriptor_maximum(desc);
679266077Sdes	for(r_cnt=0; r_cnt < r_max; r_cnt++) {
680266077Sdes		if(*dlen == 0) {
681266077Sdes			if(r_cnt < sldns_rr_descriptor_minimum(desc))
682266077Sdes				goto failed;
683266077Sdes			break; /* nothing more to print */
684266077Sdes		}
685266077Sdes		rdftype = sldns_rr_descriptor_field_type(desc, r_cnt);
686266077Sdes		if(r_cnt != 0)
687266077Sdes			w += sldns_str_print(s, slen, " ");
688266077Sdes		n = sldns_wire2str_rdf_scan(d, dlen, s, slen, rdftype,
689266077Sdes			pkt, pktlen);
690266077Sdes		if(n == -1) {
691266077Sdes		failed:
692266077Sdes			/* failed, use unknown format */
693266077Sdes			*d = origd; *s = origs;
694266077Sdes			*dlen = origdlen; *slen = origslen;
695266077Sdes			return sldns_wire2str_rdata_unknown_scan(d, dlen,
696266077Sdes				s, slen);
697266077Sdes		}
698266077Sdes		w += n;
699266077Sdes	}
700292206Sdes	if(*dlen != 0) {
701292206Sdes		goto failed;
702292206Sdes	}
703266077Sdes	return w;
704266077Sdes}
705266077Sdes
706266077Sdesint sldns_wire2str_rdata_unknown_scan(uint8_t** d, size_t* dlen, char** s,
707266077Sdes	size_t* slen)
708266077Sdes{
709266077Sdes	int w = 0;
710266077Sdes
711266077Sdes	/* print length */
712266077Sdes	w += sldns_str_print(s, slen, "\\# %u", (unsigned)*dlen);
713266077Sdes
714266077Sdes	/* print rdlen in hex */
715266077Sdes	if(*dlen != 0)
716266077Sdes		w += sldns_str_print(s, slen, " ");
717266077Sdes	w += print_hex_buf(s, slen, *d, *dlen);
718266077Sdes	(*d) += *dlen;
719266077Sdes	(*dlen) = 0;
720266077Sdes	return w;
721266077Sdes}
722266077Sdes
723266077Sdes/** print and escape one character for a domain dname */
724266077Sdesstatic int dname_char_print(char** s, size_t* slen, uint8_t c)
725266077Sdes{
726266077Sdes	if(c == '.' || c == ';' || c == '(' || c == ')' || c == '\\')
727266077Sdes		return sldns_str_print(s, slen, "\\%c", c);
728276541Sdes	else if(!(isascii((unsigned char)c) && isgraph((unsigned char)c)))
729266077Sdes		return sldns_str_print(s, slen, "\\%03u", (unsigned)c);
730266077Sdes	/* plain printout */
731266077Sdes	if(*slen) {
732266077Sdes		**s = (char)c;
733266077Sdes		(*s)++;
734266077Sdes		(*slen)--;
735266077Sdes	}
736266077Sdes	return 1;
737266077Sdes}
738266077Sdes
739266077Sdesint sldns_wire2str_dname_scan(uint8_t** d, size_t* dlen, char** s, size_t* slen,
740266077Sdes	uint8_t* pkt, size_t pktlen)
741266077Sdes{
742266077Sdes	int w = 0;
743266077Sdes	/* spool labels onto the string, use compression if its there */
744266077Sdes	uint8_t* pos = *d;
745266077Sdes	unsigned i, counter=0;
746266077Sdes	const unsigned maxcompr = 1000; /* loop detection, max compr ptrs */
747266077Sdes	int in_buf = 1;
748266077Sdes	if(*dlen == 0) return sldns_str_print(s, slen, "ErrorMissingDname");
749266077Sdes	if(*pos == 0) {
750266077Sdes		(*d)++;
751266077Sdes		(*dlen)--;
752266077Sdes		return sldns_str_print(s, slen, ".");
753266077Sdes	}
754266077Sdes	while(*pos) {
755266077Sdes		/* read label length */
756266077Sdes		uint8_t labellen = *pos++;
757266077Sdes		if(in_buf) { (*d)++; (*dlen)--; }
758266077Sdes
759266077Sdes		/* find out what sort of label we have */
760266077Sdes		if((labellen&0xc0) == 0xc0) {
761266077Sdes			/* compressed */
762266077Sdes			uint16_t target = 0;
763266077Sdes			if(in_buf && *dlen == 0)
764266077Sdes				return w + sldns_str_print(s, slen,
765266077Sdes					"ErrorPartialDname");
766266077Sdes			else if(!in_buf && pos+1 > pkt+pktlen)
767266077Sdes				return w + sldns_str_print(s, slen,
768266077Sdes					"ErrorPartialDname");
769266077Sdes			target = ((labellen&0x3f)<<8) | *pos;
770266077Sdes			if(in_buf) { (*d)++; (*dlen)--; }
771266077Sdes			/* move to target, if possible */
772266077Sdes			if(!pkt || target >= pktlen)
773266077Sdes				return w + sldns_str_print(s, slen,
774266077Sdes					"ErrorComprPtrOutOfBounds");
775266077Sdes			if(counter++ > maxcompr)
776266077Sdes				return w + sldns_str_print(s, slen,
777266077Sdes					"ErrorComprPtrLooped");
778266077Sdes			in_buf = 0;
779266077Sdes			pos = pkt+target;
780266077Sdes			continue;
781266077Sdes		} else if((labellen&0xc0)) {
782266077Sdes			/* notimpl label type */
783266077Sdes			w += sldns_str_print(s, slen,
784266077Sdes				"ErrorLABELTYPE%xIsUnknown",
785266077Sdes				(int)(labellen&0xc0));
786266077Sdes			return w;
787266077Sdes		}
788266077Sdes
789266077Sdes		/* spool label characters, end with '.' */
790266077Sdes		if(in_buf && *dlen < labellen) labellen = *dlen;
791266077Sdes		else if(!in_buf && pos+labellen > pkt+pktlen)
792266077Sdes			labellen = (uint8_t)(pkt + pktlen - pos);
793266077Sdes		for(i=0; i<(unsigned)labellen; i++) {
794266077Sdes			w += dname_char_print(s, slen, *pos++);
795266077Sdes		}
796266077Sdes		if(in_buf) {
797266077Sdes			(*d) += labellen;
798266077Sdes			(*dlen) -= labellen;
799266077Sdes			if(*dlen == 0) break;
800266077Sdes		}
801266077Sdes		w += sldns_str_print(s, slen, ".");
802266077Sdes	}
803266077Sdes	/* skip over final root label */
804266077Sdes	if(in_buf && *dlen > 0) { (*d)++; (*dlen)--; }
805266077Sdes	/* in case we printed no labels, terminate dname */
806266077Sdes	if(w == 0) w += sldns_str_print(s, slen, ".");
807266077Sdes	return w;
808266077Sdes}
809266077Sdes
810266077Sdesint sldns_wire2str_opcode_print(char** s, size_t* slen, int opcode)
811266077Sdes{
812266077Sdes	sldns_lookup_table *lt = sldns_lookup_by_id(sldns_opcodes, opcode);
813266077Sdes	if (lt && lt->name) {
814266077Sdes		return sldns_str_print(s, slen, "%s", lt->name);
815266077Sdes	}
816266077Sdes	return sldns_str_print(s, slen, "OPCODE%u", (unsigned)opcode);
817266077Sdes}
818266077Sdes
819266077Sdesint sldns_wire2str_rcode_print(char** s, size_t* slen, int rcode)
820266077Sdes{
821266077Sdes	sldns_lookup_table *lt = sldns_lookup_by_id(sldns_rcodes, rcode);
822266077Sdes	if (lt && lt->name) {
823266077Sdes		return sldns_str_print(s, slen, "%s", lt->name);
824266077Sdes	}
825266077Sdes	return sldns_str_print(s, slen, "RCODE%u", (unsigned)rcode);
826266077Sdes}
827266077Sdes
828266077Sdesint sldns_wire2str_class_print(char** s, size_t* slen, uint16_t rrclass)
829266077Sdes{
830266077Sdes	sldns_lookup_table *lt = sldns_lookup_by_id(sldns_rr_classes,
831266077Sdes		(int)rrclass);
832266077Sdes	if (lt && lt->name) {
833266077Sdes		return sldns_str_print(s, slen, "%s", lt->name);
834266077Sdes	}
835266077Sdes	return sldns_str_print(s, slen, "CLASS%u", (unsigned)rrclass);
836266077Sdes}
837266077Sdes
838266077Sdesint sldns_wire2str_type_print(char** s, size_t* slen, uint16_t rrtype)
839266077Sdes{
840266077Sdes	const sldns_rr_descriptor *descriptor = sldns_rr_descript(rrtype);
841266077Sdes	if (descriptor && descriptor->_name) {
842266077Sdes		return sldns_str_print(s, slen, "%s", descriptor->_name);
843266077Sdes	}
844266077Sdes	return sldns_str_print(s, slen, "TYPE%u", (unsigned)rrtype);
845266077Sdes}
846266077Sdes
847266077Sdesint sldns_wire2str_edns_option_code_print(char** s, size_t* slen,
848266077Sdes	uint16_t opcode)
849266077Sdes{
850266077Sdes	sldns_lookup_table *lt = sldns_lookup_by_id(sldns_edns_options,
851266077Sdes		(int)opcode);
852266077Sdes	if (lt && lt->name) {
853266077Sdes		return sldns_str_print(s, slen, "%s", lt->name);
854266077Sdes	}
855266077Sdes	return sldns_str_print(s, slen, "OPT%u", (unsigned)opcode);
856266077Sdes}
857266077Sdes
858266077Sdesint sldns_wire2str_class_scan(uint8_t** d, size_t* dlen, char** s, size_t* slen)
859266077Sdes{
860266077Sdes	uint16_t c;
861266077Sdes	if(*dlen == 0) return 0;
862266077Sdes	if(*dlen < 2) return print_remainder_hex("Error malformed 0x", d, dlen, s, slen);
863266077Sdes	c = sldns_read_uint16(*d);
864266077Sdes	(*d)+=2;
865266077Sdes	(*dlen)-=2;
866266077Sdes	return sldns_wire2str_class_print(s, slen, c);
867266077Sdes}
868266077Sdes
869266077Sdesint sldns_wire2str_type_scan(uint8_t** d, size_t* dlen, char** s, size_t* slen)
870266077Sdes{
871266077Sdes	uint16_t t;
872266077Sdes	if(*dlen == 0) return 0;
873266077Sdes	if(*dlen < 2) return print_remainder_hex("Error malformed 0x", d, dlen, s, slen);
874266077Sdes	t = sldns_read_uint16(*d);
875266077Sdes	(*d)+=2;
876266077Sdes	(*dlen)-=2;
877266077Sdes	return sldns_wire2str_type_print(s, slen, t);
878266077Sdes}
879266077Sdes
880266077Sdesint sldns_wire2str_ttl_scan(uint8_t** d, size_t* dlen, char** s, size_t* slen)
881266077Sdes{
882266077Sdes	uint32_t ttl;
883266077Sdes	if(*dlen == 0) return 0;
884266077Sdes	if(*dlen < 4) return print_remainder_hex("Error malformed 0x", d, dlen, s, slen);
885266077Sdes	ttl = sldns_read_uint32(*d);
886266077Sdes	(*d)+=4;
887266077Sdes	(*dlen)-=4;
888266077Sdes	return sldns_str_print(s, slen, "%u", (unsigned)ttl);
889266077Sdes}
890266077Sdes
891266077Sdesint sldns_wire2str_rdf_scan(uint8_t** d, size_t* dlen, char** s, size_t* slen,
892266077Sdes	int rdftype, uint8_t* pkt, size_t pktlen)
893266077Sdes{
894266077Sdes	if(*dlen == 0) return 0;
895266077Sdes	switch(rdftype) {
896266077Sdes	case LDNS_RDF_TYPE_NONE:
897266077Sdes		return 0;
898266077Sdes	case LDNS_RDF_TYPE_DNAME:
899266077Sdes		return sldns_wire2str_dname_scan(d, dlen, s, slen, pkt, pktlen);
900266077Sdes	case LDNS_RDF_TYPE_INT8:
901266077Sdes		return sldns_wire2str_int8_scan(d, dlen, s, slen);
902266077Sdes	case LDNS_RDF_TYPE_INT16:
903266077Sdes		return sldns_wire2str_int16_scan(d, dlen, s, slen);
904266077Sdes	case LDNS_RDF_TYPE_INT32:
905266077Sdes		return sldns_wire2str_int32_scan(d, dlen, s, slen);
906266077Sdes	case LDNS_RDF_TYPE_PERIOD:
907266077Sdes		return sldns_wire2str_period_scan(d, dlen, s, slen);
908266077Sdes	case LDNS_RDF_TYPE_TSIGTIME:
909266077Sdes		return sldns_wire2str_tsigtime_scan(d, dlen, s, slen);
910266077Sdes	case LDNS_RDF_TYPE_A:
911266077Sdes		return sldns_wire2str_a_scan(d, dlen, s, slen);
912266077Sdes	case LDNS_RDF_TYPE_AAAA:
913266077Sdes		return sldns_wire2str_aaaa_scan(d, dlen, s, slen);
914266077Sdes	case LDNS_RDF_TYPE_STR:
915266077Sdes		return sldns_wire2str_str_scan(d, dlen, s, slen);
916266077Sdes	case LDNS_RDF_TYPE_APL:
917266077Sdes		return sldns_wire2str_apl_scan(d, dlen, s, slen);
918266077Sdes	case LDNS_RDF_TYPE_B32_EXT:
919266077Sdes		return sldns_wire2str_b32_ext_scan(d, dlen, s, slen);
920266077Sdes	case LDNS_RDF_TYPE_B64:
921266077Sdes		return sldns_wire2str_b64_scan(d, dlen, s, slen);
922266077Sdes	case LDNS_RDF_TYPE_HEX:
923266077Sdes		return sldns_wire2str_hex_scan(d, dlen, s, slen);
924266077Sdes	case LDNS_RDF_TYPE_NSEC:
925266077Sdes		return sldns_wire2str_nsec_scan(d, dlen, s, slen);
926266077Sdes	case LDNS_RDF_TYPE_NSEC3_SALT:
927266077Sdes		return sldns_wire2str_nsec3_salt_scan(d, dlen, s, slen);
928266077Sdes	case LDNS_RDF_TYPE_TYPE:
929266077Sdes		return sldns_wire2str_type_scan(d, dlen, s, slen);
930266077Sdes	case LDNS_RDF_TYPE_CLASS:
931266077Sdes		return sldns_wire2str_class_scan(d, dlen, s, slen);
932266077Sdes	case LDNS_RDF_TYPE_CERT_ALG:
933266077Sdes		return sldns_wire2str_cert_alg_scan(d, dlen, s, slen);
934266077Sdes	case LDNS_RDF_TYPE_ALG:
935266077Sdes		return sldns_wire2str_alg_scan(d, dlen, s, slen);
936266077Sdes	case LDNS_RDF_TYPE_UNKNOWN:
937266077Sdes		return sldns_wire2str_unknown_scan(d, dlen, s, slen);
938266077Sdes	case LDNS_RDF_TYPE_TIME:
939266077Sdes		return sldns_wire2str_time_scan(d, dlen, s, slen);
940266077Sdes	case LDNS_RDF_TYPE_LOC:
941266077Sdes		return sldns_wire2str_loc_scan(d, dlen, s, slen);
942266077Sdes	case LDNS_RDF_TYPE_WKS:
943266077Sdes	case LDNS_RDF_TYPE_SERVICE:
944266077Sdes		return sldns_wire2str_wks_scan(d, dlen, s, slen);
945266077Sdes	case LDNS_RDF_TYPE_NSAP:
946266077Sdes		return sldns_wire2str_nsap_scan(d, dlen, s, slen);
947266077Sdes	case LDNS_RDF_TYPE_ATMA:
948266077Sdes		return sldns_wire2str_atma_scan(d, dlen, s, slen);
949266077Sdes	case LDNS_RDF_TYPE_IPSECKEY:
950266077Sdes		return sldns_wire2str_ipseckey_scan(d, dlen, s, slen, pkt,
951266077Sdes			pktlen);
952266077Sdes	case LDNS_RDF_TYPE_HIP:
953266077Sdes		return sldns_wire2str_hip_scan(d, dlen, s, slen);
954266077Sdes	case LDNS_RDF_TYPE_INT16_DATA:
955266077Sdes		return sldns_wire2str_int16_data_scan(d, dlen, s, slen);
956266077Sdes	case LDNS_RDF_TYPE_NSEC3_NEXT_OWNER:
957266077Sdes		return sldns_wire2str_b32_ext_scan(d, dlen, s, slen);
958266077Sdes	case LDNS_RDF_TYPE_ILNP64:
959266077Sdes		return sldns_wire2str_ilnp64_scan(d, dlen, s, slen);
960266077Sdes	case LDNS_RDF_TYPE_EUI48:
961266077Sdes		return sldns_wire2str_eui48_scan(d, dlen, s, slen);
962266077Sdes	case LDNS_RDF_TYPE_EUI64:
963266077Sdes		return sldns_wire2str_eui64_scan(d, dlen, s, slen);
964266077Sdes	case LDNS_RDF_TYPE_TAG:
965266077Sdes		return sldns_wire2str_tag_scan(d, dlen, s, slen);
966266077Sdes	case LDNS_RDF_TYPE_LONG_STR:
967266077Sdes		return sldns_wire2str_long_str_scan(d, dlen, s, slen);
968266077Sdes	}
969266077Sdes	/* unknown rdf type */
970266077Sdes	return -1;
971266077Sdes}
972266077Sdes
973266077Sdesint sldns_wire2str_int8_scan(uint8_t** d, size_t* dl, char** s, size_t* sl)
974266077Sdes{
975266077Sdes	int w;
976266077Sdes	if(*dl < 1) return -1;
977266077Sdes	w = sldns_str_print(s, sl, "%u", (unsigned)**d);
978266077Sdes	(*d)++;
979266077Sdes	(*dl)--;
980266077Sdes	return w;
981266077Sdes}
982266077Sdes
983266077Sdesint sldns_wire2str_int16_scan(uint8_t** d, size_t* dl, char** s, size_t* sl)
984266077Sdes{
985266077Sdes	int w;
986266077Sdes	if(*dl < 2) return -1;
987266077Sdes	w = sldns_str_print(s, sl, "%lu", (unsigned long)sldns_read_uint16(*d));
988266077Sdes	(*d)+=2;
989266077Sdes	(*dl)-=2;
990266077Sdes	return w;
991266077Sdes}
992266077Sdes
993266077Sdesint sldns_wire2str_int32_scan(uint8_t** d, size_t* dl, char** s, size_t* sl)
994266077Sdes{
995266077Sdes	int w;
996266077Sdes	if(*dl < 4) return -1;
997266077Sdes	w = sldns_str_print(s, sl, "%lu", (unsigned long)sldns_read_uint32(*d));
998266077Sdes	(*d)+=4;
999266077Sdes	(*dl)-=4;
1000266077Sdes	return w;
1001266077Sdes}
1002266077Sdes
1003266077Sdesint sldns_wire2str_period_scan(uint8_t** d, size_t* dl, char** s, size_t* sl)
1004266077Sdes{
1005266077Sdes	int w;
1006266077Sdes	if(*dl < 4) return -1;
1007266077Sdes	w = sldns_str_print(s, sl, "%u", (unsigned)sldns_read_uint32(*d));
1008266077Sdes	(*d)+=4;
1009266077Sdes	(*dl)-=4;
1010266077Sdes	return w;
1011266077Sdes}
1012266077Sdes
1013266077Sdesint sldns_wire2str_tsigtime_scan(uint8_t** d, size_t* dl, char** s, size_t* sl)
1014266077Sdes{
1015266077Sdes	/* tsigtime is 48 bits network order unsigned integer */
1016266077Sdes	int w;
1017266077Sdes	uint64_t tsigtime = 0;
1018266077Sdes	uint64_t d0, d1, d2, d3, d4, d5;
1019266077Sdes	if(*dl < 6) return -1;
1020266077Sdes	d0 = (*d)[0]; /* cast to uint64 for shift operations */
1021266077Sdes	d1 = (*d)[1];
1022266077Sdes	d2 = (*d)[2];
1023266077Sdes	d3 = (*d)[3];
1024266077Sdes	d4 = (*d)[4];
1025266077Sdes	d5 = (*d)[5];
1026266077Sdes	tsigtime = (d0<<40) | (d1<<32) | (d2<<24) | (d3<<16) | (d4<<8) | d5;
1027266077Sdes#ifndef USE_WINSOCK
1028266077Sdes	w = sldns_str_print(s, sl, "%llu", (long long)tsigtime);
1029266077Sdes#else
1030266077Sdes	w = sldns_str_print(s, sl, "%I64u", (long long)tsigtime);
1031266077Sdes#endif
1032266077Sdes	(*d)+=6;
1033266077Sdes	(*dl)-=6;
1034266077Sdes	return w;
1035266077Sdes}
1036266077Sdes
1037266077Sdesint sldns_wire2str_a_scan(uint8_t** d, size_t* dl, char** s, size_t* sl)
1038266077Sdes{
1039266077Sdes	char buf[32];
1040266077Sdes	int w;
1041266077Sdes	if(*dl < 4) return -1;
1042266077Sdes	if(!inet_ntop(AF_INET, *d, buf, (socklen_t)sizeof(buf)))
1043266077Sdes		return -1;
1044266077Sdes	w = sldns_str_print(s, sl, "%s", buf);
1045266077Sdes	(*d)+=4;
1046266077Sdes	(*dl)-=4;
1047266077Sdes	return w;
1048266077Sdes}
1049266077Sdes
1050266077Sdesint sldns_wire2str_aaaa_scan(uint8_t** d, size_t* dl, char** s, size_t* sl)
1051266077Sdes{
1052266077Sdes#ifdef AF_INET6
1053266077Sdes	char buf[64];
1054266077Sdes	int w;
1055266077Sdes	if(*dl < 16) return -1;
1056266077Sdes	if(!inet_ntop(AF_INET6, *d, buf, (socklen_t)sizeof(buf)))
1057266077Sdes		return -1;
1058266077Sdes	w = sldns_str_print(s, sl, "%s", buf);
1059266077Sdes	(*d)+=16;
1060266077Sdes	(*dl)-=16;
1061266077Sdes	return w;
1062266077Sdes#else
1063266077Sdes	return -1;
1064266077Sdes#endif
1065266077Sdes}
1066266077Sdes
1067266077Sdes/** printout escaped TYPE_STR character */
1068266077Sdesstatic int str_char_print(char** s, size_t* sl, uint8_t c)
1069266077Sdes{
1070276541Sdes	if(isprint((unsigned char)c) || c == '\t') {
1071266077Sdes		if(c == '\"' || c == '\\')
1072266077Sdes			return sldns_str_print(s, sl, "\\%c", c);
1073266077Sdes		if(*sl) {
1074266077Sdes			**s = (char)c;
1075266077Sdes			(*s)++;
1076266077Sdes			(*sl)--;
1077266077Sdes		}
1078266077Sdes		return 1;
1079266077Sdes	}
1080266077Sdes	return sldns_str_print(s, sl, "\\%03u", (unsigned)c);
1081266077Sdes}
1082266077Sdes
1083266077Sdesint sldns_wire2str_str_scan(uint8_t** d, size_t* dl, char** s, size_t* sl)
1084266077Sdes{
1085266077Sdes	int w = 0;
1086266077Sdes	size_t i, len;
1087266077Sdes	if(*dl < 1) return -1;
1088266077Sdes	len = **d;
1089266077Sdes	if(*dl < 1+len) return -1;
1090266077Sdes	(*d)++;
1091266077Sdes	(*dl)--;
1092266077Sdes	w += sldns_str_print(s, sl, "\"");
1093266077Sdes	for(i=0; i<len; i++)
1094266077Sdes		w += str_char_print(s, sl, (*d)[i]);
1095266077Sdes	w += sldns_str_print(s, sl, "\"");
1096266077Sdes	(*d)+=len;
1097266077Sdes	(*dl)-=len;
1098266077Sdes	return w;
1099266077Sdes}
1100266077Sdes
1101266077Sdesint sldns_wire2str_apl_scan(uint8_t** d, size_t* dl, char** s, size_t* sl)
1102266077Sdes{
1103266077Sdes	int i, w = 0;
1104266077Sdes	uint16_t family;
1105266077Sdes	uint8_t negation, prefix, adflength;
1106266077Sdes	if(*dl < 4) return -1;
1107266077Sdes	family = sldns_read_uint16(*d);
1108266077Sdes	prefix = (*d)[2];
1109266077Sdes	negation = ((*d)[3] & LDNS_APL_NEGATION);
1110266077Sdes	adflength = ((*d)[3] & LDNS_APL_MASK);
1111266077Sdes	if(*dl < 4+(size_t)adflength) return -1;
1112266077Sdes	if(family != LDNS_APL_IP4 && family != LDNS_APL_IP6)
1113266077Sdes		return -1; /* unknown address family */
1114266077Sdes	if(negation)
1115266077Sdes		w += sldns_str_print(s, sl, "!");
1116266077Sdes	w += sldns_str_print(s, sl, "%u:", (unsigned)family);
1117266077Sdes	if(family == LDNS_APL_IP4) {
1118266077Sdes		/* check if prefix <32 ? */
1119266077Sdes		/* address is variable length 0 - 4 */
1120266077Sdes		for(i=0; i<4; i++) {
1121266077Sdes			if(i > 0)
1122266077Sdes				w += sldns_str_print(s, sl, ".");
1123266077Sdes			if(i < (int)adflength)
1124266077Sdes				w += sldns_str_print(s, sl, "%d", (*d)[4+i]);
1125266077Sdes			else	w += sldns_str_print(s, sl, "0");
1126266077Sdes		}
1127266077Sdes	} else if(family == LDNS_APL_IP6) {
1128266077Sdes		/* check if prefix <128 ? */
1129266077Sdes		/* address is variable length 0 - 16 */
1130266077Sdes		for(i=0; i<16; i++) {
1131266077Sdes			if(i%2 == 0 && i>0)
1132266077Sdes				w += sldns_str_print(s, sl, ":");
1133266077Sdes			if(i < (int)adflength)
1134266077Sdes				w += sldns_str_print(s, sl, "%02x", (*d)[4+i]);
1135266077Sdes			else	w += sldns_str_print(s, sl, "00");
1136266077Sdes		}
1137266077Sdes	}
1138266077Sdes	w += sldns_str_print(s, sl, "/%u", (unsigned)prefix);
1139266077Sdes	(*d) += 4+adflength;
1140266077Sdes	(*dl) -= 4+adflength;
1141266077Sdes	return w;
1142266077Sdes}
1143266077Sdes
1144266077Sdesint sldns_wire2str_b32_ext_scan(uint8_t** d, size_t* dl, char** s, size_t* sl)
1145266077Sdes{
1146266077Sdes	size_t datalen;
1147266077Sdes	size_t sz;
1148266077Sdes	if(*dl < 1) return -1;
1149266077Sdes	datalen = (*d)[0];
1150266077Sdes	if(*dl < 1+datalen) return -1;
1151266077Sdes	sz = sldns_b32_ntop_calculate_size(datalen);
1152266077Sdes	if(*sl < sz+1) {
1153266077Sdes		(*d) += datalen+1;
1154266077Sdes		(*dl) -= (datalen+1);
1155266077Sdes		return (int)sz; /* out of space really, but would need buffer
1156266077Sdes			in order to truncate the output */
1157266077Sdes	}
1158266077Sdes	sldns_b32_ntop_extended_hex((*d)+1, datalen, *s, *sl);
1159266077Sdes	(*d) += datalen+1;
1160266077Sdes	(*dl) -= (datalen+1);
1161266077Sdes	(*s) += sz;
1162266077Sdes	(*sl) -= sz;
1163266077Sdes	return (int)sz;
1164266077Sdes}
1165266077Sdes
1166266077Sdes/** scan number of bytes from wire into b64 presentation format */
1167266077Sdesstatic int sldns_wire2str_b64_scan_num(uint8_t** d, size_t* dl, char** s,
1168266077Sdes	size_t* sl, size_t num)
1169266077Sdes{
1170266077Sdes	/* b64_ntop_calculate size includes null at the end */
1171266077Sdes	size_t sz = sldns_b64_ntop_calculate_size(num)-1;
1172266077Sdes	if(*sl < sz+1) {
1173266077Sdes		(*d) += num;
1174266077Sdes		(*dl) -= num;
1175266077Sdes		return (int)sz; /* out of space really, but would need buffer
1176266077Sdes			in order to truncate the output */
1177266077Sdes	}
1178266077Sdes	sldns_b64_ntop(*d, num, *s, *sl);
1179266077Sdes	(*d) += num;
1180266077Sdes	(*dl) -= num;
1181266077Sdes	(*s) += sz;
1182266077Sdes	(*sl) -= sz;
1183266077Sdes	return (int)sz;
1184266077Sdes}
1185266077Sdes
1186266077Sdesint sldns_wire2str_b64_scan(uint8_t** d, size_t* dl, char** s, size_t* sl)
1187266077Sdes{
1188266077Sdes	return sldns_wire2str_b64_scan_num(d, dl, s, sl, *dl);
1189266077Sdes}
1190266077Sdes
1191266077Sdesint sldns_wire2str_hex_scan(uint8_t** d, size_t* dl, char** s, size_t* sl)
1192266077Sdes{
1193266077Sdes	return print_remainder_hex("", d, dl, s, sl);
1194266077Sdes}
1195266077Sdes
1196266077Sdesint sldns_wire2str_nsec_scan(uint8_t** d, size_t* dl, char** s, size_t* sl)
1197266077Sdes{
1198266077Sdes	uint8_t* p = *d;
1199266077Sdes	size_t pl = *dl;
1200266077Sdes	unsigned i, bit, window, block_len;
1201266077Sdes	uint16_t t;
1202266077Sdes	int w = 0;
1203266077Sdes
1204266077Sdes	/* check for errors */
1205266077Sdes	while(pl) {
1206266077Sdes		if(pl < 2) return -1;
1207266077Sdes		block_len = (unsigned)p[1];
1208266077Sdes		if(pl < 2+block_len) return -1;
1209266077Sdes		p += block_len+2;
1210266077Sdes		pl -= block_len+2;
1211266077Sdes	}
1212266077Sdes
1213266077Sdes	/* do it */
1214266077Sdes	p = *d;
1215266077Sdes	pl = *dl;
1216266077Sdes	while(pl) {
1217266077Sdes		if(pl < 2) return -1; /* cannot happen */
1218266077Sdes		window = (unsigned)p[0];
1219266077Sdes		block_len = (unsigned)p[1];
1220266077Sdes		if(pl < 2+block_len) return -1; /* cannot happen */
1221266077Sdes		p += 2;
1222266077Sdes		for(i=0; i<block_len; i++) {
1223266077Sdes			if(p[i] == 0) continue;
1224266077Sdes			/* base type number for this octet */
1225266077Sdes			t = ((window)<<8) | (i << 3);
1226266077Sdes			for(bit=0; bit<8; bit++) {
1227266077Sdes				if((p[i]&(0x80>>bit))) {
1228266077Sdes					if(w) w += sldns_str_print(s, sl, " ");
1229266077Sdes					w += sldns_wire2str_type_print(s, sl,
1230266077Sdes						t+bit);
1231266077Sdes				}
1232266077Sdes			}
1233266077Sdes		}
1234266077Sdes		p += block_len;
1235266077Sdes		pl -= block_len+2;
1236266077Sdes	}
1237266077Sdes	(*d) += *dl;
1238266077Sdes	(*dl) = 0;
1239266077Sdes	return w;
1240266077Sdes}
1241266077Sdes
1242266077Sdesint sldns_wire2str_nsec3_salt_scan(uint8_t** d, size_t* dl, char** s, size_t* sl)
1243266077Sdes{
1244266077Sdes	size_t salt_len;
1245266077Sdes	int w;
1246266077Sdes	if(*dl < 1) return -1;
1247266077Sdes	salt_len = (size_t)(*d)[0];
1248266077Sdes	if(*dl < 1+salt_len) return -1;
1249266077Sdes	(*d)++;
1250266077Sdes	(*dl)--;
1251266077Sdes	if(salt_len == 0) {
1252266077Sdes		return sldns_str_print(s, sl, "-");
1253266077Sdes	}
1254266077Sdes	w = print_hex_buf(s, sl, *d, salt_len);
1255266077Sdes	(*dl)-=salt_len;
1256266077Sdes	(*d)+=salt_len;
1257266077Sdes	return w;
1258266077Sdes}
1259266077Sdes
1260266077Sdesint sldns_wire2str_cert_alg_scan(uint8_t** d, size_t* dl, char** s, size_t* sl)
1261266077Sdes{
1262266077Sdes	sldns_lookup_table *lt;
1263266077Sdes	int data, w;
1264266077Sdes	if(*dl < 2) return -1;
1265266077Sdes	data = (int)sldns_read_uint16(*d);
1266266077Sdes	lt = sldns_lookup_by_id(sldns_cert_algorithms, data);
1267266077Sdes	if(lt && lt->name)
1268266077Sdes		w = sldns_str_print(s, sl, "%s", lt->name);
1269266077Sdes	else 	w = sldns_str_print(s, sl, "%d", data);
1270266077Sdes	(*dl)-=2;
1271266077Sdes	(*d)+=2;
1272266077Sdes	return w;
1273266077Sdes}
1274266077Sdes
1275266077Sdesint sldns_wire2str_alg_scan(uint8_t** d, size_t* dl, char** s, size_t* sl)
1276266077Sdes{
1277266077Sdes	/* don't use algorithm mnemonics in the presentation format
1278266077Sdes	 * this kind of got sneaked into the rfc's */
1279266077Sdes	return sldns_wire2str_int8_scan(d, dl, s, sl);
1280266077Sdes}
1281266077Sdes
1282266077Sdesint sldns_wire2str_unknown_scan(uint8_t** d, size_t* dl, char** s, size_t* sl)
1283266077Sdes{
1284266077Sdes	return sldns_wire2str_rdata_unknown_scan(d, dl, s, sl);
1285266077Sdes}
1286266077Sdes
1287266077Sdesint sldns_wire2str_time_scan(uint8_t** d, size_t* dl, char** s, size_t* sl)
1288266077Sdes{
1289266077Sdes	/* create a YYYYMMDDHHMMSS string if possible */
1290266077Sdes	struct tm tm;
1291266077Sdes	char date_buf[16];
1292266077Sdes	uint32_t t;
1293266077Sdes	memset(&tm, 0, sizeof(tm));
1294266077Sdes	if(*dl < 4) return -1;
1295266077Sdes	t = sldns_read_uint32(*d);
1296266077Sdes	date_buf[15]=0;
1297266077Sdes	if(sldns_serial_arithmitics_gmtime_r(t, time(NULL), &tm) &&
1298266077Sdes		strftime(date_buf, 15, "%Y%m%d%H%M%S", &tm)) {
1299266077Sdes		(*d) += 4;
1300266077Sdes		(*dl) -= 4;
1301266077Sdes		return sldns_str_print(s, sl, "%s", date_buf);
1302266077Sdes	}
1303266077Sdes	return -1;
1304266077Sdes}
1305266077Sdes
1306266077Sdesstatic int
1307266077Sdesloc_cm_print(char** str, size_t* sl, uint8_t mantissa, uint8_t exponent)
1308266077Sdes{
1309266077Sdes	int w = 0;
1310266077Sdes	uint8_t i;
1311266077Sdes	/* is it 0.<two digits> ? */
1312266077Sdes	if(exponent < 2) {
1313266077Sdes		if(exponent == 1)
1314266077Sdes			mantissa *= 10;
1315266077Sdes		return sldns_str_print(str, sl, "0.%02ld", (long)mantissa);
1316266077Sdes	}
1317266077Sdes	/* always <digit><string of zeros> */
1318266077Sdes	w += sldns_str_print(str, sl, "%d", (int)mantissa);
1319266077Sdes	for(i=0; i<exponent-2; i++)
1320266077Sdes		w += sldns_str_print(str, sl, "0");
1321266077Sdes	return w;
1322266077Sdes}
1323266077Sdes
1324266077Sdesint sldns_wire2str_loc_scan(uint8_t** d, size_t* dl, char** str, size_t* sl)
1325266077Sdes{
1326266077Sdes	/* we could do checking (ie degrees < 90 etc)? */
1327266077Sdes	uint8_t version;
1328266077Sdes	uint8_t size;
1329266077Sdes	uint8_t horizontal_precision;
1330266077Sdes	uint8_t vertical_precision;
1331266077Sdes	uint32_t longitude;
1332266077Sdes	uint32_t latitude;
1333266077Sdes	uint32_t altitude;
1334266077Sdes	char northerness;
1335266077Sdes	char easterness;
1336266077Sdes	uint32_t h;
1337266077Sdes	uint32_t m;
1338266077Sdes	double s;
1339266077Sdes	uint32_t equator = (uint32_t)1 << 31; /* 2**31 */
1340266077Sdes	int w = 0;
1341266077Sdes
1342266077Sdes	if(*dl < 16) return -1;
1343266077Sdes	version = (*d)[0];
1344266077Sdes	if(version != 0)
1345266077Sdes		return sldns_wire2str_hex_scan(d, dl, str, sl);
1346266077Sdes	size = (*d)[1];
1347266077Sdes	horizontal_precision = (*d)[2];
1348266077Sdes	vertical_precision = (*d)[3];
1349266077Sdes
1350266077Sdes	latitude = sldns_read_uint32((*d)+4);
1351266077Sdes	longitude = sldns_read_uint32((*d)+8);
1352266077Sdes	altitude = sldns_read_uint32((*d)+12);
1353266077Sdes
1354266077Sdes	if (latitude > equator) {
1355266077Sdes		northerness = 'N';
1356266077Sdes		latitude = latitude - equator;
1357266077Sdes	} else {
1358266077Sdes		northerness = 'S';
1359266077Sdes		latitude = equator - latitude;
1360266077Sdes	}
1361266077Sdes	h = latitude / (1000 * 60 * 60);
1362266077Sdes	latitude = latitude % (1000 * 60 * 60);
1363266077Sdes	m = latitude / (1000 * 60);
1364266077Sdes	latitude = latitude % (1000 * 60);
1365266077Sdes	s = (double) latitude / 1000.0;
1366266077Sdes	w += sldns_str_print(str, sl, "%02u %02u %06.3f %c ",
1367266077Sdes		h, m, s, northerness);
1368266077Sdes
1369266077Sdes	if (longitude > equator) {
1370266077Sdes		easterness = 'E';
1371266077Sdes		longitude = longitude - equator;
1372266077Sdes	} else {
1373266077Sdes		easterness = 'W';
1374266077Sdes		longitude = equator - longitude;
1375266077Sdes	}
1376266077Sdes	h = longitude / (1000 * 60 * 60);
1377266077Sdes	longitude = longitude % (1000 * 60 * 60);
1378266077Sdes	m = longitude / (1000 * 60);
1379266077Sdes	longitude = longitude % (1000 * 60);
1380266077Sdes	s = (double) longitude / (1000.0);
1381266077Sdes	w += sldns_str_print(str, sl, "%02u %02u %06.3f %c ",
1382266077Sdes		h, m, s, easterness);
1383266077Sdes
1384266077Sdes	s = ((double) altitude) / 100;
1385266077Sdes	s -= 100000;
1386266077Sdes
1387266077Sdes	if(altitude%100 != 0)
1388266077Sdes		w += sldns_str_print(str, sl, "%.2f", s);
1389266077Sdes	else
1390266077Sdes		w += sldns_str_print(str, sl, "%.0f", s);
1391266077Sdes
1392266077Sdes	w += sldns_str_print(str, sl, "m ");
1393266077Sdes
1394266077Sdes	w += loc_cm_print(str, sl, (size & 0xf0) >> 4, size & 0x0f);
1395266077Sdes	w += sldns_str_print(str, sl, "m ");
1396266077Sdes
1397266077Sdes	w += loc_cm_print(str, sl, (horizontal_precision & 0xf0) >> 4,
1398266077Sdes		horizontal_precision & 0x0f);
1399266077Sdes	w += sldns_str_print(str, sl, "m ");
1400266077Sdes
1401266077Sdes	w += loc_cm_print(str, sl, (vertical_precision & 0xf0) >> 4,
1402266077Sdes		vertical_precision & 0x0f);
1403266077Sdes	w += sldns_str_print(str, sl, "m");
1404266077Sdes
1405266077Sdes	(*d)+=16;
1406266077Sdes	(*dl)-=16;
1407266077Sdes	return w;
1408266077Sdes}
1409266077Sdes
1410266077Sdesint sldns_wire2str_wks_scan(uint8_t** d, size_t* dl, char** s, size_t* sl)
1411266077Sdes{
1412266077Sdes	/* protocol, followed by bitmap of services */
1413266077Sdes	const char* proto_name = NULL;
1414266077Sdes	struct protoent *protocol;
1415266077Sdes	struct servent *service;
1416266077Sdes	uint8_t protocol_nr;
1417266077Sdes	int bit, port, w = 0;
1418266077Sdes	size_t i;
1419266077Sdes	/* we cannot print with strings because they
1420266077Sdes	 * are not portable, the presentation format may
1421266077Sdes	 * not be able to be read in on another computer.  */
1422266077Sdes	int print_symbols = 0;
1423266077Sdes
1424266077Sdes	/* protocol */
1425266077Sdes	if(*dl < 1) return -1;
1426266077Sdes	protocol_nr = (*d)[0];
1427266077Sdes	(*d)++;
1428266077Sdes	(*dl)--;
1429266077Sdes	protocol = getprotobynumber((int)protocol_nr);
1430266077Sdes	if(protocol && (protocol->p_name != NULL)) {
1431266077Sdes		w += sldns_str_print(s, sl, "%s", protocol->p_name);
1432266077Sdes		proto_name = protocol->p_name;
1433266077Sdes	} else	{
1434266077Sdes		w += sldns_str_print(s, sl, "%u", (unsigned)protocol_nr);
1435266077Sdes	}
1436266077Sdes
1437266077Sdes	for(i=0; i<*dl; i++) {
1438266077Sdes		if((*d)[i] == 0)
1439266077Sdes			continue;
1440266077Sdes		for(bit=0; bit<8; bit++) {
1441266077Sdes			if(!(((*d)[i])&(0x80>>bit)))
1442266077Sdes				continue;
1443266077Sdes			port = (int)i*8 + bit;
1444266077Sdes
1445266077Sdes			if(!print_symbols)
1446266077Sdes				service = NULL;
1447266077Sdes			else
1448266077Sdes				service = getservbyport(
1449266077Sdes					(int)htons((uint16_t)port), proto_name);
1450266077Sdes			if(service && service->s_name)
1451266077Sdes				w += sldns_str_print(s, sl, " %s",
1452266077Sdes					service->s_name);
1453266077Sdes			else 	w += sldns_str_print(s, sl, " %u",
1454266077Sdes					(unsigned)port);
1455266077Sdes		}
1456266077Sdes	}
1457266077Sdes
1458266077Sdes#ifdef HAVE_ENDSERVENT
1459266077Sdes	endservent();
1460266077Sdes#endif
1461266077Sdes#ifdef HAVE_ENDPROTOENT
1462266077Sdes        endprotoent();
1463266077Sdes#endif
1464266077Sdes	(*d) += *dl;
1465266077Sdes	(*dl) = 0;
1466266077Sdes	return w;
1467266077Sdes}
1468266077Sdes
1469266077Sdesint sldns_wire2str_nsap_scan(uint8_t** d, size_t* dl, char** s, size_t* sl)
1470266077Sdes{
1471266077Sdes	return print_remainder_hex("0x", d, dl, s, sl);
1472266077Sdes}
1473266077Sdes
1474266077Sdesint sldns_wire2str_atma_scan(uint8_t** d, size_t* dl, char** s, size_t* sl)
1475266077Sdes{
1476266077Sdes	return print_remainder_hex("", d, dl, s, sl);
1477266077Sdes}
1478266077Sdes
1479266077Sdes/* internal scan routine that can modify arguments on failure */
1480266077Sdesstatic int sldns_wire2str_ipseckey_scan_internal(uint8_t** d, size_t* dl,
1481266077Sdes	char** s, size_t* sl, uint8_t* pkt, size_t pktlen)
1482266077Sdes{
1483266077Sdes	/* http://www.ietf.org/internet-drafts/draft-ietf-ipseckey-rr-12.txt*/
1484266077Sdes	uint8_t precedence, gateway_type, algorithm;
1485266077Sdes	int w = 0;
1486266077Sdes
1487266077Sdes	if(*dl < 3) return -1;
1488266077Sdes	precedence = (*d)[0];
1489266077Sdes	gateway_type = (*d)[1];
1490266077Sdes	algorithm = (*d)[2];
1491266077Sdes	if(gateway_type > 3)
1492266077Sdes		return -1; /* unknown */
1493266077Sdes	(*d)+=3;
1494266077Sdes	(*dl)-=3;
1495266077Sdes	w += sldns_str_print(s, sl, "%d %d %d ",
1496266077Sdes		(int)precedence, (int)gateway_type, (int)algorithm);
1497266077Sdes
1498266077Sdes	switch(gateway_type) {
1499266077Sdes	case 0: /* no gateway */
1500266077Sdes		w += sldns_str_print(s, sl, ".");
1501266077Sdes		break;
1502266077Sdes	case 1: /* ip4 */
1503266077Sdes		w += sldns_wire2str_a_scan(d, dl, s, sl);
1504266077Sdes		break;
1505266077Sdes	case 2: /* ip6 */
1506266077Sdes		w += sldns_wire2str_aaaa_scan(d, dl, s, sl);
1507266077Sdes		break;
1508266077Sdes	case 3: /* dname */
1509266077Sdes		w += sldns_wire2str_dname_scan(d, dl, s, sl, pkt, pktlen);
1510266077Sdes		break;
1511266077Sdes	default: /* unknown */
1512266077Sdes		return -1;
1513266077Sdes	}
1514266077Sdes
1515266077Sdes	if(*dl < 1)
1516266077Sdes		return -1;
1517266077Sdes	w += sldns_str_print(s, sl, " ");
1518266077Sdes	w += sldns_wire2str_b64_scan_num(d, dl, s, sl, *dl);
1519266077Sdes	return w;
1520266077Sdes}
1521266077Sdes
1522266077Sdesint sldns_wire2str_ipseckey_scan(uint8_t** d, size_t* dl, char** s, size_t* sl,
1523266077Sdes	uint8_t* pkt, size_t pktlen)
1524266077Sdes{
1525266077Sdes	uint8_t* od = *d;
1526266077Sdes	char* os = *s;
1527266077Sdes	size_t odl = *dl, osl = *sl;
1528266077Sdes	int w=sldns_wire2str_ipseckey_scan_internal(d, dl, s, sl, pkt, pktlen);
1529266077Sdes	if(w == -1) {
1530266077Sdes		*d = od;
1531266077Sdes		*s = os;
1532266077Sdes		*dl = odl;
1533266077Sdes		*sl = osl;
1534266077Sdes		return -1;
1535266077Sdes	}
1536266077Sdes	return w;
1537266077Sdes}
1538266077Sdes
1539266077Sdesint sldns_wire2str_hip_scan(uint8_t** d, size_t* dl, char** s, size_t* sl)
1540266077Sdes{
1541266077Sdes	int w;
1542266077Sdes	uint8_t algo, hitlen;
1543266077Sdes	uint16_t pklen;
1544266077Sdes
1545266077Sdes	/* read lengths */
1546266077Sdes	if(*dl < 4)
1547266077Sdes		return -1;
1548266077Sdes	hitlen = (*d)[0];
1549266077Sdes	algo = (*d)[1];
1550266077Sdes	pklen = sldns_read_uint16((*d)+2);
1551266077Sdes	if(*dl < (size_t)4 + (size_t)hitlen + (size_t)pklen)
1552266077Sdes		return -1;
1553266077Sdes
1554266077Sdes	/* write: algo hit pubkey */
1555266077Sdes	w = sldns_str_print(s, sl, "%u ", (unsigned)algo);
1556266077Sdes	w += print_hex_buf(s, sl, (*d)+4, hitlen);
1557266077Sdes	w += sldns_str_print(s, sl, " ");
1558266077Sdes	(*d)+=4+hitlen;
1559266077Sdes	(*dl)-= (4+hitlen);
1560266077Sdes	w += sldns_wire2str_b64_scan_num(d, dl, s, sl, pklen);
1561266077Sdes	return w;
1562266077Sdes}
1563266077Sdes
1564266077Sdesint sldns_wire2str_int16_data_scan(uint8_t** d, size_t* dl, char** s, size_t* sl)
1565266077Sdes{
1566266077Sdes	uint16_t n;
1567266077Sdes	if(*dl < 2)
1568266077Sdes		return -1;
1569266077Sdes	n = sldns_read_uint16(*d);
1570266077Sdes	if(*dl < 2+(size_t)n)
1571266077Sdes		return -1;
1572266077Sdes	(*d)+=2;
1573266077Sdes	(*dl)-=2;
1574266077Sdes	return sldns_wire2str_b64_scan_num(d, dl, s, sl, n);
1575266077Sdes}
1576266077Sdes
1577266077Sdesint sldns_wire2str_nsec3_next_owner_scan(uint8_t** d, size_t* dl, char** s,
1578266077Sdes	size_t* sl)
1579266077Sdes{
1580266077Sdes	return sldns_wire2str_b32_ext_scan(d, dl, s, sl);
1581266077Sdes}
1582266077Sdes
1583266077Sdesint sldns_wire2str_ilnp64_scan(uint8_t** d, size_t* dl, char** s, size_t* sl)
1584266077Sdes{
1585266077Sdes	int w;
1586266077Sdes	if(*dl < 8)
1587266077Sdes		return -1;
1588266077Sdes	w = sldns_str_print(s, sl, "%.4x:%.4x:%.4x:%.4x",
1589266077Sdes		sldns_read_uint16(*d), sldns_read_uint16((*d)+2),
1590266077Sdes		sldns_read_uint16((*d)+4), sldns_read_uint16((*d)+6));
1591266077Sdes	(*d)+=8;
1592266077Sdes	(*dl)-=8;
1593266077Sdes	return w;
1594266077Sdes}
1595266077Sdes
1596266077Sdesint sldns_wire2str_eui48_scan(uint8_t** d, size_t* dl, char** s, size_t* sl)
1597266077Sdes{
1598266077Sdes	int w;
1599266077Sdes	if(*dl < 6)
1600266077Sdes		return -1;
1601266077Sdes	w = sldns_str_print(s, sl, "%.2x-%.2x-%.2x-%.2x-%.2x-%.2x",
1602266077Sdes		(*d)[0], (*d)[1], (*d)[2], (*d)[3], (*d)[4], (*d)[5]);
1603266077Sdes	(*d)+=6;
1604266077Sdes	(*dl)-=6;
1605266077Sdes	return w;
1606266077Sdes}
1607266077Sdes
1608266077Sdesint sldns_wire2str_eui64_scan(uint8_t** d, size_t* dl, char** s, size_t* sl)
1609266077Sdes{
1610266077Sdes	int w;
1611266077Sdes	if(*dl < 8)
1612266077Sdes		return -1;
1613266077Sdes	w = sldns_str_print(s, sl, "%.2x-%.2x-%.2x-%.2x-%.2x-%.2x-%.2x-%.2x",
1614266077Sdes		(*d)[0], (*d)[1], (*d)[2], (*d)[3], (*d)[4], (*d)[5],
1615266077Sdes		(*d)[6], (*d)[7]);
1616266077Sdes	(*d)+=8;
1617266077Sdes	(*dl)-=8;
1618266077Sdes	return w;
1619266077Sdes}
1620266077Sdes
1621266077Sdesint sldns_wire2str_tag_scan(uint8_t** d, size_t* dl, char** s, size_t* sl)
1622266077Sdes{
1623266077Sdes	size_t i, n;
1624266077Sdes	int w = 0;
1625266077Sdes	if(*dl < 1)
1626266077Sdes		return -1;
1627266077Sdes	n = (size_t)((*d)[0]);
1628266077Sdes	if(*dl < 1+n)
1629266077Sdes		return -1;
1630266077Sdes	for(i=0; i<n; i++)
1631276541Sdes		if(!isalnum((unsigned char)(*d)[i]))
1632266077Sdes			return -1;
1633266077Sdes	for(i=0; i<n; i++)
1634266077Sdes		w += sldns_str_print(s, sl, "%c", (char)(*d)[i]);
1635266077Sdes	(*d)+=n+1;
1636266077Sdes	(*dl)-=(n+1);
1637266077Sdes	return w;
1638266077Sdes}
1639266077Sdes
1640266077Sdesint sldns_wire2str_long_str_scan(uint8_t** d, size_t* dl, char** s, size_t* sl)
1641266077Sdes{
1642266077Sdes	size_t i;
1643266077Sdes	int w = 0;
1644266077Sdes	w += sldns_str_print(s, sl, "\"");
1645266077Sdes	for(i=0; i<*dl; i++)
1646266077Sdes		w += str_char_print(s, sl, (*d)[i]);
1647266077Sdes	w += sldns_str_print(s, sl, "\"");
1648266077Sdes	(*d)+=*dl;
1649266077Sdes	(*dl)=0;
1650266077Sdes	return w;
1651266077Sdes}
1652266077Sdes
1653266077Sdesint sldns_wire2str_edns_llq_print(char** s, size_t* sl, uint8_t* data,
1654266077Sdes	size_t len)
1655266077Sdes{
1656266077Sdes	/* LLQ constants */
1657266077Sdes	const char* llq_errors[] = {"NO-ERROR", "SERV-FULL", "STATIC",
1658266077Sdes		"FORMAT-ERR", "NO-SUCH-LLQ", "BAD-VERS", "UNKNOWN_ERR"};
1659266077Sdes	const unsigned int llq_errors_num = 7;
1660266077Sdes	const char* llq_opcodes[] = {"LLQ-SETUP", "LLQ-REFRESH", "LLQ-EVENT"};
1661266077Sdes	const unsigned int llq_opcodes_num = 3;
1662266077Sdes	uint16_t version, llq_opcode, error_code;
1663266077Sdes	uint64_t llq_id;
1664266077Sdes	uint32_t lease_life; /* Requested or granted life of LLQ, in seconds */
1665266077Sdes	int w = 0;
1666266077Sdes
1667266077Sdes	/* read the record */
1668266077Sdes	if(len != 18) {
1669266077Sdes		w += sldns_str_print(s, sl, "malformed LLQ ");
1670266077Sdes		w += print_hex_buf(s, sl, data, len);
1671266077Sdes		return w;
1672266077Sdes	}
1673266077Sdes	version = sldns_read_uint16(data);
1674266077Sdes	llq_opcode = sldns_read_uint16(data+2);
1675266077Sdes	error_code = sldns_read_uint16(data+4);
1676266077Sdes	memmove(&llq_id, data+6, sizeof(llq_id));
1677266077Sdes	lease_life = sldns_read_uint32(data+14);
1678266077Sdes
1679266077Sdes	/* print it */
1680266077Sdes	w += sldns_str_print(s, sl, "v%d ", (int)version);
1681266077Sdes	if(llq_opcode < llq_opcodes_num)
1682266077Sdes		w += sldns_str_print(s, sl, "%s", llq_opcodes[llq_opcode]);
1683266077Sdes	else	w += sldns_str_print(s, sl, "opcode %d", (int)llq_opcode);
1684266077Sdes	if(error_code < llq_errors_num)
1685266077Sdes		w += sldns_str_print(s, sl, " %s", llq_errors[error_code]);
1686266077Sdes	else	w += sldns_str_print(s, sl, " error %d", (int)error_code);
1687266077Sdes#ifndef USE_WINSOCK
1688266077Sdes	w += sldns_str_print(s, sl, " id %llx lease-life %lu",
1689266077Sdes		(unsigned long long)llq_id, (unsigned long)lease_life);
1690266077Sdes#else
1691266077Sdes	w += sldns_str_print(s, sl, " id %I64x lease-life %lu",
1692266077Sdes		(unsigned long long)llq_id, (unsigned long)lease_life);
1693266077Sdes#endif
1694266077Sdes	return w;
1695266077Sdes}
1696266077Sdes
1697266077Sdesint sldns_wire2str_edns_ul_print(char** s, size_t* sl, uint8_t* data,
1698266077Sdes	size_t len)
1699266077Sdes{
1700266077Sdes	uint32_t lease;
1701266077Sdes	int w = 0;
1702266077Sdes	if(len != 4) {
1703266077Sdes		w += sldns_str_print(s, sl, "malformed UL ");
1704266077Sdes		w += print_hex_buf(s, sl, data, len);
1705266077Sdes		return w;
1706266077Sdes	}
1707266077Sdes	lease = sldns_read_uint32(data);
1708266077Sdes	w += sldns_str_print(s, sl, "lease %lu", (unsigned long)lease);
1709266077Sdes	return w;
1710266077Sdes}
1711266077Sdes
1712266077Sdesint sldns_wire2str_edns_nsid_print(char** s, size_t* sl, uint8_t* data,
1713266077Sdes	size_t len)
1714266077Sdes{
1715266077Sdes	int w = 0;
1716266077Sdes	size_t i, printed=0;
1717266077Sdes	w += print_hex_buf(s, sl, data, len);
1718266077Sdes	for(i=0; i<len; i++) {
1719276541Sdes		if(isprint((unsigned char)data[i]) || data[i] == '\t') {
1720266077Sdes			if(!printed) {
1721266077Sdes				w += sldns_str_print(s, sl, " (");
1722266077Sdes				printed = 1;
1723266077Sdes			}
1724266077Sdes			w += sldns_str_print(s, sl, "%c", (char)data[i]);
1725266077Sdes		}
1726266077Sdes	}
1727266077Sdes	if(printed)
1728266077Sdes		w += sldns_str_print(s, sl, ")");
1729266077Sdes	return w;
1730266077Sdes}
1731266077Sdes
1732266077Sdesint sldns_wire2str_edns_dau_print(char** s, size_t* sl, uint8_t* data,
1733266077Sdes	size_t len)
1734266077Sdes{
1735266077Sdes	sldns_lookup_table *lt;
1736266077Sdes	size_t i;
1737266077Sdes	int w = 0;
1738266077Sdes	for(i=0; i<len; i++) {
1739266077Sdes		lt = sldns_lookup_by_id(sldns_algorithms, (int)data[i]);
1740266077Sdes		if(lt && lt->name)
1741266077Sdes			w += sldns_str_print(s, sl, " %s", lt->name);
1742266077Sdes		else 	w += sldns_str_print(s, sl, " %d", (int)data[i]);
1743266077Sdes	}
1744266077Sdes	return w;
1745266077Sdes}
1746266077Sdes
1747266077Sdesint sldns_wire2str_edns_dhu_print(char** s, size_t* sl, uint8_t* data,
1748266077Sdes	size_t len)
1749266077Sdes{
1750266077Sdes	sldns_lookup_table *lt;
1751266077Sdes	size_t i;
1752266077Sdes	int w = 0;
1753266077Sdes	for(i=0; i<len; i++) {
1754266077Sdes		lt = sldns_lookup_by_id(sldns_hashes, (int)data[i]);
1755266077Sdes		if(lt && lt->name)
1756266077Sdes			w += sldns_str_print(s, sl, " %s", lt->name);
1757266077Sdes		else 	w += sldns_str_print(s, sl, " %d", (int)data[i]);
1758266077Sdes	}
1759266077Sdes	return w;
1760266077Sdes}
1761266077Sdes
1762266077Sdesint sldns_wire2str_edns_n3u_print(char** s, size_t* sl, uint8_t* data,
1763266077Sdes	size_t len)
1764266077Sdes{
1765266077Sdes	size_t i;
1766266077Sdes	int w = 0;
1767266077Sdes	for(i=0; i<len; i++) {
1768266077Sdes		if(data[i] == 1)
1769266077Sdes			w += sldns_str_print(s, sl, " SHA1");
1770266077Sdes		else 	w += sldns_str_print(s, sl, " %d", (int)data[i]);
1771266077Sdes	}
1772266077Sdes	return w;
1773266077Sdes}
1774266077Sdes
1775266077Sdesint sldns_wire2str_edns_subnet_print(char** s, size_t* sl, uint8_t* data,
1776266077Sdes	size_t len)
1777266077Sdes{
1778266077Sdes	int w = 0;
1779266077Sdes	uint16_t family;
1780266077Sdes	uint8_t source, scope;
1781266077Sdes	if(len < 4) {
1782266077Sdes		w += sldns_str_print(s, sl, "malformed subnet ");
1783266077Sdes		w += print_hex_buf(s, sl, data, len);
1784266077Sdes		return w;
1785266077Sdes	}
1786266077Sdes	family = sldns_read_uint16(data);
1787266077Sdes	source = data[2];
1788266077Sdes	scope = data[3];
1789266077Sdes	if(family == 1) {
1790266077Sdes		/* IP4 */
1791266077Sdes		char buf[64];
1792266077Sdes		uint8_t ip4[4];
1793266077Sdes		memset(ip4, 0, sizeof(ip4));
1794266077Sdes		if(len-4 > 4) {
1795266077Sdes			w += sldns_str_print(s, sl, "trailingdata:");
1796266077Sdes			w += print_hex_buf(s, sl, data+4+4, len-4-4);
1797266077Sdes			w += sldns_str_print(s, sl, " ");
1798266077Sdes			len = 4+4;
1799266077Sdes		}
1800266077Sdes		memmove(ip4, data+4, len-4);
1801266077Sdes		if(!inet_ntop(AF_INET, ip4, buf, (socklen_t)sizeof(buf))) {
1802266077Sdes			w += sldns_str_print(s, sl, "ip4ntoperror ");
1803266077Sdes			w += print_hex_buf(s, sl, data+4+4, len-4-4);
1804266077Sdes		} else {
1805266077Sdes			w += sldns_str_print(s, sl, "%s", buf);
1806266077Sdes		}
1807266077Sdes	} else if(family == 2) {
1808266077Sdes		/* IP6 */
1809266077Sdes		char buf[64];
1810266077Sdes		uint8_t ip6[16];
1811266077Sdes		memset(ip6, 0, sizeof(ip6));
1812266077Sdes		if(len-4 > 16) {
1813266077Sdes			w += sldns_str_print(s, sl, "trailingdata:");
1814266077Sdes			w += print_hex_buf(s, sl, data+4+16, len-4-16);
1815266077Sdes			w += sldns_str_print(s, sl, " ");
1816266077Sdes			len = 4+16;
1817266077Sdes		}
1818266077Sdes		memmove(ip6, data+4, len-4);
1819266077Sdes#ifdef AF_INET6
1820266077Sdes		if(!inet_ntop(AF_INET6, ip6, buf, (socklen_t)sizeof(buf))) {
1821266077Sdes			w += sldns_str_print(s, sl, "ip6ntoperror ");
1822266077Sdes			w += print_hex_buf(s, sl, data+4+4, len-4-4);
1823266077Sdes		} else {
1824266077Sdes			w += sldns_str_print(s, sl, "%s", buf);
1825266077Sdes		}
1826266077Sdes#else
1827266077Sdes		w += print_hex_buf(s, sl, data+4+4, len-4-4);
1828266077Sdes#endif
1829266077Sdes	} else {
1830266077Sdes		/* unknown */
1831266077Sdes		w += sldns_str_print(s, sl, "family %d ",
1832266077Sdes			(int)family);
1833266077Sdes		w += print_hex_buf(s, sl, data, len);
1834266077Sdes	}
1835266077Sdes	w += sldns_str_print(s, sl, "/%d scope /%d", (int)source, (int)scope);
1836266077Sdes	return w;
1837266077Sdes}
1838266077Sdes
1839266077Sdesint sldns_wire2str_edns_option_print(char** s, size_t* sl,
1840266077Sdes	uint16_t option_code, uint8_t* optdata, size_t optlen)
1841266077Sdes{
1842266077Sdes	int w = 0;
1843266077Sdes	w += sldns_wire2str_edns_option_code_print(s, sl, option_code);
1844266077Sdes	w += sldns_str_print(s, sl, ": ");
1845266077Sdes	switch(option_code) {
1846266077Sdes	case LDNS_EDNS_LLQ:
1847266077Sdes		w += sldns_wire2str_edns_llq_print(s, sl, optdata, optlen);
1848266077Sdes		break;
1849266077Sdes	case LDNS_EDNS_UL:
1850266077Sdes		w += sldns_wire2str_edns_ul_print(s, sl, optdata, optlen);
1851266077Sdes		break;
1852266077Sdes	case LDNS_EDNS_NSID:
1853266077Sdes		w += sldns_wire2str_edns_nsid_print(s, sl, optdata, optlen);
1854266077Sdes		break;
1855266077Sdes	case LDNS_EDNS_DAU:
1856266077Sdes		w += sldns_wire2str_edns_dau_print(s, sl, optdata, optlen);
1857266077Sdes		break;
1858266077Sdes	case LDNS_EDNS_DHU:
1859266077Sdes		w += sldns_wire2str_edns_dhu_print(s, sl, optdata, optlen);
1860266077Sdes		break;
1861266077Sdes	case LDNS_EDNS_N3U:
1862266077Sdes		w += sldns_wire2str_edns_n3u_print(s, sl, optdata, optlen);
1863266077Sdes		break;
1864266077Sdes	case LDNS_EDNS_CLIENT_SUBNET:
1865266077Sdes		w += sldns_wire2str_edns_subnet_print(s, sl, optdata, optlen);
1866266077Sdes		break;
1867266077Sdes	default:
1868266077Sdes		/* unknown option code */
1869266077Sdes		w += print_hex_buf(s, sl, optdata, optlen);
1870266077Sdes		break;
1871266077Sdes	}
1872266077Sdes	return w;
1873266077Sdes}
1874266077Sdes
1875266077Sdes/** print the edns options to string */
1876266077Sdesstatic int
1877266077Sdesprint_edns_opts(char** s, size_t* sl, uint8_t* rdata, size_t rdatalen)
1878266077Sdes{
1879266077Sdes	uint16_t option_code, option_len;
1880266077Sdes	int w = 0;
1881266077Sdes	while(rdatalen > 0) {
1882266077Sdes		/* option name */
1883266077Sdes		if(rdatalen < 4) {
1884266077Sdes			w += sldns_str_print(s, sl, " ; malformed: ");
1885266077Sdes			w += print_hex_buf(s, sl, rdata, rdatalen);
1886266077Sdes			return w;
1887266077Sdes		}
1888266077Sdes		option_code = sldns_read_uint16(rdata);
1889266077Sdes		option_len = sldns_read_uint16(rdata+2);
1890266077Sdes		rdata += 4;
1891266077Sdes		rdatalen -= 4;
1892266077Sdes
1893266077Sdes		/* option value */
1894266077Sdes		if(rdatalen < (size_t)option_len) {
1895266077Sdes			w += sldns_str_print(s, sl, " ; malformed ");
1896266077Sdes			w += sldns_wire2str_edns_option_code_print(s, sl,
1897266077Sdes				option_code);
1898266077Sdes			w += sldns_str_print(s, sl, ": ");
1899266077Sdes			w += print_hex_buf(s, sl, rdata, rdatalen);
1900266077Sdes			return w;
1901266077Sdes		}
1902266077Sdes		w += sldns_str_print(s, sl, " ; ");
1903266077Sdes		w += sldns_wire2str_edns_option_print(s, sl, option_code,
1904266077Sdes			rdata, option_len);
1905266077Sdes		rdata += option_len;
1906266077Sdes		rdatalen -= option_len;
1907266077Sdes	}
1908266077Sdes	return w;
1909266077Sdes}
1910266077Sdes
1911266077Sdesint sldns_wire2str_edns_scan(uint8_t** data, size_t* data_len, char** str,
1912266077Sdes        size_t* str_len, uint8_t* pkt, size_t pktlen)
1913266077Sdes{
1914266077Sdes	int w = 0;
1915266077Sdes	uint8_t ext_rcode, edns_version;
1916266077Sdes	uint16_t udpsize, edns_bits, rdatalen;
1917266077Sdes	w += sldns_str_print(str, str_len, "; EDNS:");
1918266077Sdes
1919266077Sdes	/* some input checks, domain name */
1920266077Sdes	if(*data_len < 1+10)
1921266077Sdes		return w + print_remainder_hex("Error malformed 0x",
1922266077Sdes			data, data_len, str, str_len);
1923266077Sdes	if(*data[0] != 0) {
1924266077Sdes		return w + print_remainder_hex("Error nonrootdname 0x",
1925266077Sdes			data, data_len, str, str_len);
1926266077Sdes	}
1927266077Sdes	(*data)++;
1928266077Sdes	(*data_len)--;
1929266077Sdes
1930266077Sdes	/* check type and read fixed contents */
1931266077Sdes	if(sldns_read_uint16((*data)) != LDNS_RR_TYPE_OPT) {
1932266077Sdes		return w + print_remainder_hex("Error nottypeOPT 0x",
1933266077Sdes			data, data_len, str, str_len);
1934266077Sdes	}
1935266077Sdes	udpsize = sldns_read_uint16((*data)+2);
1936266077Sdes	ext_rcode = (*data)[4];
1937266077Sdes	edns_version = (*data)[5];
1938266077Sdes	edns_bits = sldns_read_uint16((*data)+6);
1939266077Sdes	rdatalen = sldns_read_uint16((*data)+8);
1940266077Sdes	(*data)+=10;
1941266077Sdes	(*data_len)-=10;
1942266077Sdes
1943266077Sdes	w += sldns_str_print(str, str_len, " version: %u;",
1944266077Sdes		(unsigned)edns_version);
1945266077Sdes	w += sldns_str_print(str, str_len, " flags:");
1946266077Sdes	if((edns_bits & LDNS_EDNS_MASK_DO_BIT))
1947266077Sdes		w += sldns_str_print(str, str_len, " do");
1948266077Sdes	/* the extended rcode is the value set, shifted four bits,
1949266077Sdes	 * and or'd with the original rcode */
1950266077Sdes	if(ext_rcode) {
1951266077Sdes		int rc = ((int)ext_rcode)<<4;
1952266077Sdes		if(pkt && pktlen >= LDNS_HEADER_SIZE)
1953266077Sdes			rc |= LDNS_RCODE_WIRE(pkt);
1954266077Sdes		w += sldns_str_print(str, str_len, " ; ext-rcode: %d", rc);
1955266077Sdes	}
1956266077Sdes	w += sldns_str_print(str, str_len, " ; udp: %u", (unsigned)udpsize);
1957266077Sdes
1958266077Sdes	if(rdatalen) {
1959266077Sdes		if(*data_len < rdatalen) {
1960266077Sdes			w += sldns_str_print(str, str_len,
1961266077Sdes				" ; Error EDNS rdata too short; ");
1962266077Sdes			rdatalen = *data_len;
1963266077Sdes		}
1964266077Sdes		w += print_edns_opts(str, str_len, *data, rdatalen);
1965266077Sdes		(*data) += rdatalen;
1966266077Sdes		(*data_len) -= rdatalen;
1967266077Sdes	}
1968266077Sdes	w += sldns_str_print(str, str_len, "\n");
1969266077Sdes	return w;
1970266077Sdes}
1971