xref: /netbsd-src/external/bsd/unbound/dist/testcode/streamtcp.c (revision bdc22b2e01993381dcefeff2bc9b56ca75a4235c)
1 /*
2  * testcode/streamtcp.c - debug program perform multiple DNS queries on tcp.
3  *
4  * Copyright (c) 2008, NLnet Labs. All rights reserved.
5  *
6  * This software is open source.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  *
12  * Redistributions of source code must retain the above copyright notice,
13  * this list of conditions and the following disclaimer.
14  *
15  * Redistributions in binary form must reproduce the above copyright notice,
16  * this list of conditions and the following disclaimer in the documentation
17  * and/or other materials provided with the distribution.
18  *
19  * Neither the name of the NLNET LABS nor the names of its contributors may
20  * be used to endorse or promote products derived from this software without
21  * specific prior written permission.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
24  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
25  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
26  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
27  * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
28  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
29  * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
30  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
31  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
32  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
33  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34  */
35 
36 /**
37  * \file
38  *
39  * This program performs multiple DNS queries on a TCP stream.
40  */
41 
42 #include "config.h"
43 #ifdef HAVE_GETOPT_H
44 #include <getopt.h>
45 #endif
46 #include <signal.h>
47 #include "util/locks.h"
48 #include "util/log.h"
49 #include "util/net_help.h"
50 #include "util/data/msgencode.h"
51 #include "util/data/msgparse.h"
52 #include "util/data/msgreply.h"
53 #include "util/data/dname.h"
54 #include "sldns/sbuffer.h"
55 #include "sldns/str2wire.h"
56 #include "sldns/wire2str.h"
57 #include <openssl/ssl.h>
58 #include <openssl/rand.h>
59 #include <openssl/err.h>
60 
61 #ifndef PF_INET6
62 /** define in case streamtcp is compiled on legacy systems */
63 #define PF_INET6 10
64 #endif
65 
66 /** usage information for streamtcp */
67 static void usage(char* argv[])
68 {
69 	printf("usage: %s [options] name type class ...\n", argv[0]);
70 	printf("	sends the name-type-class queries over TCP.\n");
71 	printf("-f server	what ipaddr@portnr to send the queries to\n");
72 	printf("-u 		use UDP. No retries are attempted.\n");
73 	printf("-n 		do not wait for an answer.\n");
74 	printf("-s		use ssl\n");
75 	printf("-h 		this help text\n");
76 	exit(1);
77 }
78 
79 /** open TCP socket to svr */
80 static int
81 open_svr(const char* svr, int udp)
82 {
83 	struct sockaddr_storage addr;
84 	socklen_t addrlen;
85 	int fd = -1;
86 	/* svr can be ip@port */
87 	memset(&addr, 0, sizeof(addr));
88 	if(!extstrtoaddr(svr, &addr, &addrlen)) {
89 		printf("fatal: bad server specs '%s'\n", svr);
90 		exit(1);
91 	}
92 	fd = socket(addr_is_ip6(&addr, addrlen)?PF_INET6:PF_INET,
93 		udp?SOCK_DGRAM:SOCK_STREAM, 0);
94 	if(fd == -1) {
95 #ifndef USE_WINSOCK
96 		perror("socket() error");
97 #else
98 		printf("socket: %s\n", wsa_strerror(WSAGetLastError()));
99 #endif
100 		exit(1);
101 	}
102 	if(connect(fd, (struct sockaddr*)&addr, addrlen) < 0) {
103 #ifndef USE_WINSOCK
104 		perror("connect() error");
105 #else
106 		printf("connect: %s\n", wsa_strerror(WSAGetLastError()));
107 #endif
108 		exit(1);
109 	}
110 	return fd;
111 }
112 
113 /** write a query over the TCP fd */
114 static void
115 write_q(int fd, int udp, SSL* ssl, sldns_buffer* buf, uint16_t id,
116 	const char* strname, const char* strtype, const char* strclass)
117 {
118 	struct query_info qinfo;
119 	uint16_t len;
120 	/* qname */
121 	qinfo.qname = sldns_str2wire_dname(strname, &qinfo.qname_len);
122 	if(!qinfo.qname) {
123 		printf("cannot parse query name: '%s'\n", strname);
124 		exit(1);
125 	}
126 
127 	/* qtype and qclass */
128 	qinfo.qtype = sldns_get_rr_type_by_name(strtype);
129 	qinfo.qclass = sldns_get_rr_class_by_name(strclass);
130 
131 	/* clear local alias */
132 	qinfo.local_alias = NULL;
133 
134 	/* make query */
135 	qinfo_query_encode(buf, &qinfo);
136 	sldns_buffer_write_u16_at(buf, 0, id);
137 	sldns_buffer_write_u16_at(buf, 2, BIT_RD);
138 
139 	if(1) {
140 		/* add EDNS DO */
141 		struct edns_data edns;
142 		memset(&edns, 0, sizeof(edns));
143 		edns.edns_present = 1;
144 		edns.bits = EDNS_DO;
145 		edns.udp_size = 4096;
146 		if(sldns_buffer_capacity(buf) >=
147 			sldns_buffer_limit(buf)+calc_edns_field_size(&edns))
148 			attach_edns_record(buf, &edns);
149 	}
150 
151 	/* send it */
152 	if(!udp) {
153 		len = (uint16_t)sldns_buffer_limit(buf);
154 		len = htons(len);
155 		if(ssl) {
156 			if(SSL_write(ssl, (void*)&len, (int)sizeof(len)) <= 0) {
157 				log_crypto_err("cannot SSL_write");
158 				exit(1);
159 			}
160 		} else {
161 			if(send(fd, (void*)&len, sizeof(len), 0) <
162 				(ssize_t)sizeof(len)){
163 #ifndef USE_WINSOCK
164 				perror("send() len failed");
165 #else
166 				printf("send len: %s\n",
167 					wsa_strerror(WSAGetLastError()));
168 #endif
169 				exit(1);
170 			}
171 		}
172 	}
173 	if(ssl) {
174 		if(SSL_write(ssl, (void*)sldns_buffer_begin(buf),
175 			(int)sldns_buffer_limit(buf)) <= 0) {
176 			log_crypto_err("cannot SSL_write");
177 			exit(1);
178 		}
179 	} else {
180 		if(send(fd, (void*)sldns_buffer_begin(buf),
181 			sldns_buffer_limit(buf), 0) <
182 			(ssize_t)sldns_buffer_limit(buf)) {
183 #ifndef USE_WINSOCK
184 			perror("send() data failed");
185 #else
186 			printf("send data: %s\n", wsa_strerror(WSAGetLastError()));
187 #endif
188 			exit(1);
189 		}
190 	}
191 
192 	free(qinfo.qname);
193 }
194 
195 /** receive DNS datagram over TCP and print it */
196 static void
197 recv_one(int fd, int udp, SSL* ssl, sldns_buffer* buf)
198 {
199 	char* pktstr;
200 	uint16_t len;
201 	if(!udp) {
202 		if(ssl) {
203 			if(SSL_read(ssl, (void*)&len, (int)sizeof(len)) <= 0) {
204 				log_crypto_err("could not SSL_read");
205 				exit(1);
206 			}
207 		} else {
208 			if(recv(fd, (void*)&len, sizeof(len), 0) <
209 				(ssize_t)sizeof(len)) {
210 #ifndef USE_WINSOCK
211 				perror("read() len failed");
212 #else
213 				printf("read len: %s\n",
214 					wsa_strerror(WSAGetLastError()));
215 #endif
216 				exit(1);
217 			}
218 		}
219 		len = ntohs(len);
220 		sldns_buffer_clear(buf);
221 		sldns_buffer_set_limit(buf, len);
222 		if(ssl) {
223 			int r = SSL_read(ssl, (void*)sldns_buffer_begin(buf),
224 				(int)len);
225 			if(r <= 0) {
226 				log_crypto_err("could not SSL_read");
227 				exit(1);
228 			}
229 			if(r != (int)len)
230 				fatal_exit("ssl_read %d of %d", r, len);
231 		} else {
232 			if(recv(fd, (void*)sldns_buffer_begin(buf), len, 0) <
233 				(ssize_t)len) {
234 #ifndef USE_WINSOCK
235 				perror("read() data failed");
236 #else
237 				printf("read data: %s\n",
238 					wsa_strerror(WSAGetLastError()));
239 #endif
240 				exit(1);
241 			}
242 		}
243 	} else {
244 		ssize_t l;
245 		sldns_buffer_clear(buf);
246 		if((l=recv(fd, (void*)sldns_buffer_begin(buf),
247 			sldns_buffer_capacity(buf), 0)) < 0) {
248 #ifndef USE_WINSOCK
249 			perror("read() data failed");
250 #else
251 			printf("read data: %s\n",
252 				wsa_strerror(WSAGetLastError()));
253 #endif
254 			exit(1);
255 		}
256 		sldns_buffer_set_limit(buf, (size_t)l);
257 		len = (size_t)l;
258 	}
259 	printf("\nnext received packet\n");
260 	log_buf(0, "data", buf);
261 
262 	pktstr = sldns_wire2str_pkt(sldns_buffer_begin(buf), len);
263 	printf("%s", pktstr);
264 	free(pktstr);
265 }
266 
267 static int get_random(void)
268 {
269 	int r;
270 	if (RAND_bytes((unsigned char*)&r, (int)sizeof(r)) == 1) {
271 		return r;
272 	}
273 	return arc4random();
274 }
275 
276 /** send the TCP queries and print answers */
277 static void
278 send_em(const char* svr, int udp, int usessl, int noanswer, int num, char** qs)
279 {
280 	sldns_buffer* buf = sldns_buffer_new(65553);
281 	int fd = open_svr(svr, udp);
282 	int i;
283 	SSL_CTX* ctx = NULL;
284 	SSL* ssl = NULL;
285 	if(!buf) fatal_exit("out of memory");
286 	if(usessl) {
287 		ctx = connect_sslctx_create(NULL, NULL, NULL);
288 		if(!ctx) fatal_exit("cannot create ssl ctx");
289 		ssl = outgoing_ssl_fd(ctx, fd);
290 		if(!ssl) fatal_exit("cannot create ssl");
291 		while(1) {
292 			int r;
293 			ERR_clear_error();
294 			if( (r=SSL_do_handshake(ssl)) == 1)
295 				break;
296 			r = SSL_get_error(ssl, r);
297 			if(r != SSL_ERROR_WANT_READ &&
298 				r != SSL_ERROR_WANT_WRITE) {
299 				log_crypto_err("could not ssl_handshake");
300 				exit(1);
301 			}
302 		}
303 		if(1) {
304 			X509* x = SSL_get_peer_certificate(ssl);
305 			if(!x) printf("SSL: no peer certificate\n");
306 			else {
307 				X509_print_fp(stdout, x);
308 				X509_free(x);
309 			}
310 		}
311 	}
312 	for(i=0; i<num; i+=3) {
313 		printf("\nNext query is %s %s %s\n", qs[i], qs[i+1], qs[i+2]);
314 		write_q(fd, udp, ssl, buf, (uint16_t)get_random(), qs[i],
315 			qs[i+1], qs[i+2]);
316 		/* print at least one result */
317 		if(!noanswer)
318 			recv_one(fd, udp, ssl, buf);
319 	}
320 
321 	if(usessl) {
322 		SSL_shutdown(ssl);
323 		SSL_free(ssl);
324 		SSL_CTX_free(ctx);
325 	}
326 #ifndef USE_WINSOCK
327 	close(fd);
328 #else
329 	closesocket(fd);
330 #endif
331 	sldns_buffer_free(buf);
332 	printf("orderly exit\n");
333 }
334 
335 #ifdef SIGPIPE
336 /** SIGPIPE handler */
337 static RETSIGTYPE sigh(int sig)
338 {
339 	if(sig == SIGPIPE) {
340 		printf("got SIGPIPE, remote connection gone\n");
341 		exit(1);
342 	}
343 	printf("Got unhandled signal %d\n", sig);
344 	exit(1);
345 }
346 #endif /* SIGPIPE */
347 
348 /** getopt global, in case header files fail to declare it. */
349 extern int optind;
350 /** getopt global, in case header files fail to declare it. */
351 extern char* optarg;
352 
353 /** main program for streamtcp */
354 int main(int argc, char** argv)
355 {
356 	int c;
357 	const char* svr = "127.0.0.1";
358 	int udp = 0;
359 	int noanswer = 0;
360 	int usessl = 0;
361 
362 #ifdef USE_WINSOCK
363 	WSADATA wsa_data;
364 	if(WSAStartup(MAKEWORD(2,2), &wsa_data) != 0) {
365 		printf("WSAStartup failed\n");
366 		return 1;
367 	}
368 #endif
369 
370 	/* lock debug start (if any) */
371 	log_init(0, 0, 0);
372 	checklock_start();
373 
374 #ifdef SIGPIPE
375 	if(signal(SIGPIPE, &sigh) == SIG_ERR) {
376 		perror("could not install signal handler");
377 		return 1;
378 	}
379 #endif
380 
381 	/* command line options */
382 	if(argc == 1) {
383 		usage(argv);
384 	}
385 	while( (c=getopt(argc, argv, "f:hnsu")) != -1) {
386 		switch(c) {
387 			case 'f':
388 				svr = optarg;
389 				break;
390 			case 'n':
391 				noanswer = 1;
392 				break;
393 			case 'u':
394 				udp = 1;
395 				break;
396 			case 's':
397 				usessl = 1;
398 				break;
399 			case 'h':
400 			case '?':
401 			default:
402 				usage(argv);
403 		}
404 	}
405 	argc -= optind;
406 	argv += optind;
407 
408 	if(argc % 3 != 0) {
409 		printf("queries must be multiples of name,type,class\n");
410 		return 1;
411 	}
412 	if(usessl) {
413 #if OPENSSL_VERSION_NUMBER < 0x10100000 || !defined(HAVE_OPENSSL_INIT_SSL)
414 		ERR_load_SSL_strings();
415 #endif
416 #if OPENSSL_VERSION_NUMBER < 0x10100000 || !defined(HAVE_OPENSSL_INIT_CRYPTO)
417 		OpenSSL_add_all_algorithms();
418 #else
419 		OPENSSL_init_crypto(OPENSSL_INIT_ADD_ALL_CIPHERS
420 			| OPENSSL_INIT_ADD_ALL_DIGESTS
421 			| OPENSSL_INIT_LOAD_CRYPTO_STRINGS, NULL);
422 #endif
423 #if OPENSSL_VERSION_NUMBER < 0x10100000 || !defined(HAVE_OPENSSL_INIT_SSL)
424 		(void)SSL_library_init();
425 #else
426 		(void)OPENSSL_init_ssl(OPENSSL_INIT_LOAD_SSL_STRINGS, NULL);
427 #endif
428 	}
429 	send_em(svr, udp, usessl, noanswer, argc, argv);
430 	checklock_stop();
431 #ifdef USE_WINSOCK
432 	WSACleanup();
433 #endif
434 	return 0;
435 }
436