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, ®ion, 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, ®ion, 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, ®ion, 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