nsprpub/pr/tests/nbconn.c

Fri, 16 Jan 2015 18:13:44 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Fri, 16 Jan 2015 18:13:44 +0100
branch
TOR_BUG_9701
changeset 14
925c144e1f1f
permissions
-rw-r--r--

Integrate suggestion from review to improve consistency with existing code.

     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/. */
     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  */
    27 #include "nspr.h"
    28 #include "plgetopt.h"
    29 #include <stdio.h>
    30 #include <string.h>
    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
    37 typedef struct Server_Param {
    38     PRFileDesc *sp_fd;		/* server port */
    39 } Server_Param;
    40 static void PR_CALLBACK TCP_Server(void *arg);
    42 int _debug_on;
    43 #define DPRINTF(arg) if (_debug_on) printf arg
    45 static PRIntn connection_success_test();
    46 static PRIntn connection_failure_test();
    48 int main(int argc, char **argv)
    49 {
    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;
    60     /*
    61      * -d           debug mode
    62      */
    64     PLOptStatus os;
    65     PLOptState *opt = PL_CreateOptState(argc, argv, "d");
    66     while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
    67     {
    68         if (PL_OPT_BAD == os) continue;
    69         switch (opt->option)
    70         {
    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);
    83     PR_STDIO_INIT();
    84     if (hostname)
    85 		default_case = 0;
    86 	else
    87 		default_case = 1;
    89 	if (default_case) {
    91 		/*
    92 		 * In the default case the following tests are executed:
    93 		 *	1. successful connection: a server thread accepts a connection
    94 		 *	   from the main thread
    95 		 *	2. unsuccessful connection: the main thread tries to connect to a
    96 		 *	   nonexistent port and expects to get an error
    97 		 */
    98 		rv = connection_success_test();
    99 		if (rv == 0)
   100 			rv = connection_failure_test();
   101 		return rv;
   102 	} else {
   103     	PRFileDesc *sock;
   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);
   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 		}
   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 		}
   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",
   167 					PR_GetError(), PR_GetOSError());
   168 		}
   169 		PR_Close(sock);
   170     	printf( "PASS\n");
   171     	return 0;
   173 	}
   174 }
   177 /*
   178  * TCP Server
   179  *    Server Thread
   180  *    Accept a connection from the client and write some data
   181  */
   182 static void PR_CALLBACK
   183 TCP_Server(void *arg)
   184 {
   185     Server_Param *sp = (Server_Param *) arg;
   186     PRFileDesc *sockfd, *newsockfd;
   187 	char data_buf[DATA_BUF_SIZE];
   188     PRIntn rv, bytes_read;
   190 	sockfd = sp->sp_fd;
   191 	if ((newsockfd = PR_Accept(sockfd, NULL,
   192 		PR_INTERVAL_NO_TIMEOUT)) == NULL) {
   193 		fprintf(stderr,"ERROR - PR_Accept failed: (%d,%d)\n",
   194 										PR_GetError(), PR_GetOSError());
   195 		return;
   196 	}
   197 	bytes_read = 0;
   198 	while (bytes_read != DATA_BUF_SIZE) {
   199 		rv = PR_Read(newsockfd, data_buf + bytes_read ,
   200 									DATA_BUF_SIZE - bytes_read);
   201 		if (rv < 0) {
   202 			fprintf(stderr,"Error - PR_Read failed: (%d, %d)\n",
   203 							PR_GetError(), PR_GetOSError());
   204 			PR_Close(newsockfd);
   205 			return;
   206 		}
   207 		PR_ASSERT(rv != 0);
   208 		bytes_read += rv;
   209 	}
   210 	DPRINTF(("Bytes read from client - %d\n",bytes_read));
   211 	rv = PR_Write(newsockfd, data_buf,DATA_BUF_SIZE);
   212 	if (rv < 0) {
   213 		fprintf(stderr,"Error - PR_Write failed: (%d, %d)\n",
   214 						PR_GetError(), PR_GetOSError());
   215 		PR_Close(newsockfd);
   216 		return;
   217 	}
   218 	PR_ASSERT(rv == DATA_BUF_SIZE);
   219 	DPRINTF(("Bytes written to client - %d\n",rv));
   220 	PR_Close(newsockfd);
   221 }
   224 /*
   225  * test for successful connection using a non-blocking socket
   226  */
   227 static PRIntn
   228 connection_success_test()
   229 {
   230 	PRFileDesc *sockfd = NULL, *conn_fd = NULL;
   231 	PRNetAddr netaddr;
   232 	PRInt32 i, rv;
   233     PRPollDesc pd;
   234     PRSocketOptionData optData;
   235 	PRThread *thr = NULL;
   236 	Server_Param sp;
   237 	char send_buf[DATA_BUF_SIZE], recv_buf[DATA_BUF_SIZE];
   238     PRIntn default_case, n, bytes_read, bytes_sent;
   239     PRIntn failed_already = 0;
   241 	/*
   242 	 * Create a tcp socket
   243 	 */
   244 	if ((sockfd = PR_NewTCPSocket()) == NULL) {
   245 		fprintf(stderr,"Error - PR_NewTCPSocket failed\n");
   246 		failed_already=1;
   247 		goto def_exit;
   248 	}
   249 	memset(&netaddr, 0 , sizeof(netaddr));
   250 	netaddr.inet.family = PR_AF_INET;
   251 	netaddr.inet.port = PR_htons(TCP_SERVER_PORT);
   252 	netaddr.inet.ip = PR_htonl(PR_INADDR_ANY);
   253 	/*
   254 	 * try a few times to bind server's address, if addresses are in
   255 	 * use
   256 	 */
   257 	i = 0;
   258 	while (PR_Bind(sockfd, &netaddr) < 0) {
   259 		if (PR_GetError() == PR_ADDRESS_IN_USE_ERROR) {
   260 			netaddr.inet.port += 2;
   261 			if (i++ < SERVER_MAX_BIND_COUNT)
   262 				continue;
   263 		}
   264 		fprintf(stderr,"ERROR - PR_Bind failed: (%d,%d)\n",
   265 									PR_GetError(), PR_GetOSError());
   266 		failed_already=1;
   267 		goto def_exit;
   268 	}
   270 	if (PR_Listen(sockfd, 32) < 0) {
   271 		fprintf(stderr,"ERROR - PR_Listen failed: (%d,%d)\n",
   272 									PR_GetError(), PR_GetOSError());
   273 		failed_already=1;
   274 		goto def_exit;
   275 	}
   277 	if (PR_GetSockName(sockfd, &netaddr) < 0) {
   278 		fprintf(stderr,"ERROR - PR_GetSockName failed: (%d,%d)\n",
   279 									PR_GetError(), PR_GetOSError());
   280 		failed_already=1;
   281 		goto def_exit;
   282 	}
   283 	if ((conn_fd = PR_NewTCPSocket()) == NULL) {
   284 		fprintf(stderr,"Error - PR_NewTCPSocket failed\n");
   285 		failed_already=1;
   286 		goto def_exit;
   287 	}
   288 	optData.option = PR_SockOpt_Nonblocking;
   289 	optData.value.non_blocking = PR_TRUE;
   290 	PR_SetSocketOption(conn_fd, &optData);
   291 	rv = PR_Connect(conn_fd, &netaddr, PR_INTERVAL_NO_TIMEOUT);
   292 	if (rv == PR_FAILURE) {
   293 		if (PR_GetError() == PR_IN_PROGRESS_ERROR) {
   294 			DPRINTF(("Connect in progress\n"));
   295 		} else  {
   296 			fprintf(stderr,"Error - PR_Connect failed: (%d, %d)\n",
   297 									PR_GetError(), PR_GetOSError());
   298 			failed_already=1;
   299 			goto def_exit;
   300 		}
   301 	}
   302 	/*
   303 	 * Now create a thread to accept a connection
   304 	 */
   305 	sp.sp_fd = sockfd;
   306 	thr = PR_CreateThread(PR_USER_THREAD, TCP_Server, (void *)&sp, 
   307 			PR_PRIORITY_NORMAL, PR_LOCAL_THREAD, PR_JOINABLE_THREAD, 0);
   308 	if (thr == NULL) {
   309 		fprintf(stderr,"Error - PR_CreateThread failed: (%d,%d)\n",
   310 									PR_GetError(), PR_GetOSError());
   311 		failed_already=1;
   312 		goto def_exit;
   313 	}
   314 	DPRINTF(("Created TCP_Server thread [0x%x]\n",thr));
   315 	pd.fd = conn_fd;
   316 	pd.in_flags = PR_POLL_WRITE | PR_POLL_EXCEPT;
   317 	n = PR_Poll(&pd, 1, PR_INTERVAL_NO_TIMEOUT);
   318 	if (n == -1) {
   319 		fprintf(stderr,"Error - PR_Poll failed: (%d, %d)\n",
   320 									PR_GetError(), PR_GetOSError());
   321 		failed_already=1;
   322 		goto def_exit;
   323 	}
   324 	if (PR_GetConnectStatus(&pd) == PR_SUCCESS) {
   325 		PRInt32 rv;
   327 		DPRINTF(("Connection successful\n"));
   329 		/*
   330 		 * Write some data, read it back and check data integrity to
   331 		 * make sure the connection is good
   332 		 */
   333 		pd.in_flags = PR_POLL_WRITE;
   334 		bytes_sent = 0;
   335 		memset(send_buf, 'a', DATA_BUF_SIZE);
   336 		while (bytes_sent != DATA_BUF_SIZE) {
   337 			rv = PR_Poll(&pd, 1, PR_INTERVAL_NO_TIMEOUT);
   338 			if (rv < 0) {
   339 				fprintf(stderr,"Error - PR_Poll failed: (%d, %d)\n",
   340 								PR_GetError(), PR_GetOSError());
   341 				failed_already=1;
   342 				goto def_exit;
   343 			}
   344 			PR_ASSERT((rv == 1) && (pd.out_flags == PR_POLL_WRITE));
   345 			rv = PR_Write(conn_fd, send_buf + bytes_sent,
   346 										DATA_BUF_SIZE - bytes_sent);
   347 			if (rv < 0) {
   348 				fprintf(stderr,"Error - PR_Write failed: (%d, %d)\n",
   349 								PR_GetError(), PR_GetOSError());
   350 				failed_already=1;
   351 				goto def_exit;
   352 			}
   353 			PR_ASSERT(rv > 0);
   354 			bytes_sent += rv;
   355 		}
   356 		DPRINTF(("Bytes written to server - %d\n",bytes_sent));
   357 		PR_Shutdown(conn_fd, PR_SHUTDOWN_SEND);
   358 		pd.in_flags = PR_POLL_READ;
   359 		bytes_read = 0;
   360 		memset(recv_buf, 0, DATA_BUF_SIZE);
   361 		while (bytes_read != DATA_BUF_SIZE) {
   362 			rv = PR_Poll(&pd, 1, PR_INTERVAL_NO_TIMEOUT);
   363 			if (rv < 0) {
   364 				fprintf(stderr,"Error - PR_Poll failed: (%d, %d)\n",
   365 								PR_GetError(), PR_GetOSError());
   366 				failed_already=1;
   367 				goto def_exit;
   368 			}
   369 			PR_ASSERT((rv == 1) && (pd.out_flags == PR_POLL_READ));
   370 			rv = PR_Read(conn_fd, recv_buf + bytes_read ,
   371 										DATA_BUF_SIZE - bytes_read);
   372 			if (rv < 0) {
   373 				fprintf(stderr,"Error - PR_Read failed: (%d, %d)\n",
   374 								PR_GetError(), PR_GetOSError());
   375 				failed_already=1;
   376 				goto def_exit;
   377 			}
   378 			PR_ASSERT(rv != 0);
   379 			bytes_read += rv;
   380 		}
   381 		DPRINTF(("Bytes read from server - %d\n",bytes_read));
   382 		/*
   383 		 * verify the data read
   384 		 */
   385 		if (memcmp(send_buf, recv_buf, DATA_BUF_SIZE) != 0) {
   386 			fprintf(stderr,"ERROR - data corruption\n");
   387 			failed_already=1;
   388 			goto def_exit;
   389 		}
   390 		DPRINTF(("Data integrity verified\n"));
   391 	} else {
   392 		fprintf(stderr,"PR_GetConnectStatus: connect failed: (%ld, %ld)\n",
   393 				PR_GetError(), PR_GetOSError());
   394 		failed_already = 1;
   395 		goto def_exit;
   396 	}
   397 def_exit:
   398 	if (thr) {
   399 		PR_JoinThread(thr);
   400 		thr = NULL;
   401 	}
   402 	if (sockfd) {
   403 		PR_Close(sockfd);
   404 		sockfd = NULL;
   405 	}
   406 	if (conn_fd) {
   407 		PR_Close(conn_fd);
   408 		conn_fd = NULL;
   409 	}
   410 	if (failed_already)
   411 		return 1;
   412 	else
   413 		return 0;
   415 }
   417 /*
   418  * test for connection to a nonexistent port using a non-blocking socket
   419  */
   420 static PRIntn
   421 connection_failure_test()
   422 {
   423 	PRFileDesc *sockfd = NULL, *conn_fd = NULL;
   424 	PRNetAddr netaddr;
   425 	PRInt32 i, rv;
   426     PRPollDesc pd;
   427     PRSocketOptionData optData;
   428     PRIntn n, failed_already = 0;
   430 	/*
   431 	 * Create a tcp socket
   432 	 */
   433 	if ((sockfd = PR_NewTCPSocket()) == NULL) {
   434 		fprintf(stderr,"Error - PR_NewTCPSocket failed\n");
   435 		failed_already=1;
   436 		goto def_exit;
   437 	}
   438 	memset(&netaddr, 0 , sizeof(netaddr));
   439 	netaddr.inet.family = PR_AF_INET;
   440 	netaddr.inet.port = PR_htons(TCP_SERVER_PORT);
   441 	netaddr.inet.ip = PR_htonl(PR_INADDR_ANY);
   442 	/*
   443 	 * try a few times to bind server's address, if addresses are in
   444 	 * use
   445 	 */
   446 	i = 0;
   447 	while (PR_Bind(sockfd, &netaddr) < 0) {
   448 		if (PR_GetError() == PR_ADDRESS_IN_USE_ERROR) {
   449 			netaddr.inet.port += 2;
   450 			if (i++ < SERVER_MAX_BIND_COUNT)
   451 				continue;
   452 		}
   453 		fprintf(stderr,"ERROR - PR_Bind failed: (%d,%d)\n",
   454 									PR_GetError(), PR_GetOSError());
   455 		failed_already=1;
   456 		goto def_exit;
   457 	}
   459 	if (PR_GetSockName(sockfd, &netaddr) < 0) {
   460 		fprintf(stderr,"ERROR - PR_GetSockName failed: (%d,%d)\n",
   461 									PR_GetError(), PR_GetOSError());
   462 		failed_already=1;
   463 		goto def_exit;
   464 	}
   465 #ifdef AIX
   466 	/*
   467 	 * On AIX, set to unused/reserved port
   468 	 */
   469 	netaddr.inet.port = PR_htons(TCP_UNUSED_PORT);
   470 #endif
   471 	if ((conn_fd = PR_NewTCPSocket()) == NULL) {
   472 		fprintf(stderr,"Error - PR_NewTCPSocket failed\n");
   473 		failed_already=1;
   474 		goto def_exit;
   475 	}
   476 	optData.option = PR_SockOpt_Nonblocking;
   477 	optData.value.non_blocking = PR_TRUE;
   478 	PR_SetSocketOption(conn_fd, &optData);
   479 	rv = PR_Connect(conn_fd, &netaddr, PR_INTERVAL_NO_TIMEOUT);
   480 	if (rv == PR_FAILURE) {
   481 		DPRINTF(("PR_Connect to a non-listen port failed: (%d, %d)\n",
   482 									PR_GetError(), PR_GetOSError()));
   483 	} else {
   484 		PR_ASSERT(rv == PR_SUCCESS);
   485 		fprintf(stderr,"Error - PR_Connect succeeded, expected to fail\n");
   486 		failed_already=1;
   487 		goto def_exit;
   488 	}
   489 	pd.fd = conn_fd;
   490 	pd.in_flags = PR_POLL_WRITE | PR_POLL_EXCEPT;
   491 	n = PR_Poll(&pd, 1, PR_INTERVAL_NO_TIMEOUT);
   492 	if (n == -1) {
   493 		fprintf(stderr,"Error - PR_Poll failed: (%d, %d)\n",
   494 									PR_GetError(), PR_GetOSError());
   495 		failed_already=1;
   496 		goto def_exit;
   497 	}
   498 	if (PR_GetConnectStatus(&pd) == PR_SUCCESS) {
   499 		PRInt32 rv;
   500 		fprintf(stderr,"PR_GetConnectStatus succeeded, expected to fail\n");
   501 		failed_already = 1;
   502 		goto def_exit;
   503 	}
   504 	rv = PR_GetError();
   505 	DPRINTF(("Connection failed, successfully with PR_Error %d\n",rv));
   506 def_exit:
   507 	if (sockfd) {
   508 		PR_Close(sockfd);
   509 		sockfd = NULL;
   510 	}
   511 	if (conn_fd) {
   512 		PR_Close(conn_fd);
   513 		conn_fd = NULL;
   514 	}
   515 	if (failed_already)
   516 		return 1;
   517 	else
   518 		return 0;
   520 }

mercurial