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  * This test is the same as acceptread.c except that it uses the
8  * emulated acceptread method instead of the regular acceptread.
9  */
10 
11 #include <prio.h>
12 #include <prprf.h>
13 #include <prinit.h>
14 #include <prnetdb.h>
15 #include <prinrval.h>
16 #include <prthread.h>
17 #include <pprio.h>
18 
19 #include <plerror.h>
20 
21 #include <stdlib.h>
22 
23 #ifdef DEBUG
24 #define PORT_INC_DO +100
25 #else
26 #define PORT_INC_DO
27 #endif
28 #ifdef IS_64
29 #define PORT_INC_3264 +200
30 #else
31 #define PORT_INC_3264
32 #endif
33 
34 #define DEFAULT_PORT 12273 PORT_INC_DO PORT_INC_3264
35 #define GET "GET / HTTP/1.0\n\n"
36 static PRFileDesc *std_out, *err_out;
37 static PRIntervalTime write_dally, accept_timeout;
38 static PRDescIdentity emu_layer_ident;
39 static PRIOMethods emu_layer_methods;
40 
41 /* the acceptread method in emu_layer_methods */
emu_AcceptRead(PRFileDesc * sd,PRFileDesc ** nd,PRNetAddr ** raddr,void * buf,PRInt32 amount,PRIntervalTime timeout)42 static PRInt32 PR_CALLBACK emu_AcceptRead(PRFileDesc *sd, PRFileDesc **nd,
43         PRNetAddr **raddr, void *buf, PRInt32 amount, PRIntervalTime timeout)
44 {
45     return PR_EmulateAcceptRead(sd, nd, raddr, buf, amount, timeout);
46 }
47 
PrintAddress(const PRNetAddr * address)48 static PRStatus PrintAddress(const PRNetAddr* address)
49 {
50     char buffer[100];
51     PRStatus rv = PR_NetAddrToString(address, buffer, sizeof(buffer));
52     if (PR_FAILURE == rv) {
53         PL_FPrintError(err_out, "PR_NetAddrToString");
54     }
55     else PR_fprintf(
56             std_out, "Accepted connection from (0x%p)%s:%d\n",
57             address, buffer, address->inet.port);
58     return rv;
59 }  /* PrintAddress */
60 
ConnectingThread(void * arg)61 static void ConnectingThread(void *arg)
62 {
63     PRInt32 nbytes;
64     char buf[1024];
65     PRFileDesc *sock;
66     PRNetAddr peer_addr, *addr;
67 
68     addr = (PRNetAddr*)arg;
69 
70     sock = PR_NewTCPSocket();
71     if (sock == NULL)
72     {
73         PL_FPrintError(err_out, "PR_NewTCPSocket (client) failed");
74         PR_ProcessExit(1);
75     }
76 
77     if (PR_Connect(sock, addr, PR_INTERVAL_NO_TIMEOUT) == PR_FAILURE)
78     {
79         PL_FPrintError(err_out, "PR_Connect (client) failed");
80         PR_ProcessExit(1);
81     }
82     if (PR_GetPeerName(sock, &peer_addr) == PR_FAILURE)
83     {
84         PL_FPrintError(err_out, "PR_GetPeerName (client) failed");
85         PR_ProcessExit(1);
86     }
87 
88     /*
89     ** Then wait between the connection coming up and sending the expected
90     ** data. At some point in time, the server should fail due to a timeou
91     ** on the AcceptRead() operation, which according to the document is
92     ** only due to the read() portion.
93     */
94     PR_Sleep(write_dally);
95 
96     nbytes = PR_Send(sock, GET, sizeof(GET), 0, PR_INTERVAL_NO_TIMEOUT);
97     if (nbytes == -1) {
98         PL_FPrintError(err_out, "PR_Send (client) failed");
99     }
100 
101     nbytes = PR_Recv(sock, buf, sizeof(buf), 0, PR_INTERVAL_NO_TIMEOUT);
102     if (nbytes == -1) {
103         PL_FPrintError(err_out, "PR_Recv (client) failed");
104     }
105     else
106     {
107         PR_fprintf(std_out, "PR_Recv (client) succeeded: %d bytes\n", nbytes);
108         buf[sizeof(buf) - 1] = '\0';
109         PR_fprintf(std_out, "%s\n", buf);
110     }
111 
112     if (PR_FAILURE == PR_Shutdown(sock, PR_SHUTDOWN_BOTH)) {
113         PL_FPrintError(err_out, "PR_Shutdown (client) failed");
114     }
115 
116     if (PR_FAILURE == PR_Close(sock)) {
117         PL_FPrintError(err_out, "PR_Close (client) failed");
118     }
119 
120     return;
121 }  /* ConnectingThread */
122 
123 #define BUF_SIZE 117
AcceptingThread(void * arg)124 static void AcceptingThread(void *arg)
125 {
126     PRStatus rv;
127     PRInt32 bytes;
128     PRSize buf_size = BUF_SIZE;
129     PRUint8 buf[BUF_SIZE + (2 * sizeof(PRNetAddr)) + 32];
130     PRNetAddr *accept_addr, *listen_addr = (PRNetAddr*)arg;
131     PRFileDesc *accept_sock, *listen_sock = PR_NewTCPSocket();
132     PRFileDesc *layer;
133     PRSocketOptionData sock_opt;
134 
135     if (NULL == listen_sock)
136     {
137         PL_FPrintError(err_out, "PR_NewTCPSocket (server) failed");
138         PR_ProcessExit(1);
139     }
140     layer = PR_CreateIOLayerStub(emu_layer_ident, &emu_layer_methods);
141     if (NULL == layer)
142     {
143         PL_FPrintError(err_out, "PR_CreateIOLayerStub (server) failed");
144         PR_ProcessExit(1);
145     }
146     if (PR_PushIOLayer(listen_sock, PR_TOP_IO_LAYER, layer) == PR_FAILURE)
147     {
148         PL_FPrintError(err_out, "PR_PushIOLayer (server) failed");
149         PR_ProcessExit(1);
150     }
151     sock_opt.option = PR_SockOpt_Reuseaddr;
152     sock_opt.value.reuse_addr = PR_TRUE;
153     rv = PR_SetSocketOption(listen_sock, &sock_opt);
154     if (PR_FAILURE == rv)
155     {
156         PL_FPrintError(err_out, "PR_SetSocketOption (server) failed");
157         PR_ProcessExit(1);
158     }
159     rv = PR_Bind(listen_sock, listen_addr);
160     if (PR_FAILURE == rv)
161     {
162         PL_FPrintError(err_out, "PR_Bind (server) failed");
163         PR_ProcessExit(1);
164     }
165     rv = PR_Listen(listen_sock, 10);
166     if (PR_FAILURE == rv)
167     {
168         PL_FPrintError(err_out, "PR_Listen (server) failed");
169         PR_ProcessExit(1);
170     }
171     bytes = PR_AcceptRead(
172                 listen_sock, &accept_sock, &accept_addr, buf, buf_size, accept_timeout);
173 
174     if (-1 == bytes) {
175         PL_FPrintError(err_out, "PR_AcceptRead (server) failed");
176     }
177     else
178     {
179         PrintAddress(accept_addr);
180         PR_fprintf(
181             std_out, "(Server) read [0x%p..0x%p) %s\n",
182             buf, &buf[BUF_SIZE], buf);
183         bytes = PR_Write(accept_sock, buf, bytes);
184         rv = PR_Shutdown(accept_sock, PR_SHUTDOWN_BOTH);
185         if (PR_FAILURE == rv) {
186             PL_FPrintError(err_out, "PR_Shutdown (server) failed");
187         }
188     }
189 
190     if (-1 != bytes)
191     {
192         rv = PR_Close(accept_sock);
193         if (PR_FAILURE == rv) {
194             PL_FPrintError(err_out, "PR_Close (server) failed");
195         }
196     }
197 
198     rv = PR_Close(listen_sock);
199     if (PR_FAILURE == rv) {
200         PL_FPrintError(err_out, "PR_Close (server) failed");
201     }
202 }  /* AcceptingThread */
203 
main(int argc,char ** argv)204 int main(int argc, char **argv)
205 {
206     PRHostEnt he;
207     PRStatus status;
208     PRIntn next_index;
209     PRUint16 port_number;
210     char netdb_buf[PR_NETDB_BUF_SIZE];
211     PRNetAddr client_addr, server_addr;
212     PRThread *client_thread, *server_thread;
213     PRIntervalTime delta = PR_MillisecondsToInterval(500);
214 
215     err_out = PR_STDERR;
216     std_out = PR_STDOUT;
217     accept_timeout = PR_SecondsToInterval(2);
218     emu_layer_ident = PR_GetUniqueIdentity("Emulated AcceptRead");
219     emu_layer_methods = *PR_GetDefaultIOMethods();
220     emu_layer_methods.acceptread = emu_AcceptRead;
221 
222     if (argc != 2 && argc != 3) {
223         port_number = DEFAULT_PORT;
224     }
225     else {
226         port_number = (PRUint16)atoi(argv[(argc == 2) ? 1 : 2]);
227     }
228 
229     status = PR_InitializeNetAddr(PR_IpAddrAny, port_number, &server_addr);
230     if (PR_SUCCESS != status)
231     {
232         PL_FPrintError(err_out, "PR_InitializeNetAddr failed");
233         PR_ProcessExit(1);
234     }
235     if (argc < 3)
236     {
237         status = PR_InitializeNetAddr(
238                      PR_IpAddrLoopback, port_number, &client_addr);
239         if (PR_SUCCESS != status)
240         {
241             PL_FPrintError(err_out, "PR_InitializeNetAddr failed");
242             PR_ProcessExit(1);
243         }
244     }
245     else
246     {
247         status = PR_GetHostByName(
248                      argv[1], netdb_buf, sizeof(netdb_buf), &he);
249         if (status == PR_FAILURE)
250         {
251             PL_FPrintError(err_out, "PR_GetHostByName failed");
252             PR_ProcessExit(1);
253         }
254         next_index = PR_EnumerateHostEnt(0, &he, port_number, &client_addr);
255         if (next_index == -1)
256         {
257             PL_FPrintError(err_out, "PR_EnumerateHostEnt failed");
258             PR_ProcessExit(1);
259         }
260     }
261 
262     for (
263         write_dally = 0;
264         write_dally < accept_timeout + (2 * delta);
265         write_dally += delta)
266     {
267         PR_fprintf(
268             std_out, "Testing w/ write_dally = %d msec\n",
269             PR_IntervalToMilliseconds(write_dally));
270         server_thread = PR_CreateThread(
271                             PR_USER_THREAD, AcceptingThread, &server_addr,
272                             PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD, PR_JOINABLE_THREAD, 0);
273         if (server_thread == NULL)
274         {
275             PL_FPrintError(err_out, "PR_CreateThread (server) failed");
276             PR_ProcessExit(1);
277         }
278 
279         PR_Sleep(delta);  /* let the server pot thicken */
280 
281         client_thread = PR_CreateThread(
282                             PR_USER_THREAD, ConnectingThread, &client_addr,
283                             PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD, PR_JOINABLE_THREAD, 0);
284         if (client_thread == NULL)
285         {
286             PL_FPrintError(err_out, "PR_CreateThread (client) failed");
287             PR_ProcessExit(1);
288         }
289 
290         if (PR_JoinThread(client_thread) == PR_FAILURE) {
291             PL_FPrintError(err_out, "PR_JoinThread (client) failed");
292         }
293 
294         if (PR_JoinThread(server_thread) == PR_FAILURE) {
295             PL_FPrintError(err_out, "PR_JoinThread (server) failed");
296         }
297     }
298 
299     return 0;
300 }
301