1 /*  Copyright (C) 2021 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
2 
3     This program is free software: you can redistribute it and/or modify
4     it under the terms of the GNU General Public License as published by
5     the Free Software Foundation, either version 3 of the License, or
6     (at your option) any later version.
7 
8     This program is distributed in the hope that it will be useful,
9     but WITHOUT ANY WARRANTY; without even the implied warranty of
10     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11     GNU General Public License for more details.
12 
13     You should have received a copy of the GNU General Public License
14     along with this program.  If not, see <https://www.gnu.org/licenses/>.
15  */
16 
17 #include <assert.h>
18 #include <tap/basic.h>
19 #include <pthread.h>
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <string.h>
23 #include <fcntl.h>
24 
25 #include "libknot/descriptor.h"
26 #include "libknot/errcode.h"
27 #include "knot/query/layer.h"
28 #include "knot/query/requestor.h"
29 #include "contrib/mempattern.h"
30 #include "contrib/net.h"
31 #include "contrib/sockaddr.h"
32 #include "contrib/ucw/mempool.h"
33 
34 bool TFO = false;
35 
36 /* @note Purpose of this test is not to verify process_answer functionality,
37  *       but simply if the requesting/receiving works, so mirror is okay. */
reset(knot_layer_t * ctx)38 static int reset(knot_layer_t *ctx) { return KNOT_STATE_PRODUCE; }
begin(knot_layer_t * ctx,void * module_param)39 static int begin(knot_layer_t *ctx, void *module_param) { return reset(ctx); }
finish(knot_layer_t * ctx)40 static int finish(knot_layer_t *ctx) { return reset(ctx); }
in(knot_layer_t * ctx,knot_pkt_t * pkt)41 static int in(knot_layer_t *ctx, knot_pkt_t *pkt) { return KNOT_STATE_DONE; }
out(knot_layer_t * ctx,knot_pkt_t * pkt)42 static int out(knot_layer_t *ctx, knot_pkt_t *pkt) { return KNOT_STATE_CONSUME; }
43 
44 static const int TIMEOUT = 2000;
45 
46 /*! \brief Dummy answer processing module. */
47 const knot_layer_api_t dummy_module = {
48         &begin, &reset, &finish, &in, &out
49 };
50 
set_blocking_mode(int sock)51 static void set_blocking_mode(int sock)
52 {
53 	int flags = fcntl(sock, F_GETFL);
54 	flags &= ~O_NONBLOCK;
55 	fcntl(sock, F_SETFL, flags);
56 }
57 
responder_thread(void * arg)58 static void *responder_thread(void *arg)
59 {
60 	int fd = *(int *)arg;
61 
62 	set_blocking_mode(fd);
63 	uint8_t buf[KNOT_WIRE_MAX_PKTSIZE] = { 0 };
64 	while (true) {
65 		int client = accept(fd, NULL, NULL);
66 		if (client < 0) {
67 			break;
68 		}
69 		int len = net_dns_tcp_recv(client, buf, sizeof(buf), -1);
70 		if (len < KNOT_WIRE_HEADER_SIZE) {
71 			close(client);
72 			break;
73 		}
74 		knot_wire_set_qr(buf);
75 		net_dns_tcp_send(client, buf, len, -1, NULL);
76 		close(client);
77 	}
78 
79 	return NULL;
80 }
81 
82 /* Test implementations. */
83 
make_query(knot_requestor_t * requestor,const struct sockaddr_storage * dst,const struct sockaddr_storage * src)84 static knot_request_t *make_query(knot_requestor_t *requestor,
85                                   const struct sockaddr_storage *dst,
86                                   const struct sockaddr_storage *src)
87 {
88 	knot_pkt_t *pkt = knot_pkt_new(NULL, KNOT_WIRE_MAX_PKTSIZE, requestor->mm);
89 	assert(pkt);
90 	static const knot_dname_t *root = (uint8_t *)"";
91 	knot_pkt_put_question(pkt, root, KNOT_CLASS_IN, KNOT_RRTYPE_SOA);
92 
93 	knot_request_flag_t flags = TFO ? KNOT_REQUEST_TFO: KNOT_REQUEST_NONE;
94 
95 	return knot_request_make(requestor->mm, dst, src, pkt, NULL, flags);
96 }
97 
test_disconnected(knot_requestor_t * requestor,const struct sockaddr_storage * dst,const struct sockaddr_storage * src)98 static void test_disconnected(knot_requestor_t *requestor,
99                               const struct sockaddr_storage *dst,
100                               const struct sockaddr_storage *src)
101 {
102 	knot_request_t *req = make_query(requestor, dst, src);
103 	int ret = knot_requestor_exec(requestor, req, TIMEOUT);
104 	/* ECONNREFUSED on FreeBSD, ETIMEOUT on NetBSD/OpenBSD/macOS. */
105 	ret = (ret == KNOT_ECONNREFUSED || ret == KNOT_ETIMEOUT) ? KNOT_ECONN : ret;
106 	is_int(KNOT_ECONN, ret, "requestor: disconnected/exec");
107 	knot_request_free(req, requestor->mm);
108 
109 }
110 
test_connected(knot_requestor_t * requestor,const struct sockaddr_storage * dst,const struct sockaddr_storage * src)111 static void test_connected(knot_requestor_t *requestor,
112                            const struct sockaddr_storage *dst,
113                            const struct sockaddr_storage *src)
114 {
115 	/* Enqueue packet. */
116 	knot_request_t *req = make_query(requestor, dst, src);
117 	int ret = knot_requestor_exec(requestor, req, TIMEOUT);
118 	is_int(KNOT_EOK, ret, "requestor: connected/exec");
119 	knot_request_free(req, requestor->mm);
120 }
121 
main(int argc,char * argv[])122 int main(int argc, char *argv[])
123 {
124 #if defined(__linux__)
125 	FILE *fd = fopen("/proc/sys/net/ipv4/tcp_fastopen", "r");
126 	if (fd != NULL) {
127 		int val = fgetc(fd);
128 		fclose(fd);
129 		// 0 - disabled, 1 - server TFO (client fallbacks),
130 		// 2 - client TFO, 3 - both
131 		if (val == '1' || val == '3') {
132 			TFO = true;
133 		}
134 	}
135 #endif
136 	plan_lazy();
137 
138 	knot_mm_t mm;
139 	mm_ctx_mempool(&mm, MM_DEFAULT_BLKSIZE);
140 
141 	/* Initialize requestor. */
142 	knot_requestor_t requestor;
143 	knot_requestor_init(&requestor, &dummy_module, NULL, &mm);
144 
145 	/* Define endpoints. */
146 	struct sockaddr_storage client = { 0 };
147 	sockaddr_set(&client, AF_INET, "127.0.0.1", 0);
148 	struct sockaddr_storage server = { 0 };
149 	sockaddr_set(&server, AF_INET, "127.0.0.1", 0);
150 
151 	/* Bind to random port. */
152 	int responder_fd = net_bound_socket(SOCK_STREAM, &server, 0);
153 	assert(responder_fd >= 0);
154 	socklen_t addr_len = sockaddr_len(&server);
155 	int ret = getsockname(responder_fd, (struct sockaddr *)&server, &addr_len);
156 	ok(ret == 0, "check getsockname return");
157 
158 	/* Test requestor in disconnected environment. */
159 	test_disconnected(&requestor, &server, &client);
160 
161 	/* Start responder. */
162 	ret = listen(responder_fd, 10);
163 	ok(ret == 0, "check listen return");
164 
165 	if (TFO) {
166 		ret = net_bound_tfo(responder_fd, 10);
167 		ok(ret == KNOT_EOK, "check bound TFO return");
168 	}
169 
170 	pthread_t thread;
171 	pthread_create(&thread, 0, responder_thread, &responder_fd);
172 
173 	/* Test requestor in connected environment. */
174 	test_connected(&requestor, &server, &client);
175 
176 	/* Terminate responder. */
177 	int conn = net_connected_socket(SOCK_STREAM, &server, NULL, false);
178 	assert(conn > 0);
179 	conn = net_dns_tcp_send(conn, (uint8_t *)"", 1, TIMEOUT, NULL);
180 	assert(conn > 0);
181 	pthread_join(thread, NULL);
182 	close(responder_fd);
183 
184 	/* Cleanup. */
185 	mp_delete((struct mempool *)mm.ctx);
186 
187 	return 0;
188 }
189