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