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