1/***********************************************************************
2 * Copyright (c) 2009, Secure Endpoints Inc.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 *   notice, this list of conditions and the following disclaimer.
11 *
12 * - Redistributions in binary form must reproduce the above copyright
13 *   notice, this list of conditions and the following disclaimer in
14 *   the documentation and/or other materials provided with the
15 *   distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
20 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
21 * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
22 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
26 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
28 * OF THE POSSIBILITY OF SUCH DAMAGE.
29 *
30 **********************************************************************/
31
32#include <config.h>
33#include <roken.h>
34#include <stdio.h>
35#include <string.h>
36#include <stdlib.h>
37
38#define PORT 8013
39#define PORT_S "8013"
40
41char * prog = "Master";
42int is_client = 0;
43
44static int
45get_address(int flags, struct addrinfo ** ret)
46{
47    struct addrinfo ai;
48    int rv;
49
50    memset(&ai, 0, sizeof(ai));
51
52    ai.ai_flags = flags | AI_NUMERICHOST;
53    ai.ai_family = AF_INET;
54    ai.ai_socktype = SOCK_STREAM;
55    ai.ai_protocol = PF_UNSPEC;
56
57    rv = getaddrinfo("127.0.0.1", PORT_S, &ai, ret);
58    if (rv)
59	warnx("getaddrinfo: %s", gai_strerror(rv));
60    return rv;
61}
62
63static int
64get_connected_socket(rk_socket_t * s_ret)
65{
66    struct addrinfo * ai = NULL;
67    int rv = 0;
68    rk_socket_t s = rk_INVALID_SOCKET;
69
70    rv = get_address(0, &ai);
71    if (rv)
72	return rv;
73
74    s = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
75    if (rk_IS_BAD_SOCKET(s)) {
76	rv = 1;
77	goto done;
78    }
79
80    rv = connect(s, ai->ai_addr, ai->ai_addrlen);
81    if (rk_IS_SOCKET_ERROR(rv))
82	goto done;
83
84    *s_ret = s;
85    s = rk_INVALID_SOCKET;
86    rv = 0;
87
88 done:
89    if (!rk_IS_BAD_SOCKET(s))
90	rk_closesocket(s);
91
92    if (ai)
93	freeaddrinfo(ai);
94
95    return (rv) ? rk_SOCK_ERRNO : 0;
96}
97
98const char * test_strings[] = {
99    "Hello",
100    "01234566789012345689012345678901234567890123456789",
101    "Another test",
102    "exit"
103};
104
105static int
106test_simple_echo_client(void)
107{
108    rk_socket_t s = rk_INVALID_SOCKET;
109    int rv;
110    char buf[81];
111    int i;
112
113    fprintf(stderr, "[%s] Getting connected socket...", getprogname());
114    rv = get_connected_socket(&s);
115    if (rv) {
116	fprintf(stderr, "\n[%s] get_connected_socket() failed (%s)\n",
117		getprogname(), strerror(rk_SOCK_ERRNO));
118	return 1;
119    }
120
121    fprintf(stderr, "[%s] done\n", getprogname());
122
123    for (i=0; i < sizeof(test_strings)/sizeof(test_strings[0]); i++) {
124	rv = send(s, test_strings[i], strlen(test_strings[i]), 0);
125	if (rk_IS_SOCKET_ERROR(rv)) {
126	    fprintf(stderr, "[%s] send() failure (%s)\n",
127		    getprogname(), strerror(rk_SOCK_ERRNO));
128	    rk_closesocket(s);
129	    return 1;
130	}
131
132	rv = recv(s, buf, sizeof(buf), 0);
133	if (rk_IS_SOCKET_ERROR(rv)) {
134	    fprintf (stderr, "[%s] recv() failure (%s)\n",
135		     getprogname(), strerror(rk_SOCK_ERRNO));
136	    rk_closesocket(s);
137	    return 1;
138	}
139
140	if (rv == 0) {
141	    fprintf (stderr, "[%s] No data received\n", prog);
142	    rk_closesocket(s);
143	    return 1;
144	}
145
146	if (rv != strlen(test_strings[i])) {
147	    fprintf (stderr, "[%s] Data length mismatch %d != %d\n", prog, rv, strlen(test_strings[i]));
148	    rk_closesocket(s);
149	    return 1;
150	}
151    }
152
153    fprintf (stderr, "[%s] Done\n", prog);
154    rk_closesocket(s);
155    return 0;
156}
157
158static int
159test_simple_echo_socket(void)
160{
161    fprintf (stderr, "[%s] Process ID %d\n", prog, GetCurrentProcessId());
162    fprintf (stderr, "[%s] Starting echo test with sockets\n", prog);
163
164    if (is_client) {
165	return test_simple_echo_client();
166    } else {
167
168	rk_socket_t s = rk_INVALID_SOCKET;
169
170	fprintf (stderr, "[%s] Listening for connections...\n", prog);
171	mini_inetd(htons(PORT), &s);
172	if (rk_IS_BAD_SOCKET(s)) {
173	    fprintf (stderr, "[%s] Connect failed (%s)\n",
174		     getprogname(), strerror(rk_SOCK_ERRNO));
175	} else {
176	    fprintf (stderr, "[%s] Connected\n", prog);
177	}
178
179	{
180	    char buf[81];
181	    int rv, srv;
182
183	    while ((rv = recv(s, buf, sizeof(buf), 0)) != 0 && !rk_IS_SOCKET_ERROR(rv)) {
184		buf[rv] = 0;
185		fprintf(stderr, "[%s] Received [%s]\n", prog, buf);
186
187		/* simple echo */
188		srv = send(s, buf, rv, 0);
189		if (srv != rv) {
190		    if (rk_IS_SOCKET_ERROR(srv))
191			fprintf(stderr, "[%s] send() error [%s]\n",
192				getprogname(), strerror(rk_SOCK_ERRNO));
193		    else
194			fprintf(stderr, "[%s] send() size mismatch %d != %d",
195				getprogname(), srv, rv);
196		}
197
198		if (!strcmp(buf, "exit")) {
199		    fprintf(stderr, "[%s] Exiting...\n", prog);
200		    shutdown(s, SD_SEND);
201		    rk_closesocket(s);
202		    return 0;
203		}
204	    }
205
206	    fprintf(stderr, "[%s] recv() failed (%s)\n",
207		    getprogname(),
208		    strerror(rk_SOCK_ERRNO));
209	}
210
211	rk_closesocket(s);
212    }
213
214    return 1;
215}
216
217static int
218test_simple_echo(void)
219{
220    fprintf (stderr, "[%s] Starting echo test\n", prog);
221
222    if (is_client) {
223
224	return test_simple_echo_client();
225
226    } else {
227
228	fprintf (stderr, "[%s] Listening for connections...\n", prog);
229	mini_inetd(htons(PORT), NULL);
230	fprintf (stderr, "[%s] Connected\n", prog);
231
232	{
233	    char buf[81];
234	    while (gets(buf)) {
235		fprintf(stderr, "[%s] Received [%s]\n", prog, buf);
236
237		if (!strcmp(buf, "exit"))
238		    return 0;
239
240		/* simple echo */
241		puts(buf);
242	    }
243
244	    fprintf(stderr, "[%s] gets() failed (%s)\n", prog, _strerror("gets"));
245	}
246    }
247
248    return 1;
249}
250
251static int
252do_client(void)
253{
254    int rv = 0;
255
256    rk_SOCK_INIT();
257
258    prog = "Client";
259    is_client = 1;
260
261    fprintf(stderr, "Starting client...\n");
262
263    rv = test_simple_echo_socket();
264
265    rk_SOCK_EXIT();
266
267    return rv;
268}
269
270static int
271do_server(void)
272{
273    int rv = 0;
274
275    rk_SOCK_INIT();
276
277    prog = "Server";
278
279    fprintf(stderr, "Starting server...\n");
280
281    rv = test_simple_echo_socket();
282
283    rk_SOCK_EXIT();
284
285    return rv;
286}
287
288static time_t
289wait_callback(void *p)
290{
291    return (time_t)-1;
292}
293
294static int
295do_test(char * path)
296{
297    intptr_t p_server;
298    intptr_t p_client;
299    int client_rv;
300    int server_rv;
301
302    p_server = _spawnl(_P_NOWAIT, path, path, "--server", NULL);
303    if (p_server <= 0) {
304	fprintf(stderr, "%s: %s", path, _strerror("Can't start server process"));
305	return 1;
306    }
307#ifdef _WIN32
308    /* On Windows, the _spawn*() functions return a process handle on
309       success.  We need a process ID for use with
310       wait_for_process_timed(). */
311
312    p_server = GetProcessId((HANDLE) p_server);
313#endif
314    fprintf(stderr, "Created server process ID %d\n", p_server);
315
316    p_client = _spawnl(_P_NOWAIT, path, path, "--client", NULL);
317    if (p_client <= 0) {
318	fprintf(stderr, "%s: %s", path, _strerror("Can't start client process"));
319	fprintf(stderr, "Waiting for server process to terminate ...");
320	wait_for_process_timed(p_server, wait_callback, NULL, 5);
321	fprintf(stderr, "DONE\n");
322	return 1;
323    }
324#ifdef _WIN32
325    p_client = GetProcessId((HANDLE) p_client);
326#endif
327    fprintf(stderr, "Created client process ID %d\n", p_client);
328
329    fprintf(stderr, "Waiting for client process to terminate ...");
330    client_rv = wait_for_process_timed(p_client, wait_callback, NULL, 5);
331    if (SE_IS_ERROR(client_rv)) {
332	fprintf(stderr, "\nwait_for_process_timed() failed for client. rv=%d\n", client_rv);
333    } else {
334	fprintf(stderr, "DONE\n");
335    }
336
337    fprintf(stderr, "Waiting for server process to terminate ...");
338    server_rv = wait_for_process_timed(p_server, wait_callback, NULL, 5);
339    if (SE_IS_ERROR(server_rv)) {
340	fprintf(stderr, "\nwait_for_process_timed() failed for server. rv=%d\n", server_rv);
341    } else {
342	fprintf(stderr, "DONE\n");
343    }
344
345    if (client_rv == 0 && server_rv == 0) {
346	fprintf(stderr, "PASS\n");
347	return 0;
348    } else {
349	fprintf(stderr, "FAIL: Client rv=%d, Server rv=%d\n", client_rv, server_rv);
350	return 1;
351    }
352}
353
354int main(int argc, char ** argv)
355{
356    setprogname(argv[0]);
357
358    if (argc == 2 && strcmp(argv[1], "--client") == 0)
359	return do_client();
360    else if (argc == 2 && strcmp(argv[1], "--server") == 0)
361	return do_server();
362    else if (argc == 1)
363	return do_test(argv[0]);
364    else {
365	printf ("%s: Test mini_inetd() function.  Run with no arguments to start test\n",
366		argv[0]);
367	return 1;
368    }
369}
370