xref: /openbsd-src/usr.sbin/unbound/testcode/dohclient.c (revision 9c7f0a49a49eaaf8a541e2c1c60150e851bc44d2)
1e2a0f313Ssthen /*
2e2a0f313Ssthen  * testcode/dohclient.c - debug program. Perform multiple DNS queries using DoH.
3e2a0f313Ssthen  *
4e2a0f313Ssthen  * Copyright (c) 2020, NLnet Labs. All rights reserved.
5e2a0f313Ssthen  *
6e2a0f313Ssthen  * This software is open source.
7e2a0f313Ssthen  *
8e2a0f313Ssthen  * Redistribution and use in source and binary forms, with or without
9e2a0f313Ssthen  * modification, are permitted provided that the following conditions
10e2a0f313Ssthen  * are met:
11e2a0f313Ssthen  *
12e2a0f313Ssthen  * Redistributions of source code must retain the above copyright notice,
13e2a0f313Ssthen  * this list of conditions and the following disclaimer.
14e2a0f313Ssthen  *
15e2a0f313Ssthen  * Redistributions in binary form must reproduce the above copyright notice,
16e2a0f313Ssthen  * this list of conditions and the following disclaimer in the documentation
17e2a0f313Ssthen  * and/or other materials provided with the distribution.
18e2a0f313Ssthen  *
19e2a0f313Ssthen  * Neither the name of the NLNET LABS nor the names of its contributors may
20e2a0f313Ssthen  * be used to endorse or promote products derived from this software without
21e2a0f313Ssthen  * specific prior written permission.
22e2a0f313Ssthen  *
23e2a0f313Ssthen  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
24e2a0f313Ssthen  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
25e2a0f313Ssthen  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
26e2a0f313Ssthen  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
27e2a0f313Ssthen  * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
28e2a0f313Ssthen  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
29e2a0f313Ssthen  * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
30e2a0f313Ssthen  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
31e2a0f313Ssthen  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
32e2a0f313Ssthen  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
33e2a0f313Ssthen  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34e2a0f313Ssthen  */
35e2a0f313Ssthen 
36e2a0f313Ssthen /**
37e2a0f313Ssthen  * \file
38e2a0f313Ssthen  *
39e2a0f313Ssthen  * Simple DNS-over-HTTPS client. For testing and debugging purposes.
40e2a0f313Ssthen  * No authentication of TLS cert.
41e2a0f313Ssthen  */
42e2a0f313Ssthen 
43e2a0f313Ssthen #include "config.h"
44e2a0f313Ssthen #ifdef HAVE_GETOPT_H
45e2a0f313Ssthen #include <getopt.h>
46e2a0f313Ssthen #endif
47e2a0f313Ssthen #include "sldns/wire2str.h"
48e2a0f313Ssthen #include "sldns/sbuffer.h"
49e2a0f313Ssthen #include "sldns/str2wire.h"
50e2a0f313Ssthen #include "sldns/parseutil.h"
51e2a0f313Ssthen #include "util/data/msgencode.h"
52e2a0f313Ssthen #include "util/data/msgreply.h"
53e2a0f313Ssthen #include "util/data/msgparse.h"
54e2a0f313Ssthen #include "util/net_help.h"
55e2a0f313Ssthen #include <openssl/ssl.h>
56e2a0f313Ssthen #include <openssl/err.h>
57e2a0f313Ssthen #ifdef HAVE_NGHTTP2
58e2a0f313Ssthen #include <nghttp2/nghttp2.h>
59e2a0f313Ssthen 
60e2a0f313Ssthen struct http2_session {
61e2a0f313Ssthen 	nghttp2_session* session;
62e2a0f313Ssthen 	SSL* ssl;
63e2a0f313Ssthen 	int fd;
64e2a0f313Ssthen 	int query_count;
65e2a0f313Ssthen 	/* Use POST :method if 1 */
66e2a0f313Ssthen 	int post;
67e2a0f313Ssthen 	int block_select;
68e2a0f313Ssthen 	const char* authority;
69e2a0f313Ssthen 	const char* endpoint;
70e2a0f313Ssthen 	const char* content_type;
71e2a0f313Ssthen };
72e2a0f313Ssthen 
73e2a0f313Ssthen struct http2_stream {
74e2a0f313Ssthen 	int32_t stream_id;
75e2a0f313Ssthen 	int res_status;
76e2a0f313Ssthen 	struct sldns_buffer* buf;
77e2a0f313Ssthen 	char* path;
78e2a0f313Ssthen };
79e2a0f313Ssthen 
usage(char * argv[])80e2a0f313Ssthen static void usage(char* argv[])
81e2a0f313Ssthen {
82e2a0f313Ssthen 	printf("usage: %s [options] name type class ...\n", argv[0]);
83e2a0f313Ssthen 	printf("	sends the name-type-class queries over "
84e2a0f313Ssthen 			"DNS-over-HTTPS.\n");
85e2a0f313Ssthen 	printf("-s server	IP address to send the queries to, "
86e2a0f313Ssthen 			"default: 127.0.0.1\n");
87e2a0f313Ssthen 	printf("-p		Port to connect to, default: %d\n",
88e2a0f313Ssthen 		UNBOUND_DNS_OVER_HTTPS_PORT);
89e2a0f313Ssthen 	printf("-P		Use POST method instead of default GET\n");
90e2a0f313Ssthen 	printf("-e		HTTP endpoint, default: /dns-query\n");
91e2a0f313Ssthen 	printf("-c		Content-type in request, default: "
92e2a0f313Ssthen 		"application/dns-message\n");
9372f58708Ssthen 	printf("-n		no-tls, TLS is disabled\n");
94e2a0f313Ssthen 	printf("-h 		This help text\n");
95e2a0f313Ssthen 	exit(1);
96e2a0f313Ssthen }
97e2a0f313Ssthen 
98e2a0f313Ssthen /** open TCP socket to svr */
99e2a0f313Ssthen static int
open_svr(const char * svr,int port)100e2a0f313Ssthen open_svr(const char* svr, int port)
101e2a0f313Ssthen {
102e2a0f313Ssthen 	struct sockaddr_storage addr;
103e2a0f313Ssthen 	socklen_t addrlen;
104e2a0f313Ssthen 	int fd = -1;
105e2a0f313Ssthen 	int r;
106e2a0f313Ssthen 	if(!ipstrtoaddr(svr, port, &addr, &addrlen)) {
107e2a0f313Ssthen 		printf("fatal: bad server specs '%s'\n", svr);
108e2a0f313Ssthen 		exit(1);
109e2a0f313Ssthen 	}
110e2a0f313Ssthen 
111e2a0f313Ssthen 	fd = socket(addr_is_ip6(&addr, addrlen)?PF_INET6:PF_INET,
112e2a0f313Ssthen 		SOCK_STREAM, 0);
113e2a0f313Ssthen 	if(fd == -1) {
114e2a0f313Ssthen 		perror("socket() error");
115e2a0f313Ssthen 		exit(1);
116e2a0f313Ssthen 	}
117e2a0f313Ssthen 	r = connect(fd, (struct sockaddr*)&addr, addrlen);
118e2a0f313Ssthen 	if(r < 0 && r != EINPROGRESS) {
119e2a0f313Ssthen 		perror("connect() error");
120e2a0f313Ssthen 		exit(1);
121e2a0f313Ssthen 	}
122e2a0f313Ssthen 	return fd;
123e2a0f313Ssthen }
124e2a0f313Ssthen 
http2_submit_request_read_cb(nghttp2_session * ATTR_UNUSED (session),int32_t ATTR_UNUSED (stream_id),uint8_t * buf,size_t length,uint32_t * data_flags,nghttp2_data_source * source,void * ATTR_UNUSED (cb_arg))125e2a0f313Ssthen static ssize_t http2_submit_request_read_cb(
126e2a0f313Ssthen 	nghttp2_session* ATTR_UNUSED(session),
127e2a0f313Ssthen 	int32_t ATTR_UNUSED(stream_id), uint8_t* buf, size_t length,
128e2a0f313Ssthen 	uint32_t* data_flags, nghttp2_data_source* source,
129e2a0f313Ssthen 	void* ATTR_UNUSED(cb_arg))
130e2a0f313Ssthen {
131e2a0f313Ssthen 	if(length > sldns_buffer_remaining(source->ptr))
132e2a0f313Ssthen 		length = sldns_buffer_remaining(source->ptr);
133e2a0f313Ssthen 
134e2a0f313Ssthen 	memcpy(buf, sldns_buffer_current(source->ptr), length);
135e2a0f313Ssthen 	sldns_buffer_skip(source->ptr, length);
136e2a0f313Ssthen 
137e2a0f313Ssthen 	if(sldns_buffer_remaining(source->ptr) == 0) {
138e2a0f313Ssthen 		*data_flags |= NGHTTP2_DATA_FLAG_EOF;
139e2a0f313Ssthen 	}
140e2a0f313Ssthen 
141e2a0f313Ssthen 	return length;
142e2a0f313Ssthen }
143e2a0f313Ssthen 
144e2a0f313Ssthen static void
submit_query(struct http2_session * h2_session,struct sldns_buffer * buf)145e2a0f313Ssthen submit_query(struct http2_session* h2_session, struct sldns_buffer* buf)
146e2a0f313Ssthen {
147e2a0f313Ssthen 	int32_t stream_id;
148e2a0f313Ssthen 	struct http2_stream* h2_stream;
149e2a0f313Ssthen 	nghttp2_nv headers[5];
150e2a0f313Ssthen 	char* qb64;
151e2a0f313Ssthen 	size_t qb64_size;
152e2a0f313Ssthen 	size_t qb64_expected_size;
153e2a0f313Ssthen 	size_t i;
154e2a0f313Ssthen 	nghttp2_data_provider data_prd;
155e2a0f313Ssthen 
156e2a0f313Ssthen 	h2_stream = calloc(1,  sizeof(*h2_stream));
157e2a0f313Ssthen 	if(!h2_stream)
158e2a0f313Ssthen 		fatal_exit("could not malloc http2 stream");
159e2a0f313Ssthen 	h2_stream->buf = buf;
160e2a0f313Ssthen 
161e2a0f313Ssthen 	if(h2_session->post) {
162e2a0f313Ssthen 		data_prd.source.ptr = buf;
163e2a0f313Ssthen 		data_prd.read_callback = http2_submit_request_read_cb;
164e2a0f313Ssthen 		h2_stream->path = (char*)h2_session->endpoint;
165e2a0f313Ssthen 	} else {
166e2a0f313Ssthen 		qb64_expected_size = sldns_b64_ntop_calculate_size(
167e2a0f313Ssthen 				sldns_buffer_remaining(buf));
168e2a0f313Ssthen 		qb64 = malloc(qb64_expected_size);
169e2a0f313Ssthen 		if(!qb64) fatal_exit("out of memory");
170e2a0f313Ssthen 		qb64_size = sldns_b64url_ntop(sldns_buffer_begin(buf),
171e2a0f313Ssthen 			sldns_buffer_remaining(buf), qb64, qb64_expected_size);
172e2a0f313Ssthen 		h2_stream->path = malloc(strlen(
173e2a0f313Ssthen 			h2_session->endpoint)+strlen("?dns=")+qb64_size+1);
174e2a0f313Ssthen 		if(!h2_stream->path) fatal_exit("out of memory");
175e2a0f313Ssthen 		snprintf(h2_stream->path, strlen(h2_session->endpoint)+
176e2a0f313Ssthen 			strlen("?dns=")+qb64_size+1, "%s?dns=%s",
177e2a0f313Ssthen 			h2_session->endpoint, qb64);
178e2a0f313Ssthen 		free(qb64);
179e2a0f313Ssthen 	}
180e2a0f313Ssthen 
181e2a0f313Ssthen 	headers[0].name = (uint8_t*)":method";
182e2a0f313Ssthen 	if(h2_session->post)
183e2a0f313Ssthen 		headers[0].value = (uint8_t*)"POST";
184e2a0f313Ssthen 	else
185e2a0f313Ssthen 		headers[0].value = (uint8_t*)"GET";
186e2a0f313Ssthen 	headers[1].name = (uint8_t*)":path";
187e2a0f313Ssthen 	headers[1].value = (uint8_t*)h2_stream->path;
188e2a0f313Ssthen 	headers[2].name = (uint8_t*)":scheme";
18972f58708Ssthen 	if(h2_session->ssl)
190e2a0f313Ssthen 		headers[2].value = (uint8_t*)"https";
19172f58708Ssthen 	else
19272f58708Ssthen 		headers[2].value = (uint8_t*)"http";
193e2a0f313Ssthen 	headers[3].name = (uint8_t*)":authority";
194e2a0f313Ssthen 	headers[3].value = (uint8_t*)h2_session->authority;
195e2a0f313Ssthen 	headers[4].name = (uint8_t*)"content-type";
196e2a0f313Ssthen 	headers[4].value = (uint8_t*)h2_session->content_type;
197e2a0f313Ssthen 
198e2a0f313Ssthen 	printf("Request headers\n");
199e2a0f313Ssthen 	for(i=0; i<sizeof(headers)/sizeof(headers[0]); i++) {
200e2a0f313Ssthen 		headers[i].namelen = strlen((char*)headers[i].name);
201e2a0f313Ssthen 		headers[i].valuelen = strlen((char*)headers[i].value);
202e2a0f313Ssthen 		headers[i].flags = NGHTTP2_NV_FLAG_NONE;
203e2a0f313Ssthen 		printf("%s: %s\n", headers[i].name, headers[i].value);
204e2a0f313Ssthen 	}
205e2a0f313Ssthen 
206e2a0f313Ssthen 	stream_id = nghttp2_submit_request(h2_session->session, NULL, headers,
207e2a0f313Ssthen 		sizeof(headers)/sizeof(headers[0]),
208e2a0f313Ssthen 		(h2_session->post) ? &data_prd : NULL, h2_stream);
209e2a0f313Ssthen 	if(stream_id < 0) {
210e2a0f313Ssthen 		printf("Failed to submit nghttp2 request");
211e2a0f313Ssthen 		exit(1);
212e2a0f313Ssthen 	}
213e2a0f313Ssthen 	h2_session->query_count++;
214e2a0f313Ssthen 	h2_stream->stream_id = stream_id;
215e2a0f313Ssthen }
216e2a0f313Ssthen 
217e2a0f313Ssthen static sldns_buffer*
make_query(char * qname,char * qtype,char * qclass)218e2a0f313Ssthen make_query(char* qname, char* qtype, char* qclass)
219e2a0f313Ssthen {
220e2a0f313Ssthen 	struct query_info qinfo;
221e2a0f313Ssthen 	struct edns_data edns;
222e2a0f313Ssthen 	sldns_buffer* buf = sldns_buffer_new(65553);
223e2a0f313Ssthen 	if(!buf) fatal_exit("out of memory");
224e2a0f313Ssthen 	qinfo.qname = sldns_str2wire_dname(qname, &qinfo.qname_len);
225e2a0f313Ssthen 	if(!qinfo.qname) {
226e2a0f313Ssthen 		printf("cannot parse query name: '%s'\n", qname);
227e2a0f313Ssthen 		exit(1);
228e2a0f313Ssthen 	}
229e2a0f313Ssthen 	qinfo.qtype = sldns_get_rr_type_by_name(qtype);
230437d2860Ssthen 	if(qinfo.qtype == 0 && strcmp(qtype, "TYPE0") != 0) {
231437d2860Ssthen 		printf("cannot parse query type: '%s'\n", qtype);
232437d2860Ssthen 		exit(1);
233437d2860Ssthen 	}
234e2a0f313Ssthen 	qinfo.qclass = sldns_get_rr_class_by_name(qclass);
235437d2860Ssthen 	if(qinfo.qclass == 0 && strcmp(qclass, "CLASS0") != 0) {
236437d2860Ssthen 		printf("cannot parse query class: '%s'\n", qclass);
237437d2860Ssthen 		exit(1);
238437d2860Ssthen 	}
239e2a0f313Ssthen 	qinfo.local_alias = NULL;
240e2a0f313Ssthen 
241e2a0f313Ssthen 	qinfo_query_encode(buf, &qinfo); /* flips buffer */
242e2a0f313Ssthen 	free(qinfo.qname);
243e2a0f313Ssthen 	sldns_buffer_write_u16_at(buf, 0, 0x0000);
244e2a0f313Ssthen 	sldns_buffer_write_u16_at(buf, 2, BIT_RD);
245e2a0f313Ssthen 	memset(&edns, 0, sizeof(edns));
246e2a0f313Ssthen 	edns.edns_present = 1;
247e2a0f313Ssthen 	edns.bits = EDNS_DO;
248e2a0f313Ssthen 	edns.udp_size = 4096;
249e2a0f313Ssthen 	if(sldns_buffer_capacity(buf) >=
250e2a0f313Ssthen 		sldns_buffer_limit(buf)+calc_edns_field_size(&edns))
251e2a0f313Ssthen 		attach_edns_record(buf, &edns);
252e2a0f313Ssthen 	return buf;
253e2a0f313Ssthen }
254e2a0f313Ssthen 
http2_recv_cb(nghttp2_session * ATTR_UNUSED (session),uint8_t * buf,size_t len,int ATTR_UNUSED (flags),void * cb_arg)255e2a0f313Ssthen static ssize_t http2_recv_cb(nghttp2_session* ATTR_UNUSED(session),
256e2a0f313Ssthen 	uint8_t* buf, size_t len, int ATTR_UNUSED(flags), void* cb_arg)
257e2a0f313Ssthen {
258e2a0f313Ssthen 	struct http2_session* h2_session = (struct http2_session*)cb_arg;
259e2a0f313Ssthen 	int r;
26072f58708Ssthen 	ssize_t ret;
261e2a0f313Ssthen 	struct timeval tv, *waittv;
262e2a0f313Ssthen 	fd_set rfd;
263e2a0f313Ssthen 	ERR_clear_error();
264e2a0f313Ssthen 
265e2a0f313Ssthen 	memset(&tv, 0, sizeof(tv));
266e2a0f313Ssthen 
267e2a0f313Ssthen 	if(h2_session->block_select && h2_session->query_count <= 0) {
268e2a0f313Ssthen 		return NGHTTP2_ERR_WOULDBLOCK;
269e2a0f313Ssthen 	}
270e2a0f313Ssthen 	if(h2_session->block_select)
271e2a0f313Ssthen 		waittv = NULL;
272e2a0f313Ssthen 	else
273e2a0f313Ssthen 		waittv = &tv;
274e2a0f313Ssthen 	memset(&rfd, 0, sizeof(rfd));
275e2a0f313Ssthen 	FD_ZERO(&rfd);
276e2a0f313Ssthen 	FD_SET(h2_session->fd, &rfd);
277e2a0f313Ssthen 	r = select(h2_session->fd+1, &rfd, NULL, NULL, waittv);
278e2a0f313Ssthen 	if(r <= 0) {
279e2a0f313Ssthen 		return NGHTTP2_ERR_WOULDBLOCK;
280e2a0f313Ssthen 	}
281e2a0f313Ssthen 
28272f58708Ssthen 	if(h2_session->ssl) {
283e2a0f313Ssthen 		r = SSL_read(h2_session->ssl, buf, len);
284e2a0f313Ssthen 		if(r <= 0) {
285e2a0f313Ssthen 			int want = SSL_get_error(h2_session->ssl, r);
286e2a0f313Ssthen 			if(want == SSL_ERROR_ZERO_RETURN) {
287e2a0f313Ssthen 				return NGHTTP2_ERR_EOF;
288e2a0f313Ssthen 			}
289*9c7f0a49Ssthen 			log_crypto_err_io("could not SSL_read", want);
290e2a0f313Ssthen 			return NGHTTP2_ERR_EOF;
291e2a0f313Ssthen 		}
292e2a0f313Ssthen 		return r;
293e2a0f313Ssthen 	}
294e2a0f313Ssthen 
29572f58708Ssthen 	ret = read(h2_session->fd, buf, len);
29672f58708Ssthen 	if(ret == 0) {
29772f58708Ssthen 		return NGHTTP2_ERR_EOF;
29872f58708Ssthen 	} else if(ret < 0) {
29972f58708Ssthen 		log_err("could not http2 read: %s", strerror(errno));
30072f58708Ssthen 		return NGHTTP2_ERR_EOF;
30172f58708Ssthen 	}
30272f58708Ssthen 	return ret;
30372f58708Ssthen }
30472f58708Ssthen 
http2_send_cb(nghttp2_session * ATTR_UNUSED (session),const uint8_t * buf,size_t len,int ATTR_UNUSED (flags),void * cb_arg)305e2a0f313Ssthen static ssize_t http2_send_cb(nghttp2_session* ATTR_UNUSED(session),
306e2a0f313Ssthen 	const uint8_t* buf, size_t len, int ATTR_UNUSED(flags), void* cb_arg)
307e2a0f313Ssthen {
308e2a0f313Ssthen 	struct http2_session* h2_session = (struct http2_session*)cb_arg;
30972f58708Ssthen 	ssize_t ret;
310e2a0f313Ssthen 
31172f58708Ssthen 	if(h2_session->ssl) {
312e2a0f313Ssthen 		int r;
313e2a0f313Ssthen 		ERR_clear_error();
314e2a0f313Ssthen 		r = SSL_write(h2_session->ssl, buf, len);
315e2a0f313Ssthen 		if(r <= 0) {
316e2a0f313Ssthen 			int want = SSL_get_error(h2_session->ssl, r);
317e2a0f313Ssthen 			if(want == SSL_ERROR_ZERO_RETURN) {
318e2a0f313Ssthen 				return NGHTTP2_ERR_CALLBACK_FAILURE;
319e2a0f313Ssthen 			}
320*9c7f0a49Ssthen 			log_crypto_err_io("could not SSL_write", want);
321e2a0f313Ssthen 			return NGHTTP2_ERR_CALLBACK_FAILURE;
322e2a0f313Ssthen 		}
323e2a0f313Ssthen 		return r;
324e2a0f313Ssthen 	}
325e2a0f313Ssthen 
32672f58708Ssthen 	ret = write(h2_session->fd, buf, len);
32772f58708Ssthen 	if(ret == 0) {
32872f58708Ssthen 		return NGHTTP2_ERR_CALLBACK_FAILURE;
32972f58708Ssthen 	} else if(ret < 0) {
33072f58708Ssthen 		log_err("could not http2 write: %s", strerror(errno));
33172f58708Ssthen 		return NGHTTP2_ERR_CALLBACK_FAILURE;
33272f58708Ssthen 	}
33372f58708Ssthen 	return ret;
33472f58708Ssthen }
33572f58708Ssthen 
http2_stream_close_cb(nghttp2_session * ATTR_UNUSED (session),int32_t ATTR_UNUSED (stream_id),nghttp2_error_code ATTR_UNUSED (error_code),void * cb_arg)336e2a0f313Ssthen static int http2_stream_close_cb(nghttp2_session* ATTR_UNUSED(session),
337e2a0f313Ssthen 	int32_t ATTR_UNUSED(stream_id),
338e2a0f313Ssthen 	nghttp2_error_code ATTR_UNUSED(error_code), void *cb_arg)
339e2a0f313Ssthen {
340e2a0f313Ssthen 	struct http2_session* h2_session = (struct http2_session*)cb_arg;
341e2a0f313Ssthen 	struct http2_stream* h2_stream;
342e2a0f313Ssthen 	if(!(h2_stream = nghttp2_session_get_stream_user_data(
343e2a0f313Ssthen 		h2_session->session, stream_id))) {
344e2a0f313Ssthen 		return 0;
345e2a0f313Ssthen 	}
346e2a0f313Ssthen 	h2_session->query_count--;
347e2a0f313Ssthen 	sldns_buffer_free(h2_stream->buf);
348e2a0f313Ssthen 	if(!h2_session->post)
349e2a0f313Ssthen 		free(h2_stream->path);
350e2a0f313Ssthen 	free(h2_stream);
351e2a0f313Ssthen 	h2_stream = NULL;
352e2a0f313Ssthen 	return 0;
353e2a0f313Ssthen }
354e2a0f313Ssthen 
http2_data_chunk_recv_cb(nghttp2_session * ATTR_UNUSED (session),uint8_t ATTR_UNUSED (flags),int32_t stream_id,const uint8_t * data,size_t len,void * cb_arg)355e2a0f313Ssthen static int http2_data_chunk_recv_cb(nghttp2_session* ATTR_UNUSED(session),
356e2a0f313Ssthen 	uint8_t ATTR_UNUSED(flags), int32_t stream_id, const uint8_t* data,
357e2a0f313Ssthen 	size_t len, void* cb_arg)
358e2a0f313Ssthen {
359e2a0f313Ssthen 	struct http2_session* h2_session = (struct http2_session*)cb_arg;
360e2a0f313Ssthen 	struct http2_stream* h2_stream;
361e2a0f313Ssthen 
362e2a0f313Ssthen 	if(!(h2_stream = nghttp2_session_get_stream_user_data(
363e2a0f313Ssthen 		h2_session->session, stream_id))) {
364e2a0f313Ssthen 		return 0;
365e2a0f313Ssthen 	}
366e2a0f313Ssthen 
367e2a0f313Ssthen 	if(sldns_buffer_remaining(h2_stream->buf) < len) {
36883152a15Ssthen 		log_err("received data chunk does not fit into buffer");
369e2a0f313Ssthen 		return NGHTTP2_ERR_CALLBACK_FAILURE;
370e2a0f313Ssthen 	}
371e2a0f313Ssthen 
372e2a0f313Ssthen 	sldns_buffer_write(h2_stream->buf, data, len);
373e2a0f313Ssthen 
374e2a0f313Ssthen 	return 0;
375e2a0f313Ssthen }
376e2a0f313Ssthen 
http2_frame_recv_cb(nghttp2_session * session,const nghttp2_frame * frame,void * ATTR_UNUSED (cb_arg))377e2a0f313Ssthen static int http2_frame_recv_cb(nghttp2_session *session,
378e2a0f313Ssthen 	const nghttp2_frame *frame, void* ATTR_UNUSED(cb_arg))
379e2a0f313Ssthen {
380e2a0f313Ssthen 	struct http2_stream* h2_stream;
381e2a0f313Ssthen 
382e2a0f313Ssthen 	if(!(h2_stream = nghttp2_session_get_stream_user_data(
383e2a0f313Ssthen 		session, frame->hd.stream_id)))
384e2a0f313Ssthen 		return 0;
385e2a0f313Ssthen 	if(frame->hd.type == NGHTTP2_HEADERS &&
386e2a0f313Ssthen 		frame->headers.cat == NGHTTP2_HCAT_RESPONSE) {
387e2a0f313Ssthen 		sldns_buffer_clear(h2_stream->buf);
388e2a0f313Ssthen 	}
389e2a0f313Ssthen 	if(((frame->hd.type != NGHTTP2_DATA &&
390e2a0f313Ssthen 		frame->hd.type != NGHTTP2_HEADERS) ||
391e2a0f313Ssthen 		frame->hd.flags & NGHTTP2_FLAG_END_STREAM) &&
392e2a0f313Ssthen 			h2_stream->res_status == 200) {
393e2a0f313Ssthen 			char* pktstr;
394e2a0f313Ssthen 			sldns_buffer_flip(h2_stream->buf);
395e2a0f313Ssthen 			pktstr = sldns_wire2str_pkt(
396e2a0f313Ssthen 				sldns_buffer_begin(h2_stream->buf),
397e2a0f313Ssthen 				sldns_buffer_limit(h2_stream->buf));
398e2a0f313Ssthen 			printf("%s\n", pktstr);
399e2a0f313Ssthen 			free(pktstr);
400e2a0f313Ssthen 			return 0;
401e2a0f313Ssthen 	}
402e2a0f313Ssthen 	return 0;
403e2a0f313Ssthen }
http2_header_cb(nghttp2_session * ATTR_UNUSED (session),const nghttp2_frame * frame,const uint8_t * name,size_t namelen,const uint8_t * value,size_t ATTR_UNUSED (valuelen),uint8_t ATTR_UNUSED (flags),void * cb_arg)404e2a0f313Ssthen static int http2_header_cb(nghttp2_session* ATTR_UNUSED(session),
405e2a0f313Ssthen 	const nghttp2_frame* frame, const uint8_t* name, size_t namelen,
406e2a0f313Ssthen 	const uint8_t* value, size_t ATTR_UNUSED(valuelen),
407e2a0f313Ssthen 	uint8_t ATTR_UNUSED(flags), void* cb_arg)
408e2a0f313Ssthen {
409e2a0f313Ssthen 	struct http2_stream* h2_stream;
410e2a0f313Ssthen 	struct http2_session* h2_session = (struct http2_session*)cb_arg;
411e2a0f313Ssthen 	printf("%s %s\n", name, value);
412e2a0f313Ssthen 	if(namelen == 7 && memcmp(":status", name, namelen) == 0) {
413e2a0f313Ssthen 		if(!(h2_stream = nghttp2_session_get_stream_user_data(
414e2a0f313Ssthen 			h2_session->session, frame->hd.stream_id))) {
415e2a0f313Ssthen 			return 0;
416e2a0f313Ssthen 		}
417e2a0f313Ssthen 		h2_stream->res_status = atoi((char*)value);
418e2a0f313Ssthen 	}
419e2a0f313Ssthen 	return 0;
420e2a0f313Ssthen }
421e2a0f313Ssthen 
422e2a0f313Ssthen static struct http2_session*
http2_session_create()423e2a0f313Ssthen http2_session_create()
424e2a0f313Ssthen {
425e2a0f313Ssthen 	struct http2_session* h2_session = calloc(1,
426e2a0f313Ssthen 		sizeof(struct http2_session));
427e2a0f313Ssthen 	nghttp2_session_callbacks* callbacks;
428e2a0f313Ssthen 	if(!h2_session)
429e2a0f313Ssthen 		fatal_exit("out of memory");
430e2a0f313Ssthen 
431e2a0f313Ssthen 	if(nghttp2_session_callbacks_new(&callbacks) == NGHTTP2_ERR_NOMEM) {
432e2a0f313Ssthen 		log_err("failed to initialize nghttp2 callback");
433a6cc1574Ssthen 		free(h2_session);
434e2a0f313Ssthen 		return NULL;
435e2a0f313Ssthen 	}
436e2a0f313Ssthen 	nghttp2_session_callbacks_set_recv_callback(callbacks, http2_recv_cb);
437e2a0f313Ssthen 	nghttp2_session_callbacks_set_send_callback(callbacks, http2_send_cb);
438e2a0f313Ssthen 	nghttp2_session_callbacks_set_on_stream_close_callback(callbacks,
439e2a0f313Ssthen 		http2_stream_close_cb);
440e2a0f313Ssthen 	nghttp2_session_callbacks_set_on_data_chunk_recv_callback(callbacks,
441e2a0f313Ssthen 		http2_data_chunk_recv_cb);
442e2a0f313Ssthen 	nghttp2_session_callbacks_set_on_frame_recv_callback(callbacks,
443e2a0f313Ssthen 		http2_frame_recv_cb);
444e2a0f313Ssthen 	nghttp2_session_callbacks_set_on_header_callback(callbacks,
445e2a0f313Ssthen 		http2_header_cb);
446e2a0f313Ssthen 	nghttp2_session_client_new(&h2_session->session, callbacks, h2_session);
447e2a0f313Ssthen 	nghttp2_session_callbacks_del(callbacks);
448e2a0f313Ssthen 	return h2_session;
449e2a0f313Ssthen }
450e2a0f313Ssthen 
451e2a0f313Ssthen static void
http2_session_delete(struct http2_session * h2_session)452e2a0f313Ssthen http2_session_delete(struct http2_session* h2_session)
453e2a0f313Ssthen {
454e2a0f313Ssthen 	nghttp2_session_del(h2_session->session);
455e2a0f313Ssthen 	free(h2_session);
456e2a0f313Ssthen }
457e2a0f313Ssthen 
458e2a0f313Ssthen static void
http2_submit_setting(struct http2_session * h2_session)459e2a0f313Ssthen http2_submit_setting(struct http2_session* h2_session)
460e2a0f313Ssthen {
461e2a0f313Ssthen 	int ret;
462e2a0f313Ssthen 	nghttp2_settings_entry settings[1] = {
463e2a0f313Ssthen 		{NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS,
464e2a0f313Ssthen 		 100}};
465e2a0f313Ssthen 
466e2a0f313Ssthen 	ret = nghttp2_submit_settings(h2_session->session, NGHTTP2_FLAG_NONE,
467e2a0f313Ssthen 		settings, 1);
468e2a0f313Ssthen 	if(ret) {
469e2a0f313Ssthen 		printf("http2: submit_settings failed, "
470e2a0f313Ssthen 			"error: %s\n", nghttp2_strerror(ret));
471e2a0f313Ssthen 		exit(1);
472e2a0f313Ssthen 	}
473e2a0f313Ssthen }
474e2a0f313Ssthen 
475e2a0f313Ssthen static void
http2_write(struct http2_session * h2_session)476e2a0f313Ssthen http2_write(struct http2_session* h2_session)
477e2a0f313Ssthen {
478e2a0f313Ssthen 	if(nghttp2_session_want_write(h2_session->session)) {
479e2a0f313Ssthen 		if(nghttp2_session_send(h2_session->session)) {
480e2a0f313Ssthen 			printf("nghttp2 session send failed\n");
481e2a0f313Ssthen 			exit(1);
482e2a0f313Ssthen 		}
483e2a0f313Ssthen 	}
484e2a0f313Ssthen }
485e2a0f313Ssthen 
486e2a0f313Ssthen static void
http2_read(struct http2_session * h2_session)487e2a0f313Ssthen http2_read(struct http2_session* h2_session)
488e2a0f313Ssthen {
489e2a0f313Ssthen 	if(nghttp2_session_want_read(h2_session->session)) {
490e2a0f313Ssthen 		if(nghttp2_session_recv(h2_session->session)) {
491e2a0f313Ssthen 			printf("nghttp2 session mem_recv failed\n");
492e2a0f313Ssthen 			exit(1);
493e2a0f313Ssthen 		}
494e2a0f313Ssthen 	}
495e2a0f313Ssthen }
496e2a0f313Ssthen 
497e2a0f313Ssthen static void
run(struct http2_session * h2_session,int port,int no_tls,int count,char ** q)49872f58708Ssthen run(struct http2_session* h2_session, int port, int no_tls, int count, char** q)
499e2a0f313Ssthen {
500e2a0f313Ssthen 	int i;
501e2a0f313Ssthen 	SSL_CTX* ctx = NULL;
502e2a0f313Ssthen 	SSL* ssl = NULL;
503e2a0f313Ssthen 	int fd;
504e2a0f313Ssthen 	struct sldns_buffer* buf = NULL;
505e2a0f313Ssthen 
506e2a0f313Ssthen 	fd = open_svr(h2_session->authority, port);
507e2a0f313Ssthen 	h2_session->fd = fd;
508e2a0f313Ssthen 
50972f58708Ssthen 	if(!no_tls) {
510e2a0f313Ssthen 		ctx = connect_sslctx_create(NULL, NULL, NULL, 0);
511e2a0f313Ssthen 		if(!ctx) fatal_exit("cannot create ssl ctx");
512a6cc1574Ssthen #ifdef HAVE_SSL_CTX_SET_ALPN_PROTOS
513e2a0f313Ssthen 		SSL_CTX_set_alpn_protos(ctx, (const unsigned char *)"\x02h2", 3);
514a6cc1574Ssthen #endif
515e2a0f313Ssthen 		ssl = outgoing_ssl_fd(ctx, fd);
516e2a0f313Ssthen 		if(!ssl) {
517e2a0f313Ssthen 			printf("cannot create ssl\n");
518e2a0f313Ssthen 			exit(1);
519e2a0f313Ssthen 		}
520e2a0f313Ssthen 		h2_session->ssl = ssl;
521e2a0f313Ssthen 		while(1) {
522e2a0f313Ssthen 			int r;
523e2a0f313Ssthen 			ERR_clear_error();
524e2a0f313Ssthen 			if( (r=SSL_do_handshake(ssl)) == 1)
525e2a0f313Ssthen 				break;
526e2a0f313Ssthen 			r = SSL_get_error(ssl, r);
527e2a0f313Ssthen 			if(r != SSL_ERROR_WANT_READ &&
528e2a0f313Ssthen 				r != SSL_ERROR_WANT_WRITE) {
529*9c7f0a49Ssthen 				log_crypto_err_io("could not ssl_handshake", r);
530e2a0f313Ssthen 				exit(1);
531e2a0f313Ssthen 			}
532e2a0f313Ssthen 		}
53372f58708Ssthen 	}
534e2a0f313Ssthen 
535e2a0f313Ssthen 	http2_submit_setting(h2_session);
536e2a0f313Ssthen 	http2_write(h2_session);
537e2a0f313Ssthen 	http2_read(h2_session); /* Read setting from remote peer */
538e2a0f313Ssthen 
539e2a0f313Ssthen 	h2_session->block_select = 1;
540e2a0f313Ssthen 
54183152a15Ssthen 	/* handle query */
542e2a0f313Ssthen 	for(i=0; i<count; i+=3) {
543e2a0f313Ssthen 		buf = make_query(q[i], q[i+1], q[i+2]);
544e2a0f313Ssthen 		submit_query(h2_session, buf);
545e2a0f313Ssthen 	}
546e2a0f313Ssthen 	http2_write(h2_session);
547e2a0f313Ssthen 	while(h2_session->query_count) {
548e2a0f313Ssthen 		http2_read(h2_session);
549e2a0f313Ssthen 		http2_write(h2_session);
550e2a0f313Ssthen 	}
551e2a0f313Ssthen 
552e2a0f313Ssthen 	/* shutdown */
553e2a0f313Ssthen 	http2_session_delete(h2_session);
55472f58708Ssthen 	if(ssl) {
555e2a0f313Ssthen 		SSL_shutdown(ssl);
556e2a0f313Ssthen 		SSL_free(ssl);
55772f58708Ssthen 	}
55872f58708Ssthen 	if(ctx) {
559e2a0f313Ssthen 		SSL_CTX_free(ctx);
56072f58708Ssthen 	}
5611996a427Ssthen 	sock_close(fd);
562e2a0f313Ssthen }
563e2a0f313Ssthen 
564e2a0f313Ssthen /** getopt global, in case header files fail to declare it. */
565e2a0f313Ssthen extern int optind;
566e2a0f313Ssthen /** getopt global, in case header files fail to declare it. */
567e2a0f313Ssthen extern char* optarg;
main(int argc,char ** argv)568e2a0f313Ssthen int main(int argc, char** argv)
569e2a0f313Ssthen {
570e2a0f313Ssthen 	int c;
57172f58708Ssthen 	int port = UNBOUND_DNS_OVER_HTTPS_PORT, no_tls = 0;
57272f58708Ssthen 	struct http2_session* h2_session;
573e2a0f313Ssthen 
57472f58708Ssthen #ifdef USE_WINSOCK
57572f58708Ssthen 	WSADATA wsa_data;
57672f58708Ssthen 	if(WSAStartup(MAKEWORD(2,2), &wsa_data) != 0) {
57772f58708Ssthen 		printf("WSAStartup failed\n");
57872f58708Ssthen 		return 1;
57972f58708Ssthen 	}
58072f58708Ssthen #endif
58172f58708Ssthen 	checklock_start();
58283152a15Ssthen 	log_init(0, 0, 0);
583437d2860Ssthen 	log_ident_set("dohclient");
58472f58708Ssthen 
58572f58708Ssthen 	h2_session = http2_session_create();
58672f58708Ssthen 	if(!h2_session) fatal_exit("out of memory");
587e2a0f313Ssthen 	if(argc == 1) {
588e2a0f313Ssthen 		usage(argv);
589e2a0f313Ssthen 	}
590e2a0f313Ssthen 
591e2a0f313Ssthen 	h2_session->authority = "127.0.0.1";
592e2a0f313Ssthen 	h2_session->post = 0;
593e2a0f313Ssthen 	h2_session->endpoint = "/dns-query";
594e2a0f313Ssthen 	h2_session->content_type = "application/dns-message";
595e2a0f313Ssthen 
59672f58708Ssthen 	while((c=getopt(argc, argv, "c:e:hns:p:P")) != -1) {
597e2a0f313Ssthen 		switch(c) {
598e2a0f313Ssthen 			case 'c':
599e2a0f313Ssthen 				h2_session->content_type = optarg;
600e2a0f313Ssthen 				break;
601e2a0f313Ssthen 			case 'e':
602e2a0f313Ssthen 				h2_session->endpoint = optarg;
603e2a0f313Ssthen 				break;
60472f58708Ssthen 			case 'n':
60572f58708Ssthen 				no_tls = 1;
60672f58708Ssthen 				break;
607e2a0f313Ssthen 			case 'p':
608e2a0f313Ssthen 				if(atoi(optarg)==0 && strcmp(optarg,"0")!=0) {
609e2a0f313Ssthen 					printf("error parsing port, "
610e2a0f313Ssthen 					    "number expected: %s\n", optarg);
611e2a0f313Ssthen 					return 1;
612e2a0f313Ssthen 				}
613e2a0f313Ssthen 				port = atoi(optarg);
614e2a0f313Ssthen 				break;
615e2a0f313Ssthen 			case 'P':
616e2a0f313Ssthen 				h2_session->post = 1;
617e2a0f313Ssthen 				break;
618e2a0f313Ssthen 			case 's':
619e2a0f313Ssthen 				h2_session->authority = optarg;
620e2a0f313Ssthen 				break;
621e2a0f313Ssthen 			case 'h':
622e2a0f313Ssthen 			case '?':
623e2a0f313Ssthen 			default:
624e2a0f313Ssthen 				usage(argv);
625e2a0f313Ssthen 		}
626e2a0f313Ssthen 	}
627e2a0f313Ssthen 	argc -= optind;
628e2a0f313Ssthen 	argv += optind;
629e2a0f313Ssthen 	if(argc%3!=0) {
630e2a0f313Ssthen 		printf("Invalid input. Specify qname, qtype, and qclass.\n");
631e2a0f313Ssthen 		return 1;
632e2a0f313Ssthen 	}
633e2a0f313Ssthen 
634a6cc1574Ssthen 	if(!no_tls) {
635a6cc1574Ssthen #if OPENSSL_VERSION_NUMBER < 0x10100000 || !defined(HAVE_OPENSSL_INIT_SSL)
636a6cc1574Ssthen 		ERR_load_SSL_strings();
637a6cc1574Ssthen #endif
638a6cc1574Ssthen #if OPENSSL_VERSION_NUMBER < 0x10100000 || !defined(HAVE_OPENSSL_INIT_CRYPTO)
639a6cc1574Ssthen #  ifndef S_SPLINT_S
640a6cc1574Ssthen 		OpenSSL_add_all_algorithms();
641a6cc1574Ssthen #  endif
642a6cc1574Ssthen #else
643a6cc1574Ssthen 		OPENSSL_init_crypto(OPENSSL_INIT_ADD_ALL_CIPHERS
644a6cc1574Ssthen 			| OPENSSL_INIT_ADD_ALL_DIGESTS
645a6cc1574Ssthen 			| OPENSSL_INIT_LOAD_CRYPTO_STRINGS, NULL);
646a6cc1574Ssthen #endif
647a6cc1574Ssthen #if OPENSSL_VERSION_NUMBER < 0x10100000 || !defined(HAVE_OPENSSL_INIT_SSL)
648a6cc1574Ssthen 		(void)SSL_library_init();
649a6cc1574Ssthen #else
650a6cc1574Ssthen 		(void)OPENSSL_init_ssl(OPENSSL_INIT_LOAD_SSL_STRINGS, NULL);
651a6cc1574Ssthen #endif
652a6cc1574Ssthen 	}
65372f58708Ssthen 	run(h2_session, port, no_tls, argc, argv);
654e2a0f313Ssthen 
65572f58708Ssthen 	checklock_stop();
65672f58708Ssthen #ifdef USE_WINSOCK
65772f58708Ssthen 	WSACleanup();
65872f58708Ssthen #endif
659e2a0f313Ssthen 	return 0;
660e2a0f313Ssthen }
661e2a0f313Ssthen #else
main(int ATTR_UNUSED (argc),char ** ATTR_UNUSED (argv))662e2a0f313Ssthen int main(int ATTR_UNUSED(argc), char** ATTR_UNUSED(argv))
663e2a0f313Ssthen {
664e2a0f313Ssthen 	printf("Compiled without nghttp2, cannot run test.\n");
665e2a0f313Ssthen 	return 1;
666e2a0f313Ssthen }
667e2a0f313Ssthen #endif /*  HAVE_NGHTTP2 */
668