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