nbconn.c (14758B)
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* This Source Code Form is subject to the terms of the Mozilla Public 3 * License, v. 2.0. If a copy of the MPL was not distributed with this 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 5 6 /* 7 * A test for nonblocking connect. Functions tested include PR_Connect, 8 * PR_Poll, and PR_GetConnectStatus. 9 * 10 * The test should be invoked with a host name, for example: 11 * nbconn www.netscape.com 12 * It will do a nonblocking connect to port 80 (HTTP) on that host, 13 * and when connected, issue the "GET /" HTTP command. 14 * 15 * You should run this test in three ways: 16 * 1. To a known web site, such as www.netscape.com. The HTML of the 17 * top-level page at the web site should be printed. 18 * 2. To a machine not running a web server at port 80. This test should 19 * fail. Ideally the error code should be PR_CONNECT_REFUSED_ERROR. 20 * But it is possible to return PR_UNKNOWN_ERROR on certain platforms. 21 * 3. To an unreachable machine, for example, a machine that is off line. 22 * The test should fail after the connect times out. Ideally the 23 * error code should be PR_IO_TIMEOUT_ERROR, but it is possible to 24 * return PR_UNKNOWN_ERROR on certain platforms. 25 */ 26 27 #include "nspr.h" 28 #include "plgetopt.h" 29 #include <stdio.h> 30 #include <string.h> 31 32 #define SERVER_MAX_BIND_COUNT 100 33 #define DATA_BUF_SIZE 256 34 #define TCP_SERVER_PORT 10000 35 #define TCP_UNUSED_PORT 211 36 37 typedef struct Server_Param { 38 PRFileDesc* sp_fd; /* server port */ 39 } Server_Param; 40 static void PR_CALLBACK TCP_Server(void* arg); 41 42 int _debug_on; 43 #define DPRINTF(arg) \ 44 if (_debug_on) printf arg 45 46 static PRIntn connection_success_test(); 47 static PRIntn connection_failure_test(); 48 49 int main(int argc, char** argv) { 50 PRHostEnt he; 51 char buf[1024]; 52 PRNetAddr addr; 53 PRPollDesc pd; 54 PRStatus rv; 55 PRSocketOptionData optData; 56 const char* hostname = NULL; 57 PRIntn default_case, n, bytes_read, bytes_sent; 58 PRInt32 failed_already = 0; 59 60 /* 61 * -d debug mode 62 */ 63 64 PLOptStatus os; 65 PLOptState* opt = PL_CreateOptState(argc, argv, "d"); 66 while (PL_OPT_EOL != (os = PL_GetNextOpt(opt))) { 67 if (PL_OPT_BAD == os) { 68 continue; 69 } 70 switch (opt->option) { 71 case 0: /* debug mode */ 72 hostname = opt->value; 73 break; 74 case 'd': /* debug mode */ 75 _debug_on = 1; 76 break; 77 default: 78 break; 79 } 80 } 81 PL_DestroyOptState(opt); 82 83 if (hostname) { 84 default_case = 0; 85 } else { 86 default_case = 1; 87 } 88 89 if (default_case) { 90 /* 91 * In the default case the following tests are executed: 92 * 1. successful connection: a server thread accepts a connection 93 * from the main thread 94 * 2. unsuccessful connection: the main thread tries to connect to a 95 * nonexistent port and expects to get an error 96 */ 97 rv = connection_success_test(); 98 if (rv == 0) { 99 rv = connection_failure_test(); 100 } 101 return rv; 102 } else { 103 PRFileDesc* sock; 104 105 if (PR_GetHostByName(argv[1], buf, sizeof(buf), &he) == PR_FAILURE) { 106 printf("Unknown host: %s\n", argv[1]); 107 exit(1); 108 } else { 109 printf("host: %s\n", buf); 110 } 111 PR_EnumerateHostEnt(0, &he, 80, &addr); 112 113 sock = PR_NewTCPSocket(); 114 optData.option = PR_SockOpt_Nonblocking; 115 optData.value.non_blocking = PR_TRUE; 116 PR_SetSocketOption(sock, &optData); 117 rv = PR_Connect(sock, &addr, PR_INTERVAL_NO_TIMEOUT); 118 if (rv == PR_FAILURE && PR_GetError() == PR_IN_PROGRESS_ERROR) { 119 printf("Connect in progress\n"); 120 } 121 122 pd.fd = sock; 123 pd.in_flags = PR_POLL_WRITE | PR_POLL_EXCEPT; 124 n = PR_Poll(&pd, 1, PR_INTERVAL_NO_TIMEOUT); 125 if (n == -1) { 126 printf("PR_Poll failed\n"); 127 exit(1); 128 } 129 printf("PR_Poll returns %d\n", n); 130 if (pd.out_flags & PR_POLL_READ) { 131 printf("PR_POLL_READ\n"); 132 } 133 if (pd.out_flags & PR_POLL_WRITE) { 134 printf("PR_POLL_WRITE\n"); 135 } 136 if (pd.out_flags & PR_POLL_EXCEPT) { 137 printf("PR_POLL_EXCEPT\n"); 138 } 139 if (pd.out_flags & PR_POLL_ERR) { 140 printf("PR_POLL_ERR\n"); 141 } 142 if (pd.out_flags & PR_POLL_NVAL) { 143 printf("PR_POLL_NVAL\n"); 144 } 145 146 if (PR_GetConnectStatus(&pd) == PR_SUCCESS) { 147 printf("PR_GetConnectStatus: connect succeeded\n"); 148 PR_Write(sock, "GET /\r\n\r\n", 9); 149 PR_Shutdown(sock, PR_SHUTDOWN_SEND); 150 pd.in_flags = PR_POLL_READ; 151 while (1) { 152 n = PR_Poll(&pd, 1, PR_INTERVAL_NO_TIMEOUT); 153 printf("poll returns %d\n", n); 154 n = PR_Read(sock, buf, sizeof(buf)); 155 printf("read returns %d\n", n); 156 if (n <= 0) { 157 break; 158 } 159 PR_Write(PR_STDOUT, buf, n); 160 } 161 } else { 162 if (PR_GetError() == PR_IN_PROGRESS_ERROR) { 163 printf("PR_GetConnectStatus: connect still in progress\n"); 164 exit(1); 165 } 166 printf("PR_GetConnectStatus: connect failed: (%ld, %ld)\n", PR_GetError(), 167 PR_GetOSError()); 168 } 169 PR_Close(sock); 170 printf("PASS\n"); 171 return 0; 172 } 173 } 174 175 /* 176 * TCP Server 177 * Server Thread 178 * Accept a connection from the client and write some data 179 */ 180 static void PR_CALLBACK TCP_Server(void* arg) { 181 Server_Param* sp = (Server_Param*)arg; 182 PRFileDesc *sockfd, *newsockfd; 183 char data_buf[DATA_BUF_SIZE]; 184 PRIntn rv, bytes_read; 185 186 sockfd = sp->sp_fd; 187 if ((newsockfd = PR_Accept(sockfd, NULL, PR_INTERVAL_NO_TIMEOUT)) == NULL) { 188 fprintf(stderr, "ERROR - PR_Accept failed: (%d,%d)\n", PR_GetError(), 189 PR_GetOSError()); 190 return; 191 } 192 bytes_read = 0; 193 while (bytes_read != DATA_BUF_SIZE) { 194 rv = PR_Read(newsockfd, data_buf + bytes_read, DATA_BUF_SIZE - bytes_read); 195 if (rv < 0) { 196 fprintf(stderr, "Error - PR_Read failed: (%d, %d)\n", PR_GetError(), 197 PR_GetOSError()); 198 PR_Close(newsockfd); 199 return; 200 } 201 PR_ASSERT(rv != 0); 202 bytes_read += rv; 203 } 204 DPRINTF(("Bytes read from client - %d\n", bytes_read)); 205 rv = PR_Write(newsockfd, data_buf, DATA_BUF_SIZE); 206 if (rv < 0) { 207 fprintf(stderr, "Error - PR_Write failed: (%d, %d)\n", PR_GetError(), 208 PR_GetOSError()); 209 PR_Close(newsockfd); 210 return; 211 } 212 PR_ASSERT(rv == DATA_BUF_SIZE); 213 DPRINTF(("Bytes written to client - %d\n", rv)); 214 PR_Close(newsockfd); 215 } 216 217 /* 218 * test for successful connection using a non-blocking socket 219 */ 220 static PRIntn connection_success_test() { 221 PRFileDesc *sockfd = NULL, *conn_fd = NULL; 222 PRNetAddr netaddr; 223 PRInt32 i, rv; 224 PRPollDesc pd; 225 PRSocketOptionData optData; 226 PRThread* thr = NULL; 227 Server_Param sp; 228 char send_buf[DATA_BUF_SIZE], recv_buf[DATA_BUF_SIZE]; 229 PRIntn default_case, n, bytes_read, bytes_sent; 230 PRIntn failed_already = 0; 231 232 /* 233 * Create a tcp socket 234 */ 235 if ((sockfd = PR_NewTCPSocket()) == NULL) { 236 fprintf(stderr, "Error - PR_NewTCPSocket failed\n"); 237 failed_already = 1; 238 goto def_exit; 239 } 240 memset(&netaddr, 0, sizeof(netaddr)); 241 netaddr.inet.family = PR_AF_INET; 242 netaddr.inet.port = PR_htons(TCP_SERVER_PORT); 243 netaddr.inet.ip = PR_htonl(PR_INADDR_ANY); 244 /* 245 * try a few times to bind server's address, if addresses are in 246 * use 247 */ 248 i = 0; 249 while (PR_Bind(sockfd, &netaddr) < 0) { 250 if (PR_GetError() == PR_ADDRESS_IN_USE_ERROR) { 251 netaddr.inet.port += 2; 252 if (i++ < SERVER_MAX_BIND_COUNT) { 253 continue; 254 } 255 } 256 fprintf(stderr, "ERROR - PR_Bind failed: (%d,%d)\n", PR_GetError(), 257 PR_GetOSError()); 258 failed_already = 1; 259 goto def_exit; 260 } 261 262 if (PR_Listen(sockfd, 32) < 0) { 263 fprintf(stderr, "ERROR - PR_Listen failed: (%d,%d)\n", PR_GetError(), 264 PR_GetOSError()); 265 failed_already = 1; 266 goto def_exit; 267 } 268 269 if (PR_GetSockName(sockfd, &netaddr) < 0) { 270 fprintf(stderr, "ERROR - PR_GetSockName failed: (%d,%d)\n", PR_GetError(), 271 PR_GetOSError()); 272 failed_already = 1; 273 goto def_exit; 274 } 275 if ((conn_fd = PR_NewTCPSocket()) == NULL) { 276 fprintf(stderr, "Error - PR_NewTCPSocket failed\n"); 277 failed_already = 1; 278 goto def_exit; 279 } 280 optData.option = PR_SockOpt_Nonblocking; 281 optData.value.non_blocking = PR_TRUE; 282 PR_SetSocketOption(conn_fd, &optData); 283 rv = PR_Connect(conn_fd, &netaddr, PR_INTERVAL_NO_TIMEOUT); 284 if (rv == PR_FAILURE) { 285 if (PR_GetError() == PR_IN_PROGRESS_ERROR) { 286 DPRINTF(("Connect in progress\n")); 287 } else { 288 fprintf(stderr, "Error - PR_Connect failed: (%d, %d)\n", PR_GetError(), 289 PR_GetOSError()); 290 failed_already = 1; 291 goto def_exit; 292 } 293 } 294 /* 295 * Now create a thread to accept a connection 296 */ 297 sp.sp_fd = sockfd; 298 thr = PR_CreateThread(PR_USER_THREAD, TCP_Server, (void*)&sp, 299 PR_PRIORITY_NORMAL, PR_LOCAL_THREAD, PR_JOINABLE_THREAD, 300 0); 301 if (thr == NULL) { 302 fprintf(stderr, "Error - PR_CreateThread failed: (%d,%d)\n", PR_GetError(), 303 PR_GetOSError()); 304 failed_already = 1; 305 goto def_exit; 306 } 307 DPRINTF(("Created TCP_Server thread [0x%x]\n", thr)); 308 pd.fd = conn_fd; 309 pd.in_flags = PR_POLL_WRITE | PR_POLL_EXCEPT; 310 n = PR_Poll(&pd, 1, PR_INTERVAL_NO_TIMEOUT); 311 if (n == -1) { 312 fprintf(stderr, "Error - PR_Poll failed: (%d, %d)\n", PR_GetError(), 313 PR_GetOSError()); 314 failed_already = 1; 315 goto def_exit; 316 } 317 if (PR_GetConnectStatus(&pd) == PR_SUCCESS) { 318 PRInt32 rv; 319 320 DPRINTF(("Connection successful\n")); 321 322 /* 323 * Write some data, read it back and check data integrity to 324 * make sure the connection is good 325 */ 326 pd.in_flags = PR_POLL_WRITE; 327 bytes_sent = 0; 328 memset(send_buf, 'a', DATA_BUF_SIZE); 329 while (bytes_sent != DATA_BUF_SIZE) { 330 rv = PR_Poll(&pd, 1, PR_INTERVAL_NO_TIMEOUT); 331 if (rv < 0) { 332 fprintf(stderr, "Error - PR_Poll failed: (%d, %d)\n", PR_GetError(), 333 PR_GetOSError()); 334 failed_already = 1; 335 goto def_exit; 336 } 337 PR_ASSERT((rv == 1) && (pd.out_flags == PR_POLL_WRITE)); 338 rv = PR_Write(conn_fd, send_buf + bytes_sent, DATA_BUF_SIZE - bytes_sent); 339 if (rv < 0) { 340 fprintf(stderr, "Error - PR_Write failed: (%d, %d)\n", PR_GetError(), 341 PR_GetOSError()); 342 failed_already = 1; 343 goto def_exit; 344 } 345 PR_ASSERT(rv > 0); 346 bytes_sent += rv; 347 } 348 DPRINTF(("Bytes written to server - %d\n", bytes_sent)); 349 PR_Shutdown(conn_fd, PR_SHUTDOWN_SEND); 350 pd.in_flags = PR_POLL_READ; 351 bytes_read = 0; 352 memset(recv_buf, 0, DATA_BUF_SIZE); 353 while (bytes_read != DATA_BUF_SIZE) { 354 rv = PR_Poll(&pd, 1, PR_INTERVAL_NO_TIMEOUT); 355 if (rv < 0) { 356 fprintf(stderr, "Error - PR_Poll failed: (%d, %d)\n", PR_GetError(), 357 PR_GetOSError()); 358 failed_already = 1; 359 goto def_exit; 360 } 361 PR_ASSERT((rv == 1) && (pd.out_flags == PR_POLL_READ)); 362 rv = PR_Read(conn_fd, recv_buf + bytes_read, DATA_BUF_SIZE - bytes_read); 363 if (rv < 0) { 364 fprintf(stderr, "Error - PR_Read failed: (%d, %d)\n", PR_GetError(), 365 PR_GetOSError()); 366 failed_already = 1; 367 goto def_exit; 368 } 369 PR_ASSERT(rv != 0); 370 bytes_read += rv; 371 } 372 DPRINTF(("Bytes read from server - %d\n", bytes_read)); 373 /* 374 * verify the data read 375 */ 376 if (memcmp(send_buf, recv_buf, DATA_BUF_SIZE) != 0) { 377 fprintf(stderr, "ERROR - data corruption\n"); 378 failed_already = 1; 379 goto def_exit; 380 } 381 DPRINTF(("Data integrity verified\n")); 382 } else { 383 fprintf(stderr, "PR_GetConnectStatus: connect failed: (%ld, %ld)\n", 384 PR_GetError(), PR_GetOSError()); 385 failed_already = 1; 386 goto def_exit; 387 } 388 def_exit: 389 if (thr) { 390 PR_JoinThread(thr); 391 thr = NULL; 392 } 393 if (sockfd) { 394 PR_Close(sockfd); 395 sockfd = NULL; 396 } 397 if (conn_fd) { 398 PR_Close(conn_fd); 399 conn_fd = NULL; 400 } 401 if (failed_already) { 402 return 1; 403 } else { 404 return 0; 405 } 406 } 407 408 /* 409 * test for connection to a nonexistent port using a non-blocking socket 410 */ 411 static PRIntn connection_failure_test() { 412 PRFileDesc *sockfd = NULL, *conn_fd = NULL; 413 PRNetAddr netaddr; 414 PRInt32 i, rv; 415 PRPollDesc pd; 416 PRSocketOptionData optData; 417 PRIntn n, failed_already = 0; 418 419 /* 420 * Create a tcp socket 421 */ 422 if ((sockfd = PR_NewTCPSocket()) == NULL) { 423 fprintf(stderr, "Error - PR_NewTCPSocket failed\n"); 424 failed_already = 1; 425 goto def_exit; 426 } 427 memset(&netaddr, 0, sizeof(netaddr)); 428 netaddr.inet.family = PR_AF_INET; 429 netaddr.inet.port = PR_htons(TCP_SERVER_PORT); 430 netaddr.inet.ip = PR_htonl(PR_INADDR_ANY); 431 /* 432 * try a few times to bind server's address, if addresses are in 433 * use 434 */ 435 i = 0; 436 while (PR_Bind(sockfd, &netaddr) < 0) { 437 if (PR_GetError() == PR_ADDRESS_IN_USE_ERROR) { 438 netaddr.inet.port += 2; 439 if (i++ < SERVER_MAX_BIND_COUNT) { 440 continue; 441 } 442 } 443 fprintf(stderr, "ERROR - PR_Bind failed: (%d,%d)\n", PR_GetError(), 444 PR_GetOSError()); 445 failed_already = 1; 446 goto def_exit; 447 } 448 449 if (PR_GetSockName(sockfd, &netaddr) < 0) { 450 fprintf(stderr, "ERROR - PR_GetSockName failed: (%d,%d)\n", PR_GetError(), 451 PR_GetOSError()); 452 failed_already = 1; 453 goto def_exit; 454 } 455 #ifdef AIX 456 /* 457 * On AIX, set to unused/reserved port 458 */ 459 netaddr.inet.port = PR_htons(TCP_UNUSED_PORT); 460 #endif 461 if ((conn_fd = PR_NewTCPSocket()) == NULL) { 462 fprintf(stderr, "Error - PR_NewTCPSocket failed\n"); 463 failed_already = 1; 464 goto def_exit; 465 } 466 optData.option = PR_SockOpt_Nonblocking; 467 optData.value.non_blocking = PR_TRUE; 468 PR_SetSocketOption(conn_fd, &optData); 469 rv = PR_Connect(conn_fd, &netaddr, PR_INTERVAL_NO_TIMEOUT); 470 if (rv == PR_FAILURE) { 471 DPRINTF(("PR_Connect to a non-listen port failed: (%d, %d)\n", 472 PR_GetError(), PR_GetOSError())); 473 } else { 474 PR_ASSERT(rv == PR_SUCCESS); 475 fprintf(stderr, "Error - PR_Connect succeeded, expected to fail\n"); 476 failed_already = 1; 477 goto def_exit; 478 } 479 pd.fd = conn_fd; 480 pd.in_flags = PR_POLL_WRITE | PR_POLL_EXCEPT; 481 n = PR_Poll(&pd, 1, PR_INTERVAL_NO_TIMEOUT); 482 if (n == -1) { 483 fprintf(stderr, "Error - PR_Poll failed: (%d, %d)\n", PR_GetError(), 484 PR_GetOSError()); 485 failed_already = 1; 486 goto def_exit; 487 } 488 if (PR_GetConnectStatus(&pd) == PR_SUCCESS) { 489 PRInt32 rv; 490 fprintf(stderr, "PR_GetConnectStatus succeeded, expected to fail\n"); 491 failed_already = 1; 492 goto def_exit; 493 } 494 rv = PR_GetError(); 495 DPRINTF(("Connection failed, successfully with PR_Error %d\n", rv)); 496 def_exit: 497 if (sockfd) { 498 PR_Close(sockfd); 499 sockfd = NULL; 500 } 501 if (conn_fd) { 502 PR_Close(conn_fd); 503 conn_fd = NULL; 504 } 505 if (failed_already) { 506 return 1; 507 } else { 508 return 0; 509 } 510 }