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