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) if (_debug_on) printf arg
44 
45 static PRIntn connection_success_test();
46 static PRIntn connection_failure_test();
47 
main(int argc,char ** argv)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;
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     {
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);
82 
83     PR_STDIO_INIT();
84     if (hostname)
85 		default_case = 0;
86 	else
87 		default_case = 1;
88 
89 	if (default_case) {
90 
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;
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",
167 					PR_GetError(), PR_GetOSError());
168 		}
169 		PR_Close(sock);
170     	printf( "PASS\n");
171     	return 0;
172 
173 	}
174 }
175 
176 
177 /*
178  * TCP Server
179  *    Server Thread
180  *    Accept a connection from the client and write some data
181  */
182 static void PR_CALLBACK
TCP_Server(void * arg)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;
189 
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 }
222 
223 
224 /*
225  * test for successful connection using a non-blocking socket
226  */
227 static PRIntn
connection_success_test()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;
240 
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 	}
269 
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 	}
276 
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;
326 
327 		DPRINTF(("Connection successful\n"));
328 
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;
414 
415 }
416 
417 /*
418  * test for connection to a nonexistent port using a non-blocking socket
419  */
420 static PRIntn
connection_failure_test()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;
429 
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 	}
458 
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;
519 
520 }
521