1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/nsprpub/pr/tests/nbconn.c Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,520 @@ 1.4 +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +/* 1.10 + * A test for nonblocking connect. Functions tested include PR_Connect, 1.11 + * PR_Poll, and PR_GetConnectStatus. 1.12 + * 1.13 + * The test should be invoked with a host name, for example: 1.14 + * nbconn www.netscape.com 1.15 + * It will do a nonblocking connect to port 80 (HTTP) on that host, 1.16 + * and when connected, issue the "GET /" HTTP command. 1.17 + * 1.18 + * You should run this test in three ways: 1.19 + * 1. To a known web site, such as www.netscape.com. The HTML of the 1.20 + * top-level page at the web site should be printed. 1.21 + * 2. To a machine not running a web server at port 80. This test should 1.22 + * fail. Ideally the error code should be PR_CONNECT_REFUSED_ERROR. 1.23 + * But it is possible to return PR_UNKNOWN_ERROR on certain platforms. 1.24 + * 3. To an unreachable machine, for example, a machine that is off line. 1.25 + * The test should fail after the connect times out. Ideally the 1.26 + * error code should be PR_IO_TIMEOUT_ERROR, but it is possible to 1.27 + * return PR_UNKNOWN_ERROR on certain platforms. 1.28 + */ 1.29 + 1.30 +#include "nspr.h" 1.31 +#include "plgetopt.h" 1.32 +#include <stdio.h> 1.33 +#include <string.h> 1.34 + 1.35 +#define SERVER_MAX_BIND_COUNT 100 1.36 +#define DATA_BUF_SIZE 256 1.37 +#define TCP_SERVER_PORT 10000 1.38 +#define TCP_UNUSED_PORT 211 1.39 + 1.40 +typedef struct Server_Param { 1.41 + PRFileDesc *sp_fd; /* server port */ 1.42 +} Server_Param; 1.43 +static void PR_CALLBACK TCP_Server(void *arg); 1.44 + 1.45 +int _debug_on; 1.46 +#define DPRINTF(arg) if (_debug_on) printf arg 1.47 + 1.48 +static PRIntn connection_success_test(); 1.49 +static PRIntn connection_failure_test(); 1.50 + 1.51 +int main(int argc, char **argv) 1.52 +{ 1.53 + PRHostEnt he; 1.54 + char buf[1024]; 1.55 + PRNetAddr addr; 1.56 + PRPollDesc pd; 1.57 + PRStatus rv; 1.58 + PRSocketOptionData optData; 1.59 + const char *hostname = NULL; 1.60 + PRIntn default_case, n, bytes_read, bytes_sent; 1.61 + PRInt32 failed_already = 0; 1.62 + 1.63 + /* 1.64 + * -d debug mode 1.65 + */ 1.66 + 1.67 + PLOptStatus os; 1.68 + PLOptState *opt = PL_CreateOptState(argc, argv, "d"); 1.69 + while (PL_OPT_EOL != (os = PL_GetNextOpt(opt))) 1.70 + { 1.71 + if (PL_OPT_BAD == os) continue; 1.72 + switch (opt->option) 1.73 + { 1.74 + case 0: /* debug mode */ 1.75 + hostname = opt->value; 1.76 + break; 1.77 + case 'd': /* debug mode */ 1.78 + _debug_on = 1; 1.79 + break; 1.80 + default: 1.81 + break; 1.82 + } 1.83 + } 1.84 + PL_DestroyOptState(opt); 1.85 + 1.86 + PR_STDIO_INIT(); 1.87 + if (hostname) 1.88 + default_case = 0; 1.89 + else 1.90 + default_case = 1; 1.91 + 1.92 + if (default_case) { 1.93 + 1.94 + /* 1.95 + * In the default case the following tests are executed: 1.96 + * 1. successful connection: a server thread accepts a connection 1.97 + * from the main thread 1.98 + * 2. unsuccessful connection: the main thread tries to connect to a 1.99 + * nonexistent port and expects to get an error 1.100 + */ 1.101 + rv = connection_success_test(); 1.102 + if (rv == 0) 1.103 + rv = connection_failure_test(); 1.104 + return rv; 1.105 + } else { 1.106 + PRFileDesc *sock; 1.107 + 1.108 + if (PR_GetHostByName(argv[1], buf, sizeof(buf), &he) == PR_FAILURE) { 1.109 + printf( "Unknown host: %s\n", argv[1]); 1.110 + exit(1); 1.111 + } else { 1.112 + printf( "host: %s\n", buf); 1.113 + } 1.114 + PR_EnumerateHostEnt(0, &he, 80, &addr); 1.115 + 1.116 + sock = PR_NewTCPSocket(); 1.117 + optData.option = PR_SockOpt_Nonblocking; 1.118 + optData.value.non_blocking = PR_TRUE; 1.119 + PR_SetSocketOption(sock, &optData); 1.120 + rv = PR_Connect(sock, &addr, PR_INTERVAL_NO_TIMEOUT); 1.121 + if (rv == PR_FAILURE && PR_GetError() == PR_IN_PROGRESS_ERROR) { 1.122 + printf( "Connect in progress\n"); 1.123 + } 1.124 + 1.125 + pd.fd = sock; 1.126 + pd.in_flags = PR_POLL_WRITE | PR_POLL_EXCEPT; 1.127 + n = PR_Poll(&pd, 1, PR_INTERVAL_NO_TIMEOUT); 1.128 + if (n == -1) { 1.129 + printf( "PR_Poll failed\n"); 1.130 + exit(1); 1.131 + } 1.132 + printf( "PR_Poll returns %d\n", n); 1.133 + if (pd.out_flags & PR_POLL_READ) { 1.134 + printf( "PR_POLL_READ\n"); 1.135 + } 1.136 + if (pd.out_flags & PR_POLL_WRITE) { 1.137 + printf( "PR_POLL_WRITE\n"); 1.138 + } 1.139 + if (pd.out_flags & PR_POLL_EXCEPT) { 1.140 + printf( "PR_POLL_EXCEPT\n"); 1.141 + } 1.142 + if (pd.out_flags & PR_POLL_ERR) { 1.143 + printf( "PR_POLL_ERR\n"); 1.144 + } 1.145 + if (pd.out_flags & PR_POLL_NVAL) { 1.146 + printf( "PR_POLL_NVAL\n"); 1.147 + } 1.148 + 1.149 + if (PR_GetConnectStatus(&pd) == PR_SUCCESS) { 1.150 + printf("PR_GetConnectStatus: connect succeeded\n"); 1.151 + PR_Write(sock, "GET /\r\n\r\n", 9); 1.152 + PR_Shutdown(sock, PR_SHUTDOWN_SEND); 1.153 + pd.in_flags = PR_POLL_READ; 1.154 + while (1) { 1.155 + n = PR_Poll(&pd, 1, PR_INTERVAL_NO_TIMEOUT); 1.156 + printf( "poll returns %d\n", n); 1.157 + n = PR_Read(sock, buf, sizeof(buf)); 1.158 + printf( "read returns %d\n", n); 1.159 + if (n <= 0) { 1.160 + break; 1.161 + } 1.162 + PR_Write(PR_STDOUT, buf, n); 1.163 + } 1.164 + } else { 1.165 + if (PR_GetError() == PR_IN_PROGRESS_ERROR) { 1.166 + printf( "PR_GetConnectStatus: connect still in progress\n"); 1.167 + exit(1); 1.168 + } 1.169 + printf( "PR_GetConnectStatus: connect failed: (%ld, %ld)\n", 1.170 + PR_GetError(), PR_GetOSError()); 1.171 + } 1.172 + PR_Close(sock); 1.173 + printf( "PASS\n"); 1.174 + return 0; 1.175 + 1.176 + } 1.177 +} 1.178 + 1.179 + 1.180 +/* 1.181 + * TCP Server 1.182 + * Server Thread 1.183 + * Accept a connection from the client and write some data 1.184 + */ 1.185 +static void PR_CALLBACK 1.186 +TCP_Server(void *arg) 1.187 +{ 1.188 + Server_Param *sp = (Server_Param *) arg; 1.189 + PRFileDesc *sockfd, *newsockfd; 1.190 + char data_buf[DATA_BUF_SIZE]; 1.191 + PRIntn rv, bytes_read; 1.192 + 1.193 + sockfd = sp->sp_fd; 1.194 + if ((newsockfd = PR_Accept(sockfd, NULL, 1.195 + PR_INTERVAL_NO_TIMEOUT)) == NULL) { 1.196 + fprintf(stderr,"ERROR - PR_Accept failed: (%d,%d)\n", 1.197 + PR_GetError(), PR_GetOSError()); 1.198 + return; 1.199 + } 1.200 + bytes_read = 0; 1.201 + while (bytes_read != DATA_BUF_SIZE) { 1.202 + rv = PR_Read(newsockfd, data_buf + bytes_read , 1.203 + DATA_BUF_SIZE - bytes_read); 1.204 + if (rv < 0) { 1.205 + fprintf(stderr,"Error - PR_Read failed: (%d, %d)\n", 1.206 + PR_GetError(), PR_GetOSError()); 1.207 + PR_Close(newsockfd); 1.208 + return; 1.209 + } 1.210 + PR_ASSERT(rv != 0); 1.211 + bytes_read += rv; 1.212 + } 1.213 + DPRINTF(("Bytes read from client - %d\n",bytes_read)); 1.214 + rv = PR_Write(newsockfd, data_buf,DATA_BUF_SIZE); 1.215 + if (rv < 0) { 1.216 + fprintf(stderr,"Error - PR_Write failed: (%d, %d)\n", 1.217 + PR_GetError(), PR_GetOSError()); 1.218 + PR_Close(newsockfd); 1.219 + return; 1.220 + } 1.221 + PR_ASSERT(rv == DATA_BUF_SIZE); 1.222 + DPRINTF(("Bytes written to client - %d\n",rv)); 1.223 + PR_Close(newsockfd); 1.224 +} 1.225 + 1.226 + 1.227 +/* 1.228 + * test for successful connection using a non-blocking socket 1.229 + */ 1.230 +static PRIntn 1.231 +connection_success_test() 1.232 +{ 1.233 + PRFileDesc *sockfd = NULL, *conn_fd = NULL; 1.234 + PRNetAddr netaddr; 1.235 + PRInt32 i, rv; 1.236 + PRPollDesc pd; 1.237 + PRSocketOptionData optData; 1.238 + PRThread *thr = NULL; 1.239 + Server_Param sp; 1.240 + char send_buf[DATA_BUF_SIZE], recv_buf[DATA_BUF_SIZE]; 1.241 + PRIntn default_case, n, bytes_read, bytes_sent; 1.242 + PRIntn failed_already = 0; 1.243 + 1.244 + /* 1.245 + * Create a tcp socket 1.246 + */ 1.247 + if ((sockfd = PR_NewTCPSocket()) == NULL) { 1.248 + fprintf(stderr,"Error - PR_NewTCPSocket failed\n"); 1.249 + failed_already=1; 1.250 + goto def_exit; 1.251 + } 1.252 + memset(&netaddr, 0 , sizeof(netaddr)); 1.253 + netaddr.inet.family = PR_AF_INET; 1.254 + netaddr.inet.port = PR_htons(TCP_SERVER_PORT); 1.255 + netaddr.inet.ip = PR_htonl(PR_INADDR_ANY); 1.256 + /* 1.257 + * try a few times to bind server's address, if addresses are in 1.258 + * use 1.259 + */ 1.260 + i = 0; 1.261 + while (PR_Bind(sockfd, &netaddr) < 0) { 1.262 + if (PR_GetError() == PR_ADDRESS_IN_USE_ERROR) { 1.263 + netaddr.inet.port += 2; 1.264 + if (i++ < SERVER_MAX_BIND_COUNT) 1.265 + continue; 1.266 + } 1.267 + fprintf(stderr,"ERROR - PR_Bind failed: (%d,%d)\n", 1.268 + PR_GetError(), PR_GetOSError()); 1.269 + failed_already=1; 1.270 + goto def_exit; 1.271 + } 1.272 + 1.273 + if (PR_Listen(sockfd, 32) < 0) { 1.274 + fprintf(stderr,"ERROR - PR_Listen failed: (%d,%d)\n", 1.275 + PR_GetError(), PR_GetOSError()); 1.276 + failed_already=1; 1.277 + goto def_exit; 1.278 + } 1.279 + 1.280 + if (PR_GetSockName(sockfd, &netaddr) < 0) { 1.281 + fprintf(stderr,"ERROR - PR_GetSockName failed: (%d,%d)\n", 1.282 + PR_GetError(), PR_GetOSError()); 1.283 + failed_already=1; 1.284 + goto def_exit; 1.285 + } 1.286 + if ((conn_fd = PR_NewTCPSocket()) == NULL) { 1.287 + fprintf(stderr,"Error - PR_NewTCPSocket failed\n"); 1.288 + failed_already=1; 1.289 + goto def_exit; 1.290 + } 1.291 + optData.option = PR_SockOpt_Nonblocking; 1.292 + optData.value.non_blocking = PR_TRUE; 1.293 + PR_SetSocketOption(conn_fd, &optData); 1.294 + rv = PR_Connect(conn_fd, &netaddr, PR_INTERVAL_NO_TIMEOUT); 1.295 + if (rv == PR_FAILURE) { 1.296 + if (PR_GetError() == PR_IN_PROGRESS_ERROR) { 1.297 + DPRINTF(("Connect in progress\n")); 1.298 + } else { 1.299 + fprintf(stderr,"Error - PR_Connect failed: (%d, %d)\n", 1.300 + PR_GetError(), PR_GetOSError()); 1.301 + failed_already=1; 1.302 + goto def_exit; 1.303 + } 1.304 + } 1.305 + /* 1.306 + * Now create a thread to accept a connection 1.307 + */ 1.308 + sp.sp_fd = sockfd; 1.309 + thr = PR_CreateThread(PR_USER_THREAD, TCP_Server, (void *)&sp, 1.310 + PR_PRIORITY_NORMAL, PR_LOCAL_THREAD, PR_JOINABLE_THREAD, 0); 1.311 + if (thr == NULL) { 1.312 + fprintf(stderr,"Error - PR_CreateThread failed: (%d,%d)\n", 1.313 + PR_GetError(), PR_GetOSError()); 1.314 + failed_already=1; 1.315 + goto def_exit; 1.316 + } 1.317 + DPRINTF(("Created TCP_Server thread [0x%x]\n",thr)); 1.318 + pd.fd = conn_fd; 1.319 + pd.in_flags = PR_POLL_WRITE | PR_POLL_EXCEPT; 1.320 + n = PR_Poll(&pd, 1, PR_INTERVAL_NO_TIMEOUT); 1.321 + if (n == -1) { 1.322 + fprintf(stderr,"Error - PR_Poll failed: (%d, %d)\n", 1.323 + PR_GetError(), PR_GetOSError()); 1.324 + failed_already=1; 1.325 + goto def_exit; 1.326 + } 1.327 + if (PR_GetConnectStatus(&pd) == PR_SUCCESS) { 1.328 + PRInt32 rv; 1.329 + 1.330 + DPRINTF(("Connection successful\n")); 1.331 + 1.332 + /* 1.333 + * Write some data, read it back and check data integrity to 1.334 + * make sure the connection is good 1.335 + */ 1.336 + pd.in_flags = PR_POLL_WRITE; 1.337 + bytes_sent = 0; 1.338 + memset(send_buf, 'a', DATA_BUF_SIZE); 1.339 + while (bytes_sent != DATA_BUF_SIZE) { 1.340 + rv = PR_Poll(&pd, 1, PR_INTERVAL_NO_TIMEOUT); 1.341 + if (rv < 0) { 1.342 + fprintf(stderr,"Error - PR_Poll failed: (%d, %d)\n", 1.343 + PR_GetError(), PR_GetOSError()); 1.344 + failed_already=1; 1.345 + goto def_exit; 1.346 + } 1.347 + PR_ASSERT((rv == 1) && (pd.out_flags == PR_POLL_WRITE)); 1.348 + rv = PR_Write(conn_fd, send_buf + bytes_sent, 1.349 + DATA_BUF_SIZE - bytes_sent); 1.350 + if (rv < 0) { 1.351 + fprintf(stderr,"Error - PR_Write failed: (%d, %d)\n", 1.352 + PR_GetError(), PR_GetOSError()); 1.353 + failed_already=1; 1.354 + goto def_exit; 1.355 + } 1.356 + PR_ASSERT(rv > 0); 1.357 + bytes_sent += rv; 1.358 + } 1.359 + DPRINTF(("Bytes written to server - %d\n",bytes_sent)); 1.360 + PR_Shutdown(conn_fd, PR_SHUTDOWN_SEND); 1.361 + pd.in_flags = PR_POLL_READ; 1.362 + bytes_read = 0; 1.363 + memset(recv_buf, 0, DATA_BUF_SIZE); 1.364 + while (bytes_read != DATA_BUF_SIZE) { 1.365 + rv = PR_Poll(&pd, 1, PR_INTERVAL_NO_TIMEOUT); 1.366 + if (rv < 0) { 1.367 + fprintf(stderr,"Error - PR_Poll failed: (%d, %d)\n", 1.368 + PR_GetError(), PR_GetOSError()); 1.369 + failed_already=1; 1.370 + goto def_exit; 1.371 + } 1.372 + PR_ASSERT((rv == 1) && (pd.out_flags == PR_POLL_READ)); 1.373 + rv = PR_Read(conn_fd, recv_buf + bytes_read , 1.374 + DATA_BUF_SIZE - bytes_read); 1.375 + if (rv < 0) { 1.376 + fprintf(stderr,"Error - PR_Read failed: (%d, %d)\n", 1.377 + PR_GetError(), PR_GetOSError()); 1.378 + failed_already=1; 1.379 + goto def_exit; 1.380 + } 1.381 + PR_ASSERT(rv != 0); 1.382 + bytes_read += rv; 1.383 + } 1.384 + DPRINTF(("Bytes read from server - %d\n",bytes_read)); 1.385 + /* 1.386 + * verify the data read 1.387 + */ 1.388 + if (memcmp(send_buf, recv_buf, DATA_BUF_SIZE) != 0) { 1.389 + fprintf(stderr,"ERROR - data corruption\n"); 1.390 + failed_already=1; 1.391 + goto def_exit; 1.392 + } 1.393 + DPRINTF(("Data integrity verified\n")); 1.394 + } else { 1.395 + fprintf(stderr,"PR_GetConnectStatus: connect failed: (%ld, %ld)\n", 1.396 + PR_GetError(), PR_GetOSError()); 1.397 + failed_already = 1; 1.398 + goto def_exit; 1.399 + } 1.400 +def_exit: 1.401 + if (thr) { 1.402 + PR_JoinThread(thr); 1.403 + thr = NULL; 1.404 + } 1.405 + if (sockfd) { 1.406 + PR_Close(sockfd); 1.407 + sockfd = NULL; 1.408 + } 1.409 + if (conn_fd) { 1.410 + PR_Close(conn_fd); 1.411 + conn_fd = NULL; 1.412 + } 1.413 + if (failed_already) 1.414 + return 1; 1.415 + else 1.416 + return 0; 1.417 + 1.418 +} 1.419 + 1.420 +/* 1.421 + * test for connection to a nonexistent port using a non-blocking socket 1.422 + */ 1.423 +static PRIntn 1.424 +connection_failure_test() 1.425 +{ 1.426 + PRFileDesc *sockfd = NULL, *conn_fd = NULL; 1.427 + PRNetAddr netaddr; 1.428 + PRInt32 i, rv; 1.429 + PRPollDesc pd; 1.430 + PRSocketOptionData optData; 1.431 + PRIntn n, failed_already = 0; 1.432 + 1.433 + /* 1.434 + * Create a tcp socket 1.435 + */ 1.436 + if ((sockfd = PR_NewTCPSocket()) == NULL) { 1.437 + fprintf(stderr,"Error - PR_NewTCPSocket failed\n"); 1.438 + failed_already=1; 1.439 + goto def_exit; 1.440 + } 1.441 + memset(&netaddr, 0 , sizeof(netaddr)); 1.442 + netaddr.inet.family = PR_AF_INET; 1.443 + netaddr.inet.port = PR_htons(TCP_SERVER_PORT); 1.444 + netaddr.inet.ip = PR_htonl(PR_INADDR_ANY); 1.445 + /* 1.446 + * try a few times to bind server's address, if addresses are in 1.447 + * use 1.448 + */ 1.449 + i = 0; 1.450 + while (PR_Bind(sockfd, &netaddr) < 0) { 1.451 + if (PR_GetError() == PR_ADDRESS_IN_USE_ERROR) { 1.452 + netaddr.inet.port += 2; 1.453 + if (i++ < SERVER_MAX_BIND_COUNT) 1.454 + continue; 1.455 + } 1.456 + fprintf(stderr,"ERROR - PR_Bind failed: (%d,%d)\n", 1.457 + PR_GetError(), PR_GetOSError()); 1.458 + failed_already=1; 1.459 + goto def_exit; 1.460 + } 1.461 + 1.462 + if (PR_GetSockName(sockfd, &netaddr) < 0) { 1.463 + fprintf(stderr,"ERROR - PR_GetSockName failed: (%d,%d)\n", 1.464 + PR_GetError(), PR_GetOSError()); 1.465 + failed_already=1; 1.466 + goto def_exit; 1.467 + } 1.468 +#ifdef AIX 1.469 + /* 1.470 + * On AIX, set to unused/reserved port 1.471 + */ 1.472 + netaddr.inet.port = PR_htons(TCP_UNUSED_PORT); 1.473 +#endif 1.474 + if ((conn_fd = PR_NewTCPSocket()) == NULL) { 1.475 + fprintf(stderr,"Error - PR_NewTCPSocket failed\n"); 1.476 + failed_already=1; 1.477 + goto def_exit; 1.478 + } 1.479 + optData.option = PR_SockOpt_Nonblocking; 1.480 + optData.value.non_blocking = PR_TRUE; 1.481 + PR_SetSocketOption(conn_fd, &optData); 1.482 + rv = PR_Connect(conn_fd, &netaddr, PR_INTERVAL_NO_TIMEOUT); 1.483 + if (rv == PR_FAILURE) { 1.484 + DPRINTF(("PR_Connect to a non-listen port failed: (%d, %d)\n", 1.485 + PR_GetError(), PR_GetOSError())); 1.486 + } else { 1.487 + PR_ASSERT(rv == PR_SUCCESS); 1.488 + fprintf(stderr,"Error - PR_Connect succeeded, expected to fail\n"); 1.489 + failed_already=1; 1.490 + goto def_exit; 1.491 + } 1.492 + pd.fd = conn_fd; 1.493 + pd.in_flags = PR_POLL_WRITE | PR_POLL_EXCEPT; 1.494 + n = PR_Poll(&pd, 1, PR_INTERVAL_NO_TIMEOUT); 1.495 + if (n == -1) { 1.496 + fprintf(stderr,"Error - PR_Poll failed: (%d, %d)\n", 1.497 + PR_GetError(), PR_GetOSError()); 1.498 + failed_already=1; 1.499 + goto def_exit; 1.500 + } 1.501 + if (PR_GetConnectStatus(&pd) == PR_SUCCESS) { 1.502 + PRInt32 rv; 1.503 + fprintf(stderr,"PR_GetConnectStatus succeeded, expected to fail\n"); 1.504 + failed_already = 1; 1.505 + goto def_exit; 1.506 + } 1.507 + rv = PR_GetError(); 1.508 + DPRINTF(("Connection failed, successfully with PR_Error %d\n",rv)); 1.509 +def_exit: 1.510 + if (sockfd) { 1.511 + PR_Close(sockfd); 1.512 + sockfd = NULL; 1.513 + } 1.514 + if (conn_fd) { 1.515 + PR_Close(conn_fd); 1.516 + conn_fd = NULL; 1.517 + } 1.518 + if (failed_already) 1.519 + return 1; 1.520 + else 1.521 + return 0; 1.522 + 1.523 +}