1238104Sdes/*
2238104Sdes * work.c
3238104Sdes * Where all the hard work is done
4238104Sdes * (c) 2005 NLnet Labs
5238104Sdes *
6238104Sdes * See the file LICENSE for the license
7238104Sdes *
8238104Sdes */
9238104Sdes
10238104Sdes#include "drill.h"
11238104Sdes#include <ldns/ldns.h>
12238104Sdes
13238104Sdes/**
14238104Sdes * Converts a hex string to binary data
15238104Sdes * len is the length of the string
16238104Sdes * buf is the buffer to store the result in
17238104Sdes * offset is the starting position in the result buffer
18238104Sdes *
19238104Sdes * This function returns the length of the result
20238104Sdes */
21238104Sdessize_t
22238104Sdeshexstr2bin(char *hexstr, int len, uint8_t *buf, size_t offset, size_t buf_len)
23238104Sdes{
24238104Sdes	char c;
25238104Sdes	int i;
26238104Sdes	uint8_t int8 = 0;
27238104Sdes	int sec = 0;
28238104Sdes	size_t bufpos = 0;
29238104Sdes
30238104Sdes	if (len % 2 != 0) {
31238104Sdes		return 0;
32238104Sdes	}
33238104Sdes
34238104Sdes	for (i=0; i<len; i++) {
35238104Sdes		c = hexstr[i];
36238104Sdes
37238104Sdes		/* case insensitive, skip spaces */
38238104Sdes		if (c != ' ') {
39238104Sdes			if (c >= '0' && c <= '9') {
40238104Sdes				int8 += c & 0x0f;
41238104Sdes			} else if (c >= 'a' && c <= 'z') {
42238104Sdes				int8 += (c & 0x0f) + 9;
43238104Sdes			} else if (c >= 'A' && c <= 'Z') {
44238104Sdes				int8 += (c & 0x0f) + 9;
45238104Sdes			} else {
46238104Sdes				return 0;
47238104Sdes			}
48238104Sdes
49238104Sdes			if (sec == 0) {
50238104Sdes				int8 = int8 << 4;
51238104Sdes				sec = 1;
52238104Sdes			} else {
53238104Sdes				if (bufpos + offset + 1 <= buf_len) {
54238104Sdes					buf[bufpos+offset] = int8;
55238104Sdes					int8 = 0;
56238104Sdes					sec = 0;
57238104Sdes					bufpos++;
58238104Sdes				} else {
59238104Sdes					error("Buffer too small in hexstr2bin");
60238104Sdes				}
61238104Sdes			}
62238104Sdes		}
63238104Sdes        }
64238104Sdes        return bufpos;
65238104Sdes}
66238104Sdes
67238104Sdessize_t
68238104Sdespacketbuffromfile(char *filename, uint8_t *wire)
69238104Sdes{
70238104Sdes	FILE *fp = NULL;
71238104Sdes	int c;
72238104Sdes
73238104Sdes	/* stat hack
74238104Sdes	 * 0 = normal
75238104Sdes	 * 1 = comment (skip to end of line)
76238104Sdes	 * 2 = unprintable character found, read binary data directly
77238104Sdes	 */
78238104Sdes	int state = 0;
79238104Sdes	uint8_t *hexbuf = xmalloc(LDNS_MAX_PACKETLEN);
80238104Sdes	int hexbufpos = 0;
81238104Sdes	size_t wirelen;
82238104Sdes
83238104Sdes	if (strncmp(filename, "-", 2) == 0) {
84238104Sdes		fp = stdin;
85238104Sdes	} else {
86238104Sdes		fp = fopen(filename, "r");
87238104Sdes	}
88238104Sdes	if (fp == NULL) {
89238104Sdes		perror("Unable to open file for reading");
90238104Sdes		xfree(hexbuf);
91238104Sdes		return 0;
92238104Sdes	}
93238104Sdes
94238104Sdes	/*verbose("Opened %s\n", filename);*/
95238104Sdes
96238104Sdes	c = fgetc(fp);
97238104Sdes	while (c != EOF && hexbufpos < LDNS_MAX_PACKETLEN) {
98238104Sdes		if (state < 2 && !isascii(c)) {
99238104Sdes			/*verbose("non ascii character found in file: (%d) switching to raw mode\n", c);*/
100238104Sdes			state = 2;
101238104Sdes		}
102238104Sdes		switch (state) {
103238104Sdes			case 0:
104238104Sdes				if (	(c >= '0' && c <= '9') ||
105238104Sdes					(c >= 'a' && c <= 'f') ||
106238104Sdes					(c >= 'A' && c <= 'F') )
107238104Sdes				{
108238104Sdes					hexbuf[hexbufpos] = (uint8_t) c;
109238104Sdes					hexbufpos++;
110238104Sdes				} else if (c == ';') {
111238104Sdes					state = 1;
112238104Sdes				} else if (c == ' ' || c == '\t' || c == '\n') {
113238104Sdes					/* skip whitespace */
114238104Sdes				}
115238104Sdes				break;
116238104Sdes			case 1:
117238104Sdes				if (c == '\n' || c == EOF) {
118238104Sdes					state = 0;
119238104Sdes				}
120238104Sdes				break;
121238104Sdes			case 2:
122238104Sdes				hexbuf[hexbufpos] = (uint8_t) c;
123238104Sdes				hexbufpos++;
124238104Sdes				break;
125238104Sdes		}
126238104Sdes		c = fgetc(fp);
127238104Sdes	}
128238104Sdes
129238104Sdes	if (c == EOF) {
130238104Sdes		/*
131238104Sdes		if (have_drill_opt && drill_opt->verbose) {
132238104Sdes			verbose("END OF FILE REACHED\n");
133238104Sdes			if (state < 2) {
134238104Sdes				verbose("read:\n");
135238104Sdes				verbose("%s\n", hexbuf);
136238104Sdes			} else {
137238104Sdes				verbose("Not printing wire because it contains non ascii data\n");
138238104Sdes			}
139238104Sdes		}
140238104Sdes		*/
141238104Sdes	}
142238104Sdes	if (hexbufpos >= LDNS_MAX_PACKETLEN) {
143238104Sdes		/*verbose("packet size reached\n");*/
144238104Sdes	}
145238104Sdes
146238104Sdes	/* lenient mode: length must be multiple of 2 */
147238104Sdes	if (hexbufpos % 2 != 0) {
148238104Sdes		hexbuf[hexbufpos] = (uint8_t) '0';
149238104Sdes		hexbufpos++;
150238104Sdes	}
151238104Sdes
152238104Sdes	if (state < 2) {
153238104Sdes		wirelen = hexstr2bin((char *) hexbuf,
154238104Sdes						 hexbufpos,
155238104Sdes						 wire,
156238104Sdes						 0,
157238104Sdes						 LDNS_MAX_PACKETLEN);
158238104Sdes	} else {
159238104Sdes		memcpy(wire, hexbuf, (size_t) hexbufpos);
160238104Sdes		wirelen = (size_t) hexbufpos;
161238104Sdes	}
162238104Sdes	if (fp != stdin) {
163238104Sdes		fclose(fp);
164238104Sdes	}
165238104Sdes	xfree(hexbuf);
166238104Sdes	return wirelen;
167238104Sdes}
168238104Sdes
169238104Sdesldns_buffer *
170238104Sdesread_hex_buffer(char *filename)
171238104Sdes{
172238104Sdes	uint8_t *wire;
173238104Sdes	size_t wiresize;
174238104Sdes	ldns_buffer *result_buffer = NULL;
175238104Sdes
176246854Sdes
177238104Sdes	wire = xmalloc(LDNS_MAX_PACKETLEN);
178238104Sdes
179238104Sdes	wiresize = packetbuffromfile(filename, wire);
180238104Sdes
181238104Sdes	result_buffer = LDNS_MALLOC(ldns_buffer);
182238104Sdes	ldns_buffer_new_frm_data(result_buffer, wire, wiresize);
183238104Sdes	ldns_buffer_set_position(result_buffer, ldns_buffer_capacity(result_buffer));
184238104Sdes	xfree(wire);
185246854Sdes
186238104Sdes	return result_buffer;
187238104Sdes}
188238104Sdes
189238104Sdesldns_pkt *
190238104Sdesread_hex_pkt(char *filename)
191238104Sdes{
192238104Sdes	uint8_t *wire;
193238104Sdes	size_t wiresize;
194238104Sdes
195238104Sdes	ldns_pkt *pkt = NULL;
196238104Sdes
197238104Sdes	ldns_status status = LDNS_STATUS_ERR;
198238104Sdes
199238104Sdes	wire = xmalloc(LDNS_MAX_PACKETLEN);
200238104Sdes
201238104Sdes	wiresize = packetbuffromfile(filename, wire);
202238104Sdes
203238104Sdes	if (wiresize > 0) {
204238104Sdes		status = ldns_wire2pkt(&pkt, wire, wiresize);
205238104Sdes	}
206238104Sdes
207238104Sdes	xfree(wire);
208238104Sdes
209238104Sdes	if (status == LDNS_STATUS_OK) {
210238104Sdes		return pkt;
211238104Sdes	} else {
212238104Sdes		fprintf(stderr, "Error parsing hex file: %s\n",
213238104Sdes			   ldns_get_errorstr_by_id(status));
214238104Sdes		return NULL;
215238104Sdes	}
216238104Sdes}
217238104Sdes
218238104Sdesvoid
219238104Sdesdump_hex(const ldns_pkt *pkt, const char *filename)
220238104Sdes{
221246854Sdes	uint8_t *wire = NULL;
222238104Sdes	size_t size, i;
223238104Sdes	FILE *fp;
224238104Sdes	ldns_status status;
225238104Sdes
226238104Sdes	fp = fopen(filename, "w");
227238104Sdes
228238104Sdes	if (fp == NULL) {
229238104Sdes		error("Unable to open %s for writing", filename);
230238104Sdes		return;
231238104Sdes	}
232238104Sdes
233238104Sdes	status = ldns_pkt2wire(&wire, pkt, &size);
234238104Sdes
235238104Sdes	if (status != LDNS_STATUS_OK) {
236238104Sdes		error("Unable to convert packet: error code %u", status);
237246854Sdes		LDNS_FREE(wire);
238269257Sdes		fclose(fp);
239238104Sdes		return;
240238104Sdes	}
241238104Sdes
242238104Sdes	fprintf(fp, "; 0");
243238104Sdes	for (i = 1; i < 20; i++) {
244238104Sdes		fprintf(fp, " %2u", (unsigned int) i);
245238104Sdes	}
246238104Sdes	fprintf(fp, "\n");
247238104Sdes	fprintf(fp, ";--");
248238104Sdes	for (i = 1; i < 20; i++) {
249238104Sdes		fprintf(fp, " --");
250238104Sdes	}
251238104Sdes	fprintf(fp, "\n");
252238104Sdes	for (i = 0; i < size; i++) {
253238104Sdes		if (i % 20 == 0 && i > 0) {
254238104Sdes			fprintf(fp, "\t;\t%4u-%4u\n", (unsigned int) i-19, (unsigned int) i);
255238104Sdes		}
256238104Sdes		fprintf(fp, " %02x", (unsigned int)wire[i]);
257238104Sdes	}
258238104Sdes	fprintf(fp, "\n");
259238104Sdes	fclose(fp);
260246854Sdes	LDNS_FREE(wire);
261238104Sdes}
262