1 /*
2  * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
3  *
4  * SPDX-License-Identifier: MPL-2.0
5  *
6  * This Source Code Form is subject to the terms of the Mozilla Public
7  * License, v. 2.0.  If a copy of the MPL was not distributed with this
8  * file, you can obtain one at https://mozilla.org/MPL/2.0/.
9  *
10  * See the COPYRIGHT file distributed with this work for additional
11  * information regarding copyright ownership.
12  */
13 
14 #include <stdlib.h>
15 #include <string.h>
16 #include <unistd.h>
17 
18 #include <isc/managers.h>
19 #include <isc/mem.h>
20 #include <isc/print.h>
21 #include <isc/socket.h>
22 #include <isc/string.h>
23 #include <isc/task.h>
24 #include <isc/timer.h>
25 #include <isc/util.h>
26 
27 isc_mem_t *mctx = NULL;
28 isc_nm_t *netmgr = NULL;
29 isc_taskmgr_t *taskmgr = NULL;
30 
31 static void
my_shutdown(isc_task_t * task,isc_event_t * event)32 my_shutdown(isc_task_t *task, isc_event_t *event) {
33 	char *name = event->ev_arg;
34 
35 	printf("shutdown %s (%p)\n", name, task);
36 	fflush(stdout);
37 	isc_event_free(&event);
38 }
39 
40 static void
my_send(isc_task_t * task,isc_event_t * event)41 my_send(isc_task_t *task, isc_event_t *event) {
42 	isc_socket_t *sock;
43 	isc_socketevent_t *dev;
44 
45 	sock = event->ev_sender;
46 	dev = (isc_socketevent_t *)event;
47 
48 	printf("my_send: %s task %p\n\t(sock %p, base %p, length %u, n %u, "
49 	       "result %u)\n",
50 	       (char *)(event->ev_arg), task, sock, dev->region.base,
51 	       dev->region.length, dev->n, dev->result);
52 
53 	if (dev->result != ISC_R_SUCCESS) {
54 		isc_socket_detach(&sock);
55 		isc_task_shutdown(task);
56 	}
57 
58 	if (dev->region.base != NULL) {
59 		isc_mem_put(mctx, dev->region.base, dev->region.length);
60 	}
61 
62 	isc_event_free(&event);
63 }
64 
65 static void
my_recv(isc_task_t * task,isc_event_t * event)66 my_recv(isc_task_t *task, isc_event_t *event) {
67 	isc_socket_t *sock;
68 	isc_socketevent_t *dev;
69 	isc_region_t region;
70 	char buf[1024];
71 	char host[256];
72 
73 	sock = event->ev_sender;
74 	dev = (isc_socketevent_t *)event;
75 
76 	printf("Socket %s (sock %p, base %p, length %u, n %u, result %u)\n",
77 	       (char *)(event->ev_arg), sock, dev->region.base,
78 	       dev->region.length, dev->n, dev->result);
79 	if (dev->address.type.sa.sa_family == AF_INET6) {
80 		inet_ntop(AF_INET6, &dev->address.type.sin6.sin6_addr, host,
81 			  sizeof(host));
82 		printf("\tFrom: %s port %d\n", host,
83 		       ntohs(dev->address.type.sin6.sin6_port));
84 	} else {
85 		inet_ntop(AF_INET, &dev->address.type.sin.sin_addr, host,
86 			  sizeof(host));
87 		printf("\tFrom: %s port %d\n", host,
88 		       ntohs(dev->address.type.sin.sin_port));
89 	}
90 
91 	if (dev->result != ISC_R_SUCCESS) {
92 		isc_socket_detach(&sock);
93 
94 		if (dev->region.base != NULL) {
95 			isc_mem_put(mctx, dev->region.base, dev->region.length);
96 		}
97 		isc_event_free(&event);
98 
99 		isc_task_shutdown(task);
100 		return;
101 	}
102 
103 	/*
104 	 * Echo the data back.
105 	 */
106 	if (strcmp(event->ev_arg, "so2") != 0) {
107 		region = dev->region;
108 		snprintf(buf, sizeof(buf), "\r\nReceived: %.*s\r\n\r\n",
109 			 (int)dev->n, (char *)region.base);
110 		region.base = isc_mem_get(mctx, strlen(buf) + 1);
111 		{
112 			region.length = strlen(buf) + 1;
113 			strlcpy((char *)region.base, buf, region.length);
114 		}
115 		isc_socket_send(sock, &region, task, my_send, event->ev_arg);
116 	} else {
117 		region = dev->region;
118 		printf("\r\nReceived: %.*s\r\n\r\n", (int)dev->n,
119 		       (char *)region.base);
120 	}
121 
122 	isc_socket_recv(sock, &dev->region, 1, task, my_recv, event->ev_arg);
123 
124 	isc_event_free(&event);
125 }
126 
127 static void
my_http_get(isc_task_t * task,isc_event_t * event)128 my_http_get(isc_task_t *task, isc_event_t *event) {
129 	isc_socket_t *sock;
130 	isc_socketevent_t *dev;
131 
132 	sock = event->ev_sender;
133 	dev = (isc_socketevent_t *)event;
134 
135 	printf("my_http_get: %s task %p\n\t(sock %p, base %p, length %u, "
136 	       "n %u, result %u)\n",
137 	       (char *)(event->ev_arg), task, sock, dev->region.base,
138 	       dev->region.length, dev->n, dev->result);
139 
140 	if (dev->result != ISC_R_SUCCESS) {
141 		isc_socket_detach(&sock);
142 		isc_task_shutdown(task);
143 		if (dev->region.base != NULL) {
144 			isc_mem_put(mctx, dev->region.base, dev->region.length);
145 		}
146 		isc_event_free(&event);
147 		return;
148 	}
149 
150 	isc_socket_recv(sock, &dev->region, 1, task, my_recv, event->ev_arg);
151 
152 	isc_event_free(&event);
153 }
154 
155 static void
my_connect(isc_task_t * task,isc_event_t * event)156 my_connect(isc_task_t *task, isc_event_t *event) {
157 	isc_socket_t *sock;
158 	isc_socket_connev_t *dev;
159 	isc_region_t region;
160 	char buf[1024];
161 
162 	sock = event->ev_sender;
163 	dev = (isc_socket_connev_t *)event;
164 
165 	printf("%s: Connection result:  %u\n", (char *)(event->ev_arg),
166 	       dev->result);
167 
168 	if (dev->result != ISC_R_SUCCESS) {
169 		isc_socket_detach(&sock);
170 		isc_event_free(&event);
171 		isc_task_shutdown(task);
172 		return;
173 	}
174 
175 	/*
176 	 * Send a GET string, and set up to receive (and just display)
177 	 * the result.
178 	 */
179 	snprintf(buf, sizeof(buf),
180 		 "GET / HTTP/1.1\r\nHost: www.flame.org\r\n"
181 		 "Connection: Close\r\n\r\n");
182 	region.base = isc_mem_get(mctx, strlen(buf) + 1);
183 	{
184 		region.length = strlen(buf) + 1;
185 		strlcpy((char *)region.base, buf, region.length);
186 	}
187 
188 	isc_socket_send(sock, &region, task, my_http_get, event->ev_arg);
189 
190 	isc_event_free(&event);
191 }
192 
193 static void
my_listen(isc_task_t * task,isc_event_t * event)194 my_listen(isc_task_t *task, isc_event_t *event) {
195 	char *name = event->ev_arg;
196 	isc_socket_newconnev_t *dev = NULL;
197 	isc_region_t region;
198 	isc_socket_t *oldsock = NULL;
199 	isc_task_t *newtask = NULL;
200 
201 	dev = (isc_socket_newconnev_t *)event;
202 
203 	printf("newcon %s (task %p, oldsock %p, newsock %p, result %u)\n", name,
204 	       task, event->ev_sender, dev->newsocket, dev->result);
205 	fflush(stdout);
206 
207 	if (dev->result == ISC_R_SUCCESS) {
208 		/*
209 		 * Queue another listen on this socket.
210 		 */
211 		RUNTIME_CHECK(isc_socket_accept(event->ev_sender, task,
212 						my_listen, event->ev_arg) ==
213 			      ISC_R_SUCCESS);
214 
215 		region.base = isc_mem_get(mctx, 20);
216 		region.length = 20;
217 
218 		/*
219 		 * Create a new task for this socket, and queue up a
220 		 * recv on it.
221 		 */
222 		RUNTIME_CHECK(isc_task_create(taskmgr, 0, &newtask) ==
223 			      ISC_R_SUCCESS);
224 		isc_socket_recv(dev->newsocket, &region, 1, newtask, my_recv,
225 				event->ev_arg);
226 		isc_task_detach(&newtask);
227 	} else {
228 		printf("detaching from socket %p\n", event->ev_sender);
229 		oldsock = event->ev_sender;
230 
231 		isc_socket_detach(&oldsock);
232 
233 		isc_event_free(&event);
234 		isc_task_shutdown(task);
235 		return;
236 	}
237 
238 	isc_event_free(&event);
239 }
240 
241 static void
timeout(isc_task_t * task,isc_event_t * event)242 timeout(isc_task_t *task, isc_event_t *event) {
243 	isc_socket_t *sock = event->ev_arg;
244 
245 	printf("Timeout, canceling IO on socket %p (task %p)\n", sock, task);
246 
247 	isc_socket_cancel(sock, NULL, ISC_SOCKCANCEL_ALL);
248 	isc_timer_detach((isc_timer_t **)&event->ev_sender);
249 	isc_event_free(&event);
250 }
251 
252 static char one[] = "1";
253 static char two[] = "2";
254 static char xso1[] = "so1";
255 static char xso2[] = "so2";
256 
257 int
main(int argc,char * argv[])258 main(int argc, char *argv[]) {
259 	isc_task_t *t1 = NULL, *t2 = NULL;
260 	isc_timermgr_t *timgr = NULL;
261 	isc_time_t expires;
262 	isc_interval_t interval;
263 	isc_timer_t *ti1 = NULL;
264 	unsigned int workers;
265 	isc_socketmgr_t *socketmgr = NULL;
266 	isc_socket_t *so1 = NULL, *so2 = NULL;
267 	isc_sockaddr_t sockaddr;
268 	struct in_addr ina;
269 	struct in6_addr in6a;
270 	isc_result_t result;
271 	int pf;
272 
273 	if (argc > 1) {
274 		workers = atoi(argv[1]);
275 		if (workers < 1) {
276 			workers = 1;
277 		}
278 		if (workers > 8192) {
279 			workers = 8192;
280 		}
281 	} else {
282 		workers = 2;
283 	}
284 	printf("%u workers\n", workers);
285 
286 	if (isc_net_probeipv6() == ISC_R_SUCCESS) {
287 		pf = PF_INET6;
288 	} else {
289 		pf = PF_INET;
290 	}
291 
292 	/*
293 	 * EVERYTHING needs a memory context.
294 	 */
295 	isc_mem_create(&mctx);
296 
297 	/*
298 	 * The task manager is independent (other than memory context)
299 	 */
300 	RUNTIME_CHECK(isc_managers_create(mctx, workers, 0, &netmgr,
301 					  &taskmgr) == ISC_R_SUCCESS);
302 
303 	/*
304 	 * Timer manager depends only on the memory context as well.
305 	 */
306 	RUNTIME_CHECK(isc_timermgr_create(mctx, &timgr) == ISC_R_SUCCESS);
307 
308 	RUNTIME_CHECK(isc_task_create(taskmgr, 0, &t1) == ISC_R_SUCCESS);
309 	RUNTIME_CHECK(isc_task_create(taskmgr, 0, &t2) == ISC_R_SUCCESS);
310 	RUNTIME_CHECK(isc_task_onshutdown(t1, my_shutdown, one) ==
311 		      ISC_R_SUCCESS);
312 	RUNTIME_CHECK(isc_task_onshutdown(t2, my_shutdown, two) ==
313 		      ISC_R_SUCCESS);
314 
315 	printf("task 1 = %p\n", t1);
316 	printf("task 2 = %p\n", t2);
317 
318 	RUNTIME_CHECK(isc_socketmgr_create(mctx, &socketmgr) == ISC_R_SUCCESS);
319 
320 	/*
321 	 * Open up a listener socket.
322 	 */
323 
324 	if (pf == PF_INET6) {
325 		in6a = in6addr_any;
326 		isc_sockaddr_fromin6(&sockaddr, &in6a, 5544);
327 	} else {
328 		ina.s_addr = INADDR_ANY;
329 		isc_sockaddr_fromin(&sockaddr, &ina, 5544);
330 	}
331 	RUNTIME_CHECK(isc_socket_create(socketmgr, pf, isc_sockettype_tcp,
332 					&so1) == ISC_R_SUCCESS);
333 	result = isc_socket_bind(so1, &sockaddr, ISC_SOCKET_REUSEADDRESS);
334 	RUNTIME_CHECK(result == ISC_R_SUCCESS);
335 	RUNTIME_CHECK(isc_socket_listen(so1, 0) == ISC_R_SUCCESS);
336 
337 	/*
338 	 * Queue up the first accept event.
339 	 */
340 	RUNTIME_CHECK(isc_socket_accept(so1, t1, my_listen, xso1) ==
341 		      ISC_R_SUCCESS);
342 	isc_time_settoepoch(&expires);
343 	isc_interval_set(&interval, 10, 0);
344 	RUNTIME_CHECK(isc_timer_create(timgr, isc_timertype_once, &expires,
345 				       &interval, t1, timeout, so1,
346 				       &ti1) == ISC_R_SUCCESS);
347 
348 	/*
349 	 * Open up a socket that will connect to www.flame.org, port 80.
350 	 * Why not.  :)
351 	 */
352 	ina.s_addr = inet_addr("204.152.184.97");
353 	if (0 && pf == PF_INET6) {
354 		isc_sockaddr_v6fromin(&sockaddr, &ina, 80);
355 	} else {
356 		isc_sockaddr_fromin(&sockaddr, &ina, 80);
357 	}
358 	RUNTIME_CHECK(isc_socket_create(socketmgr, isc_sockaddr_pf(&sockaddr),
359 					isc_sockettype_tcp,
360 					&so2) == ISC_R_SUCCESS);
361 
362 	RUNTIME_CHECK(isc_socket_connect(so2, &sockaddr, t2, my_connect,
363 					 xso2) == ISC_R_SUCCESS);
364 
365 	/*
366 	 * Detaching these is safe, since the socket will attach to the
367 	 * task for any outstanding requests.
368 	 */
369 	isc_task_detach(&t1);
370 	isc_task_detach(&t2);
371 
372 	/*
373 	 * Wait a short while.
374 	 */
375 #ifndef WIN32
376 	sleep(10);
377 #else  /* ifndef WIN32 */
378 	Sleep(10000);
379 #endif /* ifndef WIN32 */
380 
381 	fprintf(stderr, "Destroying socket manager\n");
382 	isc_socketmgr_destroy(&socketmgr);
383 
384 	fprintf(stderr, "Destroying timer manager\n");
385 	isc_timermgr_destroy(&timgr);
386 
387 	fprintf(stderr, "Destroying task manager\n");
388 	isc_managers_destroy(&netmgr, &taskmgr);
389 
390 	isc_mem_stats(mctx, stdout);
391 	isc_mem_destroy(&mctx);
392 
393 	return (0);
394 }
395