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 #include "prio.h"
7 #include "prmem.h"
8 #include "prprf.h"
9 #include "prlog.h"
10 #include "prerror.h"
11 #include "prnetdb.h"
12 #include "prthread.h"
13 
14 #include "plerror.h"
15 #include "plgetopt.h"
16 #include "prwin16.h"
17 
18 #include <stdlib.h>
19 #include <string.h>
20 
21 /*
22 ** Testing layering of I/O
23 **
24 **      The layered server
25 ** A thread that acts as a server. It creates a TCP listener with a dummy
26 ** layer pushed on top. Then listens for incoming connections. Each connection
27 ** request for connection will be layered as well, accept one request, echo
28 ** it back and close.
29 **
30 **      The layered client
31 ** Pretty much what you'd expect.
32 */
33 
34 static PRFileDesc *logFile;
35 static PRDescIdentity identity;
36 static PRNetAddr server_address;
37 
38 static PRIOMethods myMethods;
39 
40 typedef enum {rcv_get_debit, rcv_send_credit, rcv_data} RcvState;
41 typedef enum {xmt_send_debit, xmt_recv_credit, xmt_data} XmtState;
42 
43 struct PRFilePrivate
44 {
45     RcvState rcvstate;
46     XmtState xmtstate;
47     PRInt32 rcvreq, rcvinprogress;
48     PRInt32 xmtreq, xmtinprogress;
49 };
50 
51 typedef enum Verbosity {silent, quiet, chatty, noisy} Verbosity;
52 
53 static PRIntn minor_iterations = 5;
54 static PRIntn major_iterations = 1;
55 static Verbosity verbosity = quiet;
56 
57 #ifdef DEBUG
58 #define PORT_INC_DO +100
59 #else
60 #define PORT_INC_DO
61 #endif
62 #ifdef IS_64
63 #define PORT_INC_3264 +200
64 #else
65 #define PORT_INC_3264
66 #endif
67 
68 static PRUint16 default_port = 12273 PORT_INC_DO PORT_INC_3264;
69 
PushLayer(PRFileDesc * stack)70 static PRFileDesc *PushLayer(PRFileDesc *stack)
71 {
72     PRStatus rv;
73     PRFileDesc *layer = PR_CreateIOLayerStub(identity, &myMethods);
74     layer->secret = PR_NEWZAP(PRFilePrivate);
75     rv = PR_PushIOLayer(stack, PR_GetLayersIdentity(stack), layer);
76     PR_ASSERT(PR_SUCCESS == rv);
77     if (verbosity > quiet) {
78         PR_fprintf(logFile, "Pushed layer(0x%x) onto stack(0x%x)\n", layer, stack);
79     }
80     return stack;
81 }  /* PushLayer */
82 
PopLayer(PRFileDesc * stack)83 static PRFileDesc *PopLayer(PRFileDesc *stack)
84 {
85     PRFileDesc *popped = PR_PopIOLayer(stack, identity);
86     if (verbosity > quiet) {
87         PR_fprintf(logFile, "Popped layer(0x%x) from stack(0x%x)\n", popped, stack);
88     }
89     PR_DELETE(popped->secret);
90     popped->dtor(popped);
91     return stack;
92 }  /* PopLayer */
93 
Client(void * arg)94 static void PR_CALLBACK Client(void *arg)
95 {
96     PRStatus rv;
97     PRIntn mits;
98     PRInt32 ready;
99     PRUint8 buffer[100];
100     PRPollDesc polldesc;
101     PRIntn empty_flags = 0;
102     PRIntn bytes_read, bytes_sent;
103     PRFileDesc *stack = (PRFileDesc*)arg;
104 
105     /* Initialize the buffer so that Purify won't complain */
106     memset(buffer, 0, sizeof(buffer));
107 
108     rv = PR_Connect(stack, &server_address, PR_INTERVAL_NO_TIMEOUT);
109     if ((PR_FAILURE == rv) && (PR_IN_PROGRESS_ERROR == PR_GetError()))
110     {
111         if (verbosity > quiet) {
112             PR_fprintf(logFile, "Client connect 'in progress'\n");
113         }
114         do
115         {
116             polldesc.fd = stack;
117             polldesc.out_flags = 0;
118             polldesc.in_flags = PR_POLL_WRITE | PR_POLL_EXCEPT;
119             ready = PR_Poll(&polldesc, 1, PR_INTERVAL_NO_TIMEOUT);
120             if ((1 != ready)  /* if not 1, then we're dead */
121                 || (0 == (polldesc.in_flags & polldesc.out_flags)))
122             {
123                 PR_NOT_REACHED("Whoa!");
124                 break;
125             }
126             if (verbosity > quiet)
127                 PR_fprintf(
128                     logFile, "Client connect 'in progress' [0x%x]\n",
129                     polldesc.out_flags);
130             rv = PR_GetConnectStatus(&polldesc);
131             if ((PR_FAILURE == rv)
132                 && (PR_IN_PROGRESS_ERROR != PR_GetError())) {
133                 break;
134             }
135         } while (PR_FAILURE == rv);
136     }
137     PR_ASSERT(PR_SUCCESS == rv);
138     if (verbosity > chatty) {
139         PR_fprintf(logFile, "Client created connection\n");
140     }
141 
142     for (mits = 0; mits < minor_iterations; ++mits)
143     {
144         bytes_sent = 0;
145         if (verbosity > quiet) {
146             PR_fprintf(logFile, "Client sending %d bytes\n", sizeof(buffer));
147         }
148         do
149         {
150             if (verbosity > chatty)
151                 PR_fprintf(
152                     logFile, "Client sending %d bytes\n",
153                     sizeof(buffer) - bytes_sent);
154             ready = PR_Send(
155                         stack, buffer + bytes_sent, sizeof(buffer) - bytes_sent,
156                         empty_flags, PR_INTERVAL_NO_TIMEOUT);
157             if (verbosity > chatty) {
158                 PR_fprintf(logFile, "Client send status [%d]\n", ready);
159             }
160             if (0 < ready) {
161                 bytes_sent += ready;
162             }
163             else if ((-1 == ready) && (PR_WOULD_BLOCK_ERROR == PR_GetError()))
164             {
165                 polldesc.fd = stack;
166                 polldesc.out_flags = 0;
167                 polldesc.in_flags = PR_POLL_WRITE;
168                 ready = PR_Poll(&polldesc, 1, PR_INTERVAL_NO_TIMEOUT);
169                 if ((1 != ready)  /* if not 1, then we're dead */
170                     || (0 == (polldesc.in_flags & polldesc.out_flags)))
171                 {
172                     PR_NOT_REACHED("Whoa!");
173                     break;
174                 }
175             }
176             else {
177                 break;
178             }
179         } while (bytes_sent < sizeof(buffer));
180         PR_ASSERT(sizeof(buffer) == bytes_sent);
181 
182         bytes_read = 0;
183         do
184         {
185             if (verbosity > chatty)
186                 PR_fprintf(
187                     logFile, "Client receiving %d bytes\n",
188                     bytes_sent - bytes_read);
189             ready = PR_Recv(
190                         stack, buffer + bytes_read, bytes_sent - bytes_read,
191                         empty_flags, PR_INTERVAL_NO_TIMEOUT);
192             if (verbosity > chatty)
193                 PR_fprintf(
194                     logFile, "Client receive status [%d]\n", ready);
195             if (0 < ready) {
196                 bytes_read += ready;
197             }
198             else if ((-1 == ready) && (PR_WOULD_BLOCK_ERROR == PR_GetError()))
199             {
200                 polldesc.fd = stack;
201                 polldesc.out_flags = 0;
202                 polldesc.in_flags = PR_POLL_READ;
203                 ready = PR_Poll(&polldesc, 1, PR_INTERVAL_NO_TIMEOUT);
204                 if ((1 != ready)  /* if not 1, then we're dead */
205                     || (0 == (polldesc.in_flags & polldesc.out_flags)))
206                 {
207                     PR_NOT_REACHED("Whoa!");
208                     break;
209                 }
210             }
211             else {
212                 break;
213             }
214         } while (bytes_read < bytes_sent);
215         if (verbosity > chatty) {
216             PR_fprintf(logFile, "Client received %d bytes\n", bytes_read);
217         }
218         PR_ASSERT(bytes_read == bytes_sent);
219     }
220 
221     if (verbosity > quiet) {
222         PR_fprintf(logFile, "Client shutting down stack\n");
223     }
224 
225     rv = PR_Shutdown(stack, PR_SHUTDOWN_BOTH); PR_ASSERT(PR_SUCCESS == rv);
226 }  /* Client */
227 
Server(void * arg)228 static void PR_CALLBACK Server(void *arg)
229 {
230     PRStatus rv;
231     PRInt32 ready;
232     PRUint8 buffer[100];
233     PRFileDesc *service;
234     PRUintn empty_flags = 0;
235     struct PRPollDesc polldesc;
236     PRIntn bytes_read, bytes_sent;
237     PRFileDesc *stack = (PRFileDesc*)arg;
238     PRNetAddr client_address;
239 
240     do
241     {
242         if (verbosity > chatty) {
243             PR_fprintf(logFile, "Server accepting connection\n");
244         }
245         service = PR_Accept(stack, &client_address, PR_INTERVAL_NO_TIMEOUT);
246         if (verbosity > chatty) {
247             PR_fprintf(logFile, "Server accept status [0x%p]\n", service);
248         }
249         if ((NULL == service) && (PR_WOULD_BLOCK_ERROR == PR_GetError()))
250         {
251             polldesc.fd = stack;
252             polldesc.out_flags = 0;
253             polldesc.in_flags = PR_POLL_READ | PR_POLL_EXCEPT;
254             ready = PR_Poll(&polldesc, 1, PR_INTERVAL_NO_TIMEOUT);
255             if ((1 != ready)  /* if not 1, then we're dead */
256                 || (0 == (polldesc.in_flags & polldesc.out_flags)))
257             {
258                 PR_NOT_REACHED("Whoa!");
259                 break;
260             }
261         }
262     } while (NULL == service);
263     PR_ASSERT(NULL != service);
264 
265     if (verbosity > quiet) {
266         PR_fprintf(logFile, "Server accepting connection\n");
267     }
268 
269     do
270     {
271         bytes_read = 0;
272         do
273         {
274             if (verbosity > chatty)
275                 PR_fprintf(
276                     logFile, "Server receiving %d bytes\n",
277                     sizeof(buffer) - bytes_read);
278             ready = PR_Recv(
279                         service, buffer + bytes_read, sizeof(buffer) - bytes_read,
280                         empty_flags, PR_INTERVAL_NO_TIMEOUT);
281             if (verbosity > chatty) {
282                 PR_fprintf(logFile, "Server receive status [%d]\n", ready);
283             }
284             if (0 < ready) {
285                 bytes_read += ready;
286             }
287             else if ((-1 == ready) && (PR_WOULD_BLOCK_ERROR == PR_GetError()))
288             {
289                 polldesc.fd = service;
290                 polldesc.out_flags = 0;
291                 polldesc.in_flags = PR_POLL_READ;
292                 ready = PR_Poll(&polldesc, 1, PR_INTERVAL_NO_TIMEOUT);
293                 if ((1 != ready)  /* if not 1, then we're dead */
294                     || (0 == (polldesc.in_flags & polldesc.out_flags)))
295                 {
296                     PR_NOT_REACHED("Whoa!");
297                     break;
298                 }
299             }
300             else {
301                 break;
302             }
303         } while (bytes_read < sizeof(buffer));
304 
305         if (0 != bytes_read)
306         {
307             if (verbosity > chatty) {
308                 PR_fprintf(logFile, "Server received %d bytes\n", bytes_read);
309             }
310             PR_ASSERT(bytes_read > 0);
311 
312             bytes_sent = 0;
313             do
314             {
315                 ready = PR_Send(
316                             service, buffer + bytes_sent, bytes_read - bytes_sent,
317                             empty_flags, PR_INTERVAL_NO_TIMEOUT);
318                 if (0 < ready)
319                 {
320                     bytes_sent += ready;
321                 }
322                 else if ((-1 == ready) && (PR_WOULD_BLOCK_ERROR == PR_GetError()))
323                 {
324                     polldesc.fd = service;
325                     polldesc.out_flags = 0;
326                     polldesc.in_flags = PR_POLL_WRITE;
327                     ready = PR_Poll(&polldesc, 1, PR_INTERVAL_NO_TIMEOUT);
328                     if ((1 != ready)  /* if not 1, then we're dead */
329                         || (0 == (polldesc.in_flags & polldesc.out_flags)))
330                     {
331                         PR_NOT_REACHED("Whoa!");
332                         break;
333                     }
334                 }
335                 else {
336                     break;
337                 }
338             } while (bytes_sent < bytes_read);
339             PR_ASSERT(bytes_read == bytes_sent);
340             if (verbosity > chatty) {
341                 PR_fprintf(logFile, "Server sent %d bytes\n", bytes_sent);
342             }
343         }
344     } while (0 != bytes_read);
345 
346     if (verbosity > quiet) {
347         PR_fprintf(logFile, "Server shutting down stack\n");
348     }
349     rv = PR_Shutdown(service, PR_SHUTDOWN_BOTH); PR_ASSERT(PR_SUCCESS == rv);
350     rv = PR_Close(service); PR_ASSERT(PR_SUCCESS == rv);
351 
352 }  /* Server */
353 
MyClose(PRFileDesc * fd)354 static PRStatus PR_CALLBACK MyClose(PRFileDesc *fd)
355 {
356     PR_DELETE(fd->secret);  /* manage my secret file object */
357     return (PR_GetDefaultIOMethods())->close(fd);  /* let him do all the work */
358 }  /* MyClose */
359 
MyPoll(PRFileDesc * fd,PRInt16 in_flags,PRInt16 * out_flags)360 static PRInt16 PR_CALLBACK MyPoll(
361     PRFileDesc *fd, PRInt16 in_flags, PRInt16 *out_flags)
362 {
363     PRInt16 my_flags = -1, new_flags;
364     PRFilePrivate *mine = (PRFilePrivate*)fd->secret;
365     if (0 != (PR_POLL_READ & in_flags))
366     {
367         /* client thinks he's reading */
368         switch (mine->rcvstate)
369         {
370             case rcv_send_credit:
371                 my_flags = (in_flags & ~PR_POLL_READ) | PR_POLL_WRITE;
372                 break;
373             case rcv_data:
374             case rcv_get_debit:
375                 my_flags = in_flags;
376             default: break;
377         }
378     }
379     else if (0 != (PR_POLL_WRITE & in_flags))
380     {
381         /* client thinks he's writing */
382         switch (mine->xmtstate)
383         {
384             case xmt_recv_credit:
385                 my_flags = (in_flags & ~PR_POLL_WRITE) | PR_POLL_READ;
386                 break;
387             case xmt_send_debit:
388             case xmt_data:
389                 my_flags = in_flags;
390             default: break;
391         }
392     }
393     else {
394         PR_NOT_REACHED("How'd I get here?");
395     }
396     new_flags = (fd->lower->methods->poll)(fd->lower, my_flags, out_flags);
397     if (verbosity > chatty)
398         PR_fprintf(
399             logFile, "Poll [i: 0x%x, m: 0x%x, o: 0x%x, n: 0x%x]\n",
400             in_flags, my_flags, *out_flags, new_flags);
401     return new_flags;
402 }  /* MyPoll */
403 
MyAccept(PRFileDesc * fd,PRNetAddr * addr,PRIntervalTime timeout)404 static PRFileDesc * PR_CALLBACK MyAccept(
405     PRFileDesc *fd, PRNetAddr *addr, PRIntervalTime timeout)
406 {
407     PRStatus rv;
408     PRFileDesc *newfd;
409     PRFileDesc *newstack;
410     PRFilePrivate *newsecret;
411 
412     PR_ASSERT(fd != NULL);
413     PR_ASSERT(fd->lower != NULL);
414 
415     newstack = PR_NEW(PRFileDesc);
416     if (NULL == newstack)
417     {
418         PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
419         return NULL;
420     }
421     newsecret = PR_NEW(PRFilePrivate);
422     if (NULL == newsecret)
423     {
424         PR_DELETE(newstack);
425         PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
426         return NULL;
427     }
428     *newstack = *fd;  /* make a copy of the accepting layer */
429     *newsecret = *fd->secret;
430     newstack->secret = newsecret;
431 
432     newfd = (fd->lower->methods->accept)(fd->lower, addr, timeout);
433     if (NULL == newfd)
434     {
435         PR_DELETE(newsecret);
436         PR_DELETE(newstack);
437         return NULL;
438     }
439 
440     /* this PR_PushIOLayer call cannot fail */
441     rv = PR_PushIOLayer(newfd, PR_TOP_IO_LAYER, newstack);
442     PR_ASSERT(PR_SUCCESS == rv);
443     return newfd;  /* that's it */
444 }
445 
MyRecv(PRFileDesc * fd,void * buf,PRInt32 amount,PRIntn flags,PRIntervalTime timeout)446 static PRInt32 PR_CALLBACK MyRecv(
447     PRFileDesc *fd, void *buf, PRInt32 amount,
448     PRIntn flags, PRIntervalTime timeout)
449 {
450     char *b;
451     PRInt32 rv;
452     PRFileDesc *lo = fd->lower;
453     PRFilePrivate *mine = (PRFilePrivate*)fd->secret;
454 
455     do
456     {
457         switch (mine->rcvstate)
458         {
459             case rcv_get_debit:
460                 b = (char*)&mine->rcvreq;
461                 mine->rcvreq = amount;
462                 rv = lo->methods->recv(
463                          lo, b + mine->rcvinprogress,
464                          sizeof(mine->rcvreq) - mine->rcvinprogress, flags, timeout);
465                 if (0 == rv) {
466                     goto closed;
467                 }
468                 if ((-1 == rv) && (PR_WOULD_BLOCK_ERROR == PR_GetError())) {
469                     break;
470                 }
471                 mine->rcvinprogress += rv;  /* accumulate the read */
472                 if (mine->rcvinprogress < sizeof(mine->rcvreq)) {
473                     break;    /* loop */
474                 }
475                 mine->rcvstate = rcv_send_credit;
476                 mine->rcvinprogress = 0;
477             case rcv_send_credit:
478                 b = (char*)&mine->rcvreq;
479                 rv = lo->methods->send(
480                          lo, b + mine->rcvinprogress,
481                          sizeof(mine->rcvreq) - mine->rcvinprogress, flags, timeout);
482                 if ((-1 == rv) && (PR_WOULD_BLOCK_ERROR == PR_GetError())) {
483                     break;
484                 }
485                 mine->rcvinprogress += rv;  /* accumulate the read */
486                 if (mine->rcvinprogress < sizeof(mine->rcvreq)) {
487                     break;    /* loop */
488                 }
489                 mine->rcvstate = rcv_data;
490                 mine->rcvinprogress = 0;
491             case rcv_data:
492                 b = (char*)buf;
493                 rv = lo->methods->recv(
494                          lo, b + mine->rcvinprogress,
495                          mine->rcvreq - mine->rcvinprogress, flags, timeout);
496                 if (0 == rv) {
497                     goto closed;
498                 }
499                 if ((-1 == rv) && (PR_WOULD_BLOCK_ERROR == PR_GetError())) {
500                     break;
501                 }
502                 mine->rcvinprogress += rv;  /* accumulate the read */
503                 if (mine->rcvinprogress < amount) {
504                     break;    /* loop */
505                 }
506                 mine->rcvstate = rcv_get_debit;
507                 mine->rcvinprogress = 0;
508                 return mine->rcvreq;  /* << -- that's it! */
509             default:
510                 PR_ASSERT(!"How did I get this mine->rcvstate?");
511                 rv = -1;
512                 break;
513         }
514     } while (-1 != rv);
515     return rv;
516 closed:
517     mine->rcvinprogress = 0;
518     mine->rcvstate = rcv_get_debit;
519     return 0;
520 }  /* MyRecv */
521 
MySend(PRFileDesc * fd,const void * buf,PRInt32 amount,PRIntn flags,PRIntervalTime timeout)522 static PRInt32 PR_CALLBACK MySend(
523     PRFileDesc *fd, const void *buf, PRInt32 amount,
524     PRIntn flags, PRIntervalTime timeout)
525 {
526     char *b;
527     PRInt32 rv;
528     PRFileDesc *lo = fd->lower;
529     PRFilePrivate *mine = (PRFilePrivate*)fd->secret;
530 
531     do
532     {
533         switch (mine->xmtstate)
534         {
535             case xmt_send_debit:
536                 b = (char*)&mine->xmtreq;
537                 mine->xmtreq = amount;
538                 rv = lo->methods->send(
539                          lo, b - mine->xmtinprogress,
540                          sizeof(mine->xmtreq) - mine->xmtinprogress, flags, timeout);
541                 if ((-1 == rv) && (PR_WOULD_BLOCK_ERROR == PR_GetError())) {
542                     break;
543                 }
544                 mine->xmtinprogress += rv;
545                 if (mine->xmtinprogress < sizeof(mine->xmtreq)) {
546                     break;
547                 }
548                 mine->xmtstate = xmt_recv_credit;
549                 mine->xmtinprogress = 0;
550             case xmt_recv_credit:
551                 b = (char*)&mine->xmtreq;
552                 rv = lo->methods->recv(
553                          lo, b + mine->xmtinprogress,
554                          sizeof(mine->xmtreq) - mine->xmtinprogress, flags, timeout);
555                 if ((-1 == rv) && (PR_WOULD_BLOCK_ERROR == PR_GetError())) {
556                     break;
557                 }
558                 mine->xmtinprogress += rv;
559                 if (mine->xmtinprogress < sizeof(mine->xmtreq)) {
560                     break;
561                 }
562                 mine->xmtstate = xmt_data;
563                 mine->xmtinprogress = 0;
564             case xmt_data:
565                 b = (char*)buf;
566                 rv = lo->methods->send(
567                          lo, b + mine->xmtinprogress,
568                          mine->xmtreq - mine->xmtinprogress, flags, timeout);
569                 if ((-1 == rv) && (PR_WOULD_BLOCK_ERROR == PR_GetError())) {
570                     break;
571                 }
572                 mine->xmtinprogress += rv;
573                 if (mine->xmtinprogress < amount) {
574                     break;
575                 }
576                 mine->xmtstate = xmt_send_debit;
577                 mine->xmtinprogress = 0;
578                 return mine->xmtreq;  /* <<-- That's the one! */
579             default:
580                 PR_ASSERT(!"How did I get this mine->xmtstate?");
581                 rv = -1;
582                 break;
583         }
584     } while (-1 != rv);
585     return rv;
586 }  /* MySend */
587 
ChangeVerbosity(Verbosity verbosity,PRIntn delta)588 static Verbosity ChangeVerbosity(Verbosity verbosity, PRIntn delta)
589 {
590     PRIntn verbage = (PRIntn)verbosity + delta;
591     if (verbage < (PRIntn)silent) {
592         verbage = (PRIntn)silent;
593     }
594     else if (verbage > (PRIntn)noisy) {
595         verbage = (PRIntn)noisy;
596     }
597     return (Verbosity)verbage;
598 }  /* ChangeVerbosity */
599 
main(int argc,char ** argv)600 int main(int argc, char **argv)
601 {
602     PRStatus rv;
603     PLOptStatus os;
604     PRFileDesc *client, *service;
605     PRNetAddr any_address;
606     const char *server_name = NULL;
607     const PRIOMethods *stubMethods;
608     PRThread *client_thread, *server_thread;
609     PRThreadScope thread_scope = PR_LOCAL_THREAD;
610     PRSocketOptionData socket_noblock, socket_nodelay;
611     PLOptState *opt = PL_CreateOptState(argc, argv, "dqGC:c:p:");
612     while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
613     {
614         if (PL_OPT_BAD == os) {
615             continue;
616         }
617         switch (opt->option)
618         {
619             case 0:
620                 server_name = opt->value;
621                 break;
622             case 'd':  /* debug mode */
623                 if (verbosity < noisy) {
624                     verbosity = ChangeVerbosity(verbosity, 1);
625                 }
626                 break;
627             case 'q':  /* debug mode */
628                 if (verbosity > silent) {
629                     verbosity = ChangeVerbosity(verbosity, -1);
630                 }
631                 break;
632             case 'G':  /* use global threads */
633                 thread_scope = PR_GLOBAL_THREAD;
634                 break;
635             case 'C':  /* number of threads waiting */
636                 major_iterations = atoi(opt->value);
637                 break;
638             case 'c':  /* number of client threads */
639                 minor_iterations = atoi(opt->value);
640                 break;
641             case 'p':  /* default port */
642                 default_port = atoi(opt->value);
643                 break;
644             default:
645                 break;
646         }
647     }
648     PL_DestroyOptState(opt);
649     PR_STDIO_INIT();
650 
651     logFile = PR_GetSpecialFD(PR_StandardError);
652     identity = PR_GetUniqueIdentity("Dummy");
653     stubMethods = PR_GetDefaultIOMethods();
654 
655     /*
656     ** The protocol we're going to implement is one where in order to initiate
657     ** a send, the sender must first solicit permission. Therefore, every
658     ** send is really a send - receive - send sequence.
659     */
660     myMethods = *stubMethods;  /* first get the entire batch */
661     myMethods.accept = MyAccept;  /* then override the ones we care about */
662     myMethods.recv = MyRecv;  /* then override the ones we care about */
663     myMethods.send = MySend;  /* then override the ones we care about */
664     myMethods.close = MyClose;  /* then override the ones we care about */
665     myMethods.poll = MyPoll;  /* then override the ones we care about */
666 
667     if (NULL == server_name)
668         rv = PR_InitializeNetAddr(
669                  PR_IpAddrLoopback, default_port, &server_address);
670     else
671     {
672         rv = PR_StringToNetAddr(server_name, &server_address);
673         PR_ASSERT(PR_SUCCESS == rv);
674         rv = PR_InitializeNetAddr(
675                  PR_IpAddrNull, default_port, &server_address);
676     }
677     PR_ASSERT(PR_SUCCESS == rv);
678 
679     socket_noblock.value.non_blocking = PR_TRUE;
680     socket_noblock.option = PR_SockOpt_Nonblocking;
681     socket_nodelay.value.no_delay = PR_TRUE;
682     socket_nodelay.option = PR_SockOpt_NoDelay;
683 
684     /* one type w/o layering */
685 
686     while (major_iterations-- > 0)
687     {
688         if (verbosity > silent) {
689             PR_fprintf(logFile, "Beginning non-layered test\n");
690         }
691 
692         client = PR_NewTCPSocket(); PR_ASSERT(NULL != client);
693         service = PR_NewTCPSocket(); PR_ASSERT(NULL != service);
694 
695         rv = PR_SetSocketOption(client, &socket_noblock);
696         PR_ASSERT(PR_SUCCESS == rv);
697         rv = PR_SetSocketOption(service, &socket_noblock);
698         PR_ASSERT(PR_SUCCESS == rv);
699         rv = PR_SetSocketOption(client, &socket_nodelay);
700         PR_ASSERT(PR_SUCCESS == rv);
701         rv = PR_SetSocketOption(service, &socket_nodelay);
702         PR_ASSERT(PR_SUCCESS == rv);
703 
704         rv = PR_InitializeNetAddr(PR_IpAddrAny, default_port, &any_address);
705         PR_ASSERT(PR_SUCCESS == rv);
706         rv = PR_Bind(service, &any_address); PR_ASSERT(PR_SUCCESS == rv);
707         rv = PR_Listen(service, 10); PR_ASSERT(PR_SUCCESS == rv);
708 
709         server_thread = PR_CreateThread(
710                             PR_USER_THREAD, Server, service,
711                             PR_PRIORITY_HIGH, thread_scope,
712                             PR_JOINABLE_THREAD, 16 * 1024);
713         PR_ASSERT(NULL != server_thread);
714 
715         client_thread = PR_CreateThread(
716                             PR_USER_THREAD, Client, client,
717                             PR_PRIORITY_NORMAL, thread_scope,
718                             PR_JOINABLE_THREAD, 16 * 1024);
719         PR_ASSERT(NULL != client_thread);
720 
721         rv = PR_JoinThread(client_thread);
722         PR_ASSERT(PR_SUCCESS == rv);
723         rv = PR_JoinThread(server_thread);
724         PR_ASSERT(PR_SUCCESS == rv);
725 
726         rv = PR_Close(client); PR_ASSERT(PR_SUCCESS == rv);
727         rv = PR_Close(service); PR_ASSERT(PR_SUCCESS == rv);
728         if (verbosity > silent) {
729             PR_fprintf(logFile, "Ending non-layered test\n");
730         }
731 
732         /* with layering */
733         if (verbosity > silent) {
734             PR_fprintf(logFile, "Beginning layered test\n");
735         }
736         client = PR_NewTCPSocket(); PR_ASSERT(NULL != client);
737         service = PR_NewTCPSocket(); PR_ASSERT(NULL != service);
738 
739         rv = PR_SetSocketOption(client, &socket_noblock);
740         PR_ASSERT(PR_SUCCESS == rv);
741         rv = PR_SetSocketOption(service, &socket_noblock);
742         PR_ASSERT(PR_SUCCESS == rv);
743         rv = PR_SetSocketOption(client, &socket_nodelay);
744         PR_ASSERT(PR_SUCCESS == rv);
745         rv = PR_SetSocketOption(service, &socket_nodelay);
746         PR_ASSERT(PR_SUCCESS == rv);
747 
748         PushLayer(client);
749         PushLayer(service);
750 
751         rv = PR_InitializeNetAddr(PR_IpAddrAny, default_port, &any_address);
752         PR_ASSERT(PR_SUCCESS == rv);
753         rv = PR_Bind(service, &any_address); PR_ASSERT(PR_SUCCESS == rv);
754         rv = PR_Listen(service, 10); PR_ASSERT(PR_SUCCESS == rv);
755 
756         server_thread = PR_CreateThread(
757                             PR_USER_THREAD, Server, service,
758                             PR_PRIORITY_HIGH, thread_scope,
759                             PR_JOINABLE_THREAD, 16 * 1024);
760         PR_ASSERT(NULL != server_thread);
761 
762         client_thread = PR_CreateThread(
763                             PR_USER_THREAD, Client, client,
764                             PR_PRIORITY_NORMAL, thread_scope,
765                             PR_JOINABLE_THREAD, 16 * 1024);
766         PR_ASSERT(NULL != client_thread);
767 
768         rv = PR_JoinThread(client_thread);
769         PR_ASSERT(PR_SUCCESS == rv);
770         rv = PR_JoinThread(server_thread);
771         PR_ASSERT(PR_SUCCESS == rv);
772 
773         rv = PR_Close(PopLayer(client)); PR_ASSERT(PR_SUCCESS == rv);
774         rv = PR_Close(PopLayer(service)); PR_ASSERT(PR_SUCCESS == rv);
775         if (verbosity > silent) {
776             PR_fprintf(logFile, "Ending layered test\n");
777         }
778     }
779     return 0;
780 }  /* main */
781 
782 /* nblayer.c */
783