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 "nspr.h"
7 
8 #include "plgetopt.h"
9 
10 #include <stdlib.h>
11 #include <string.h>
12 
13 
14 #ifndef IOV_MAX
15 #define IOV_MAX 16
16 #endif
17 
18 #ifdef DEBUG
19 #define PORT_INC_DO +100
20 #else
21 #define PORT_INC_DO
22 #endif
23 #ifdef IS_64
24 #define PORT_INC_3264 +200
25 #else
26 #define PORT_INC_3264
27 #endif
28 
29 #define BASE_PORT 9867 PORT_INC_DO PORT_INC_3264
30 
Writev(int argc,char ** argv)31 int PR_CALLBACK Writev(int argc, char **argv)
32 {
33 
34     PRStatus rv;
35     PRNetAddr serverAddr;
36     PRFileDesc *clientSock, *debug = NULL;
37 
38     char *buffer = NULL;
39     PRIOVec *iov = NULL;
40     PRBool passed = PR_TRUE;
41     PRIntervalTime timein, elapsed, timeout;
42     PRIntervalTime tmo_min = 0x7fffffff, tmo_max = 0, tmo_elapsed = 0;
43     PRInt32 tmo_counted = 0, iov_index, loop, bytes, number_fragments;
44     PRInt32 message_length = 100, fragment_length = 100, messages = 100;
45     struct Descriptor {
46         PRInt32 length;
47         PRUint32 checksum;
48     } descriptor;
49 
50     /*
51      * USAGE
52      * -h       dns name of host serving the connection (default = self)
53      * -m       number of messages to send              (default = 100)
54      * -s       size of each message                    (default = 100)
55      * -f       size of each message fragment           (default = 100)
56      */
57 
58     PLOptStatus os;
59     PLOptState *opt = PL_CreateOptState(argc, argv, "dh:m:s:f:");
60 
61     PR_STDIO_INIT();
62     rv = PR_InitializeNetAddr(PR_IpAddrLoopback, BASE_PORT, &serverAddr);
63     PR_ASSERT(PR_SUCCESS == rv);
64 
65     while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
66     {
67         if (PL_OPT_BAD == os) {
68             continue;
69         }
70         switch (opt->option)
71         {
72             case 'h':  /* the remote host */
73             {
74                 PRIntn es = 0;
75                 PRHostEnt host;
76                 char buffer[1024];
77                 (void)PR_GetHostByName(opt->value, buffer, sizeof(buffer), &host);
78                 es = PR_EnumerateHostEnt(es, &host, BASE_PORT, &serverAddr);
79                 PR_ASSERT(es > 0);
80             }
81             break;
82             case 'd':  /* debug mode */
83                 debug = PR_GetSpecialFD(PR_StandardError);
84                 break;
85             case 'm':  /* number of messages to send */
86                 messages = atoi(opt->value);
87                 break;
88             case 's':  /* total size of each message */
89                 message_length = atoi(opt->value);
90                 break;
91             case 'f':  /* size of each message fragment */
92                 fragment_length = atoi(opt->value);
93                 break;
94             default:
95                 break;
96         }
97     }
98     PL_DestroyOptState(opt);
99 
100     buffer = (char*)malloc(message_length);
101 
102     number_fragments = (message_length + fragment_length - 1) / fragment_length + 1;
103     while (IOV_MAX < number_fragments)
104     {
105         fragment_length = message_length / (IOV_MAX - 2);
106         number_fragments = (message_length + fragment_length - 1) /
107                            fragment_length + 1;
108         if (NULL != debug) PR_fprintf(debug,
109                                           "Too many fragments - reset fragment length to %ld\n", fragment_length);
110     }
111     iov = (PRIOVec*)malloc(number_fragments * sizeof(PRIOVec));
112 
113     iov[0].iov_base = (char*)&descriptor;
114     iov[0].iov_len = sizeof(descriptor);
115     for (iov_index = 1; iov_index < number_fragments; ++iov_index)
116     {
117         iov[iov_index].iov_base = buffer + (iov_index - 1) * fragment_length;
118         iov[iov_index].iov_len = fragment_length;
119     }
120 
121     for (bytes = 0; bytes < message_length; ++bytes) {
122         buffer[bytes] = (char)bytes;
123     }
124 
125     timeout = PR_SecondsToInterval(1);
126 
127     for (loop = 0; loop < messages; ++loop)
128     {
129         if (NULL != debug) {
130             PR_fprintf(debug, "[%d]socket ... ", loop);
131         }
132         clientSock = PR_NewTCPSocket();
133         if (clientSock)
134         {
135             timein = PR_IntervalNow();
136             if (NULL != debug) {
137                 PR_fprintf(debug, "connecting ... ");
138             }
139             rv = PR_Connect(clientSock, &serverAddr, timeout);
140             if (PR_SUCCESS == rv)
141             {
142                 descriptor.checksum = 0;
143                 descriptor.length = (loop < (messages - 1)) ? message_length : 0;
144                 if (0 == descriptor.length) {
145                     number_fragments = 1;
146                 }
147                 else
148                     for (iov_index = 0; iov_index < descriptor.length; ++iov_index)
149                     {
150                         PRUint32 overflow = descriptor.checksum & 0x80000000;
151                         descriptor.checksum = (descriptor.checksum << 1);
152                         if (0x00000000 != overflow) {
153                             descriptor.checksum += 1;
154                         }
155                         descriptor.checksum += buffer[iov_index];
156                     }
157                 if (NULL != debug) PR_fprintf(
158                         debug, "sending %d bytes ... ", descriptor.length);
159 
160                 /* then, at the last moment ... */
161                 descriptor.length = PR_ntohl(descriptor.length);
162                 descriptor.checksum = PR_ntohl(descriptor.checksum);
163 
164                 bytes = PR_Writev(clientSock, iov, number_fragments, timeout);
165                 if (NULL != debug) {
166                     PR_fprintf(debug, "closing ... ");
167                 }
168                 rv = PR_Shutdown(clientSock, PR_SHUTDOWN_BOTH);
169                 rv = PR_Close(clientSock);
170                 if (NULL != debug) PR_fprintf(
171                         debug, "%s\n", ((PR_SUCCESS == rv) ? "good" : "bad"));
172                 elapsed = PR_IntervalNow() - timein;
173                 if (elapsed < tmo_min) {
174                     tmo_min = elapsed;
175                 }
176                 else if (elapsed > tmo_max) {
177                     tmo_max = elapsed;
178                 }
179                 tmo_elapsed += elapsed;
180                 tmo_counted += 1;
181             }
182             else
183             {
184                 if (NULL != debug) PR_fprintf(
185                         debug, "failed - retrying (%d, %d)\n",
186                         PR_GetError(), PR_GetOSError());
187                 PR_Close(clientSock);
188             }
189         }
190         else if (NULL != debug)
191         {
192             PR_fprintf(debug, "unable to create client socket\n");
193             passed = PR_FALSE;
194         }
195     }
196     if (NULL != debug) {
197         if (0 == tmo_counted) {
198             PR_fprintf(debug, "No connection made\n");
199         } else {
200             PR_fprintf(
201                 debug, "\nTimings: %d [%d] %d (microseconds)\n",
202                 PR_IntervalToMicroseconds(tmo_min),
203                 PR_IntervalToMicroseconds(tmo_elapsed / tmo_counted),
204                 PR_IntervalToMicroseconds(tmo_max));
205         }
206     }
207 
208     PR_DELETE(buffer);
209     PR_DELETE(iov);
210 
211     PR_fprintf(
212         PR_GetSpecialFD(PR_StandardError),
213         "%s\n", (passed) ? "PASSED" : "FAILED");
214     return (passed) ? 0 : 1;
215 }
216 
main(int argc,char ** argv)217 int main(int argc, char **argv)
218 {
219     return (PR_VersionCheck(PR_VERSION)) ?
220            PR_Initialize(Writev, argc, argv, 4) : -1;
221 }  /* main */
222 
223 /* writev.c */
224 
225 
226