xref: /freebsd/contrib/ldns/drill/work.c (revision 8aac90f18aef7c9eea906c3ff9a001ca7b94f375)
1 /*
2  * work.c
3  * Where all the hard work is done
4  * (c) 2005 NLnet Labs
5  *
6  * See the file LICENSE for the license
7  *
8  */
9 
10 #include "drill.h"
11 #include <ldns/ldns.h>
12 
13 /**
14  * Converts a hex string to binary data
15  * len is the length of the string
16  * buf is the buffer to store the result in
17  * offset is the starting position in the result buffer
18  *
19  * This function returns the length of the result
20  */
21 static size_t
22 hexstr2bin(char *hexstr, int len, uint8_t *buf, size_t offset, size_t buf_len)
23 {
24 	char c;
25 	int i;
26 	uint8_t int8 = 0;
27 	int sec = 0;
28 	size_t bufpos = 0;
29 
30 	if (len % 2 != 0) {
31 		return 0;
32 	}
33 
34 	for (i=0; i<len; i++) {
35 		c = hexstr[i];
36 
37 		/* case insensitive, skip spaces */
38 		if (c != ' ') {
39 			if (c >= '0' && c <= '9') {
40 				int8 += c & 0x0f;
41 			} else if (c >= 'a' && c <= 'z') {
42 				int8 += (c & 0x0f) + 9;
43 			} else if (c >= 'A' && c <= 'Z') {
44 				int8 += (c & 0x0f) + 9;
45 			} else {
46 				return 0;
47 			}
48 
49 			if (sec == 0) {
50 				int8 = int8 << 4;
51 				sec = 1;
52 			} else {
53 				if (bufpos + offset + 1 <= buf_len) {
54 					buf[bufpos+offset] = int8;
55 					int8 = 0;
56 					sec = 0;
57 					bufpos++;
58 				} else {
59 					error("Buffer too small in hexstr2bin");
60 				}
61 			}
62 		}
63         }
64         return bufpos;
65 }
66 
67 static size_t
68 packetbuffromfile(char *filename, uint8_t *wire)
69 {
70 	FILE *fp = NULL;
71 	int c;
72 
73 	/* stat hack
74 	 * 0 = normal
75 	 * 1 = comment (skip to end of line)
76 	 * 2 = unprintable character found, read binary data directly
77 	 */
78 	int state = 0;
79 	uint8_t *hexbuf = xmalloc(LDNS_MAX_PACKETLEN);
80 	int hexbufpos = 0;
81 	size_t wirelen;
82 
83 	if (strncmp(filename, "-", 2) == 0) {
84 		fp = stdin;
85 	} else {
86 		fp = fopen(filename, "r");
87 	}
88 	if (fp == NULL) {
89 		perror("Unable to open file for reading");
90 		xfree(hexbuf);
91 		return 0;
92 	}
93 
94 	/*verbose("Opened %s\n", filename);*/
95 
96 	c = fgetc(fp);
97 	while (c != EOF && hexbufpos < LDNS_MAX_PACKETLEN) {
98 		if (state < 2 && !isascii(c)) {
99 			/*verbose("non ascii character found in file: (%d) switching to raw mode\n", c);*/
100 			state = 2;
101 		}
102 		switch (state) {
103 			case 0:
104 				if (	(c >= '0' && c <= '9') ||
105 					(c >= 'a' && c <= 'f') ||
106 					(c >= 'A' && c <= 'F') )
107 				{
108 					hexbuf[hexbufpos] = (uint8_t) c;
109 					hexbufpos++;
110 				} else if (c == ';') {
111 					state = 1;
112 				} else if (c == ' ' || c == '\t' || c == '\n') {
113 					/* skip whitespace */
114 				}
115 				break;
116 			case 1:
117 				if (c == '\n' || c == EOF) {
118 					state = 0;
119 				}
120 				break;
121 			case 2:
122 				hexbuf[hexbufpos] = (uint8_t) c;
123 				hexbufpos++;
124 				break;
125 		}
126 		c = fgetc(fp);
127 	}
128 
129 	if (c == EOF) {
130 		/*
131 		if (have_drill_opt && drill_opt->verbose) {
132 			verbose("END OF FILE REACHED\n");
133 			if (state < 2) {
134 				verbose("read:\n");
135 				verbose("%s\n", hexbuf);
136 			} else {
137 				verbose("Not printing wire because it contains non ascii data\n");
138 			}
139 		}
140 		*/
141 	}
142 	if (hexbufpos >= LDNS_MAX_PACKETLEN) {
143 		/*verbose("packet size reached\n");*/
144 	}
145 
146 	/* lenient mode: length must be multiple of 2 */
147 	if (hexbufpos % 2 != 0) {
148 		hexbuf[hexbufpos] = (uint8_t) '0';
149 		hexbufpos++;
150 	}
151 
152 	if (state < 2) {
153 		wirelen = hexstr2bin((char *) hexbuf,
154 						 hexbufpos,
155 						 wire,
156 						 0,
157 						 LDNS_MAX_PACKETLEN);
158 	} else {
159 		memcpy(wire, hexbuf, (size_t) hexbufpos);
160 		wirelen = (size_t) hexbufpos;
161 	}
162 	if (fp != stdin) {
163 		fclose(fp);
164 	}
165 	xfree(hexbuf);
166 	return wirelen;
167 }
168 
169 ldns_buffer *
170 read_hex_buffer(char *filename)
171 {
172 	uint8_t *wire;
173 	size_t wiresize;
174 	ldns_buffer *result_buffer = NULL;
175 
176 
177 	wire = xmalloc(LDNS_MAX_PACKETLEN);
178 
179 	wiresize = packetbuffromfile(filename, wire);
180 
181 	result_buffer = LDNS_MALLOC(ldns_buffer);
182 	ldns_buffer_new_frm_data(result_buffer, wire, wiresize);
183 	ldns_buffer_set_position(result_buffer, ldns_buffer_capacity(result_buffer));
184 	xfree(wire);
185 
186 	return result_buffer;
187 }
188 
189 ldns_pkt *
190 read_hex_pkt(char *filename)
191 {
192 	uint8_t *wire;
193 	size_t wiresize;
194 
195 	ldns_pkt *pkt = NULL;
196 
197 	ldns_status status = LDNS_STATUS_ERR;
198 
199 	wire = xmalloc(LDNS_MAX_PACKETLEN);
200 
201 	wiresize = packetbuffromfile(filename, wire);
202 
203 	if (wiresize > 0) {
204 		status = ldns_wire2pkt(&pkt, wire, wiresize);
205 	}
206 
207 	xfree(wire);
208 
209 	if (status == LDNS_STATUS_OK) {
210 		return pkt;
211 	} else {
212 		fprintf(stderr, "Error parsing hex file: %s\n",
213 			   ldns_get_errorstr_by_id(status));
214 		return NULL;
215 	}
216 }
217 
218 void
219 dump_hex(const ldns_pkt *pkt, const char *filename)
220 {
221 	uint8_t *wire = NULL;
222 	size_t size, i;
223 	FILE *fp;
224 	ldns_status status;
225 
226 	fp = fopen(filename, "w");
227 
228 	if (fp == NULL) {
229 		error("Unable to open %s for writing", filename);
230 		return;
231 	}
232 
233 	status = ldns_pkt2wire(&wire, pkt, &size);
234 
235 	if (status != LDNS_STATUS_OK) {
236 		error("Unable to convert packet: error code %u", status);
237 		LDNS_FREE(wire);
238 		fclose(fp);
239 		return;
240 	}
241 
242 	fprintf(fp, "; 0");
243 	for (i = 1; i < 20; i++) {
244 		fprintf(fp, " %2u", (unsigned int) i);
245 	}
246 	fprintf(fp, "\n");
247 	fprintf(fp, ";--");
248 	for (i = 1; i < 20; i++) {
249 		fprintf(fp, " --");
250 	}
251 	fprintf(fp, "\n");
252 	for (i = 0; i < size; i++) {
253 		if (i % 20 == 0 && i > 0) {
254 			fprintf(fp, "\t;\t%4u-%4u\n", (unsigned int) i-19, (unsigned int) i);
255 		}
256 		fprintf(fp, " %02x", (unsigned int)wire[i]);
257 	}
258 	fprintf(fp, "\n");
259 	fclose(fp);
260 	LDNS_FREE(wire);
261 }
262