Bug 1939208 - Localize messages according to application context. r=android-reviewers...
[gecko.git] / security / manager / ssl / tests / unit / tlsserver / lib / TLSServer.cpp
blob58438193cd7085484afb11f21654a3ca43d398b0
1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 #include "TLSServer.h"
7 #include <stdio.h>
8 #include <string>
9 #include <thread>
10 #include <vector>
11 #include <fstream>
12 #include <iostream>
13 #ifdef XP_WIN
14 # include <windows.h>
15 #else
16 # include <unistd.h>
17 #endif
19 #include <utility>
21 #include "base64.h"
22 #include "mozilla/Sprintf.h"
23 #include "nspr.h"
24 #include "nss.h"
25 #include "plarenas.h"
26 #include "prenv.h"
27 #include "prerror.h"
28 #include "prnetdb.h"
29 #include "prtime.h"
30 #include "ssl.h"
31 #include "sslexp.h"
32 #include "sslproto.h"
34 namespace mozilla {
35 namespace test {
37 static const uint16_t LISTEN_PORT = 8443;
39 SSLAntiReplayContext* antiReplay = nullptr;
41 DebugLevel gDebugLevel = DEBUG_ERRORS;
42 uint16_t gCallbackPort = 0;
44 static const char kPEMBegin[] = "-----BEGIN ";
45 static const char kPEMEnd[] = "-----END ";
46 const char DEFAULT_CERT_NICKNAME[] = "default-ee";
48 struct Connection {
49 PRFileDesc* mSocket;
50 char mByte;
52 explicit Connection(PRFileDesc* aSocket);
53 ~Connection();
56 Connection::Connection(PRFileDesc* aSocket) : mSocket(aSocket), mByte(0) {}
58 Connection::~Connection() {
59 if (mSocket) {
60 PR_Close(mSocket);
64 void PrintPRError(const char* aPrefix) {
65 const char* err = PR_ErrorToName(PR_GetError());
66 if (err) {
67 if (gDebugLevel >= DEBUG_ERRORS) {
68 fprintf(stderr, "%s: %s\n", aPrefix, err);
70 } else {
71 if (gDebugLevel >= DEBUG_ERRORS) {
72 fprintf(stderr, "%s\n", aPrefix);
77 // This decodes a PEM file into `item`. The line endings need to be
78 // UNIX-style, or there will be cross-platform issues.
79 static bool DecodePEMFile(const std::string& filename, SECItem* item) {
80 std::ifstream in(filename);
81 if (in.bad()) {
82 return false;
85 char buf[1024];
86 in.getline(buf, sizeof(buf));
87 if (in.bad()) {
88 return false;
91 if (strncmp(buf, kPEMBegin, std::string::traits_type::length(kPEMBegin)) !=
92 0) {
93 return false;
96 std::string value;
97 for (;;) {
98 in.getline(buf, sizeof(buf));
99 if (in.bad()) {
100 return false;
103 if (strncmp(buf, kPEMEnd, std::string::traits_type::length(kPEMEnd)) == 0) {
104 break;
107 value += buf;
110 unsigned int binLength;
111 UniquePORTString bin(BitwiseCast<char*, unsigned char*>(
112 ATOB_AsciiToData(value.c_str(), &binLength)));
113 if (!bin || binLength == 0) {
114 PrintPRError("ATOB_AsciiToData failed");
115 return false;
118 if (SECITEM_AllocItem(nullptr, item, binLength) == nullptr) {
119 return false;
122 PORT_Memcpy(item->data, bin.get(), binLength);
123 return true;
126 static SECStatus AddKeyFromFile(const std::string& path,
127 const std::string& filename) {
128 ScopedAutoSECItem item;
130 std::string file = path + "/" + filename;
131 if (!DecodePEMFile(file, &item)) {
132 return SECFailure;
135 UniquePK11SlotInfo slot(PK11_GetInternalKeySlot());
136 if (!slot) {
137 PrintPRError("PK11_GetInternalKeySlot failed");
138 return SECFailure;
141 if (PK11_NeedUserInit(slot.get())) {
142 if (PK11_InitPin(slot.get(), nullptr, nullptr) != SECSuccess) {
143 PrintPRError("PK11_InitPin failed");
144 return SECFailure;
148 SECKEYPrivateKey* privateKey = nullptr;
149 SECItem nick = {siBuffer,
150 BitwiseCast<unsigned char*, const char*>(filename.data()),
151 static_cast<unsigned int>(filename.size())};
152 if (PK11_ImportDERPrivateKeyInfoAndReturnKey(
153 slot.get(), &item, &nick, nullptr, true, false, KU_ALL, &privateKey,
154 nullptr) != SECSuccess) {
155 PrintPRError("PK11_ImportDERPrivateKeyInfoAndReturnKey failed");
156 return SECFailure;
159 SECKEY_DestroyPrivateKey(privateKey);
160 return SECSuccess;
163 static SECStatus AddCertificateFromFile(const std::string& path,
164 const std::string& filename) {
165 ScopedAutoSECItem item;
167 std::string file = path + "/" + filename;
168 if (!DecodePEMFile(file, &item)) {
169 return SECFailure;
172 UniqueCERTCertificate cert(CERT_NewTempCertificate(
173 CERT_GetDefaultCertDB(), &item, nullptr, false, true));
174 if (!cert) {
175 PrintPRError("CERT_NewTempCertificate failed");
176 return SECFailure;
179 UniquePK11SlotInfo slot(PK11_GetInternalKeySlot());
180 if (!slot) {
181 PrintPRError("PK11_GetInternalKeySlot failed");
182 return SECFailure;
184 // The nickname is the filename without '.pem'.
185 std::string nickname = filename.substr(0, filename.length() - 4);
186 SECStatus rv = PK11_ImportCert(slot.get(), cert.get(), CK_INVALID_HANDLE,
187 nickname.c_str(), false);
188 if (rv != SECSuccess) {
189 PrintPRError("PK11_ImportCert failed");
190 return rv;
193 return SECSuccess;
196 SECStatus LoadCertificatesAndKeys(const char* basePath) {
197 // The NSS cert DB path could have been specified as "sql:path". Trim off
198 // the leading "sql:" if so.
199 if (strncmp(basePath, "sql:", 4) == 0) {
200 basePath = basePath + 4;
203 UniquePRDir fdDir(PR_OpenDir(basePath));
204 if (!fdDir) {
205 PrintPRError("PR_OpenDir failed");
206 return SECFailure;
208 // On the B2G ICS emulator, operations taken in AddCertificateFromFile
209 // appear to interact poorly with readdir (more specifically, something is
210 // causing readdir to never return null - it indefinitely loops through every
211 // file in the directory, which causes timeouts). Rather than waste more time
212 // chasing this down, loading certificates and keys happens in two phases:
213 // filename collection and then loading. (This is probably a good
214 // idea anyway because readdir isn't reentrant. Something could change later
215 // such that it gets called as a result of calling AddCertificateFromFile or
216 // AddKeyFromFile.)
217 std::vector<std::string> certificates;
218 std::vector<std::string> keys;
219 for (PRDirEntry* dirEntry = PR_ReadDir(fdDir.get(), PR_SKIP_BOTH); dirEntry;
220 dirEntry = PR_ReadDir(fdDir.get(), PR_SKIP_BOTH)) {
221 size_t nameLength = strlen(dirEntry->name);
222 if (nameLength > 4) {
223 if (strncmp(dirEntry->name + nameLength - 4, ".pem", 4) == 0) {
224 certificates.push_back(dirEntry->name);
225 } else if (strncmp(dirEntry->name + nameLength - 4, ".key", 4) == 0) {
226 keys.push_back(dirEntry->name);
230 SECStatus rv;
231 for (std::string& certificate : certificates) {
232 rv = AddCertificateFromFile(basePath, certificate.c_str());
233 if (rv != SECSuccess) {
234 return rv;
237 for (std::string& key : keys) {
238 rv = AddKeyFromFile(basePath, key.c_str());
239 if (rv != SECSuccess) {
240 return rv;
243 return SECSuccess;
246 SECStatus InitializeNSS(const char* nssCertDBDir) {
247 // Try initializing an existing DB.
248 if (NSS_Init(nssCertDBDir) == SECSuccess) {
249 return SECSuccess;
252 // Create a new DB if there is none...
253 SECStatus rv = NSS_Initialize(nssCertDBDir, nullptr, nullptr, nullptr, 0);
254 if (rv != SECSuccess) {
255 return rv;
258 // ...and load all certificates into it.
259 return LoadCertificatesAndKeys(nssCertDBDir);
262 nsresult SendAll(PRFileDesc* aSocket, const char* aData, size_t aDataLen) {
263 if (gDebugLevel >= DEBUG_VERBOSE) {
264 fprintf(stderr, "sending '%s'\n", aData);
267 while (aDataLen > 0) {
268 int32_t bytesSent =
269 PR_Send(aSocket, aData, aDataLen, 0, PR_INTERVAL_NO_TIMEOUT);
270 if (bytesSent == -1) {
271 PrintPRError("PR_Send failed");
272 return NS_ERROR_FAILURE;
275 aDataLen -= bytesSent;
276 aData += bytesSent;
279 return NS_OK;
282 nsresult ReplyToRequest(Connection* aConn) {
283 // For debugging purposes, SendAll can print out what it's sending.
284 // So, any strings we give to it to send need to be null-terminated.
285 char buf[2] = {aConn->mByte, 0};
286 return SendAll(aConn->mSocket, buf, 1);
289 nsresult SetupTLS(Connection* aConn, PRFileDesc* aModelSocket) {
290 PRFileDesc* sslSocket = SSL_ImportFD(aModelSocket, aConn->mSocket);
291 if (!sslSocket) {
292 PrintPRError("SSL_ImportFD failed");
293 return NS_ERROR_FAILURE;
295 aConn->mSocket = sslSocket;
297 /* anti-replay must be configured to accept 0RTT */
298 if (antiReplay) {
299 SECStatus rv = SSL_SetAntiReplayContext(sslSocket, antiReplay);
300 if (rv != SECSuccess) {
301 PrintPRError("error configuring anti-replay ");
302 return NS_ERROR_FAILURE;
306 SSL_OptionSet(sslSocket, SSL_SECURITY, true);
307 SSL_OptionSet(sslSocket, SSL_HANDSHAKE_AS_CLIENT, false);
308 SSL_OptionSet(sslSocket, SSL_HANDSHAKE_AS_SERVER, true);
309 // Unconditionally enabling 0RTT makes test_session_resumption.js fail
310 SSL_OptionSet(sslSocket, SSL_ENABLE_0RTT_DATA,
311 !!PR_GetEnv("MOZ_TLS_SERVER_0RTT"));
313 SSL_ResetHandshake(sslSocket, /* asServer */ 1);
315 return NS_OK;
318 nsresult ReadRequest(Connection* aConn) {
319 int32_t bytesRead =
320 PR_Recv(aConn->mSocket, &aConn->mByte, 1, 0, PR_INTERVAL_NO_TIMEOUT);
321 if (bytesRead < 0) {
322 PrintPRError("PR_Recv failed");
323 return NS_ERROR_FAILURE;
324 } else if (bytesRead == 0) {
325 PR_SetError(PR_IO_ERROR, 0);
326 PrintPRError("PR_Recv EOF in ReadRequest");
327 return NS_ERROR_FAILURE;
328 } else {
329 if (gDebugLevel >= DEBUG_VERBOSE) {
330 fprintf(stderr, "read '0x%hhx'\n", aConn->mByte);
333 return NS_OK;
336 void HandleConnection(PRFileDesc* aSocket,
337 const UniquePRFileDesc& aModelSocket) {
338 Connection conn(aSocket);
339 nsresult rv = SetupTLS(&conn, aModelSocket.get());
340 if (NS_FAILED(rv)) {
341 PR_SetError(PR_INVALID_STATE_ERROR, 0);
342 PrintPRError("PR_Recv failed");
343 exit(1);
346 // TODO: On tests that are expected to fail (e.g. due to a revoked
347 // certificate), the client will close the connection wtihout sending us the
348 // request byte. In those cases, we should keep going. But, in the cases
349 // where the connection is supposed to suceed, we should verify that we
350 // successfully receive the request and send the response.
351 rv = ReadRequest(&conn);
352 if (NS_SUCCEEDED(rv)) {
353 rv = ReplyToRequest(&conn);
357 // returns 0 on success, non-zero on error
358 int DoCallback() {
359 UniquePRFileDesc socket(PR_NewTCPSocket());
360 if (!socket) {
361 PrintPRError("PR_NewTCPSocket failed");
362 return 1;
365 PRNetAddr addr;
366 PR_InitializeNetAddr(PR_IpAddrLoopback, gCallbackPort, &addr);
367 if (PR_Connect(socket.get(), &addr, PR_INTERVAL_NO_TIMEOUT) != PR_SUCCESS) {
368 PrintPRError("PR_Connect failed");
369 return 1;
372 const char* request = "GET / HTTP/1.0\r\n\r\n";
373 SendAll(socket.get(), request, strlen(request));
374 char buf[4096];
375 memset(buf, 0, sizeof(buf));
376 int32_t bytesRead =
377 PR_Recv(socket.get(), buf, sizeof(buf) - 1, 0, PR_INTERVAL_NO_TIMEOUT);
378 if (bytesRead < 0) {
379 PrintPRError("PR_Recv failed 1");
380 return 1;
382 if (bytesRead == 0) {
383 fprintf(stderr, "PR_Recv eof 1\n");
384 return 1;
386 fprintf(stderr, "%s\n", buf);
387 return 0;
390 SECStatus ConfigSecureServerWithNamedCert(
391 PRFileDesc* fd, const char* certName,
392 /*optional*/ UniqueCERTCertificate* certOut,
393 /*optional*/ SSLKEAType* keaOut,
394 /*optional*/ SSLExtraServerCertData* extraData) {
395 UniqueCERTCertificate cert(PK11_FindCertFromNickname(certName, nullptr));
396 if (!cert) {
397 PrintPRError("PK11_FindCertFromNickname failed");
398 return SECFailure;
400 // If an intermediate certificate issued the server certificate (rather than
401 // directly by a trust anchor), we want to send it along in the handshake so
402 // we don't encounter unknown issuer errors when that's not what we're
403 // testing.
404 UniqueCERTCertificateList certList;
405 UniqueCERTCertificate issuerCert(
406 CERT_FindCertByName(CERT_GetDefaultCertDB(), &cert->derIssuer));
407 // If we can't find the issuer cert, continue without it.
408 if (issuerCert) {
409 // Sadly, CERTCertificateList does not have a CERT_NewCertificateList
410 // utility function, so we must create it ourselves. This consists
411 // of creating an arena, allocating space for the CERTCertificateList,
412 // and then transferring ownership of the arena to that list.
413 UniquePLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE));
414 if (!arena) {
415 PrintPRError("PORT_NewArena failed");
416 return SECFailure;
418 certList.reset(static_cast<CERTCertificateList*>(
419 PORT_ArenaAlloc(arena.get(), sizeof(CERTCertificateList))));
420 if (!certList) {
421 PrintPRError("PORT_ArenaAlloc failed");
422 return SECFailure;
424 certList->arena = arena.release();
425 // We also have to manually copy the certificates we care about to the
426 // list, because there aren't any utility functions for that either.
427 certList->certs = static_cast<SECItem*>(
428 PORT_ArenaAlloc(certList->arena, 2 * sizeof(SECItem)));
429 if (SECITEM_CopyItem(certList->arena, certList->certs, &cert->derCert) !=
430 SECSuccess) {
431 PrintPRError("SECITEM_CopyItem failed");
432 return SECFailure;
434 if (SECITEM_CopyItem(certList->arena, certList->certs + 1,
435 &issuerCert->derCert) != SECSuccess) {
436 PrintPRError("SECITEM_CopyItem failed");
437 return SECFailure;
439 certList->len = 2;
442 UniquePK11SlotInfo slot(PK11_GetInternalKeySlot());
443 if (!slot) {
444 PrintPRError("PK11_GetInternalKeySlot failed");
445 return SECFailure;
447 UniqueSECKEYPrivateKey key(
448 PK11_FindKeyByDERCert(slot.get(), cert.get(), nullptr));
449 if (!key) {
450 PrintPRError("PK11_FindKeyByDERCert failed");
451 return SECFailure;
454 if (extraData) {
455 SSLExtraServerCertData dataCopy = {ssl_auth_null, nullptr, nullptr,
456 nullptr, nullptr, nullptr};
457 memcpy(&dataCopy, extraData, sizeof(dataCopy));
458 dataCopy.certChain = certList.get();
460 if (SSL_ConfigServerCert(fd, cert.get(), key.get(), &dataCopy,
461 sizeof(dataCopy)) != SECSuccess) {
462 PrintPRError("SSL_ConfigServerCert failed");
463 return SECFailure;
466 } else {
467 // This is the deprecated setup mechanism, to be cleaned up in Bug 1569222
468 SSLKEAType certKEA = NSS_FindCertKEAType(cert.get());
469 if (SSL_ConfigSecureServerWithCertChain(fd, cert.get(), certList.get(),
470 key.get(), certKEA) != SECSuccess) {
471 PrintPRError("SSL_ConfigSecureServer failed");
472 return SECFailure;
475 if (keaOut) {
476 *keaOut = certKEA;
480 if (certOut) {
481 *certOut = std::move(cert);
484 SSL_OptionSet(fd, SSL_NO_CACHE, false);
485 SSL_OptionSet(fd, SSL_ENABLE_SESSION_TICKETS, true);
486 // Unconditionally enabling 0RTT makes test_session_resumption.js fail
487 SSL_OptionSet(fd, SSL_ENABLE_0RTT_DATA, !!PR_GetEnv("MOZ_TLS_SERVER_0RTT"));
489 return SECSuccess;
492 #ifdef XP_WIN
493 using PidType = DWORD;
494 constexpr bool IsValidPid(long long pid) {
495 // Excluding `(DWORD)-1` because it is not a valid process ID.
496 // See https://devblogs.microsoft.com/oldnewthing/20040223-00/?p=40503
497 return pid > 0 && pid < std::numeric_limits<PidType>::max();
499 #else
500 using PidType = pid_t;
501 constexpr bool IsValidPid(long long pid) {
502 return pid > 0 && pid <= std::numeric_limits<PidType>::max();
504 #endif
506 PidType ConvertPid(const char* pidStr) {
507 long long pid = strtoll(pidStr, nullptr, 10);
508 if (!IsValidPid(pid)) {
509 return 0;
511 return static_cast<PidType>(pid);
514 int StartServer(int argc, char* argv[], SSLSNISocketConfig sniSocketConfig,
515 void* sniSocketConfigArg, ServerConfigFunc configFunc) {
516 if (argc != 3) {
517 fprintf(stderr, "usage: %s <NSS DB directory> <ppid>\n", argv[0]);
518 return 1;
520 const char* nssCertDBDir = argv[1];
521 PidType ppid = ConvertPid(argv[2]);
523 const char* debugLevel = PR_GetEnv("MOZ_TLS_SERVER_DEBUG_LEVEL");
524 if (debugLevel) {
525 int level = atoi(debugLevel);
526 switch (level) {
527 case DEBUG_ERRORS:
528 gDebugLevel = DEBUG_ERRORS;
529 break;
530 case DEBUG_WARNINGS:
531 gDebugLevel = DEBUG_WARNINGS;
532 break;
533 case DEBUG_VERBOSE:
534 gDebugLevel = DEBUG_VERBOSE;
535 break;
536 default:
537 PrintPRError("invalid MOZ_TLS_SERVER_DEBUG_LEVEL");
538 return 1;
542 const char* callbackPort = PR_GetEnv("MOZ_TLS_SERVER_CALLBACK_PORT");
543 if (callbackPort) {
544 gCallbackPort = atoi(callbackPort);
547 if (InitializeNSS(nssCertDBDir) != SECSuccess) {
548 PR_fprintf(PR_STDERR, "InitializeNSS failed");
549 return 1;
552 if (NSS_SetDomesticPolicy() != SECSuccess) {
553 PrintPRError("NSS_SetDomesticPolicy failed");
554 return 1;
557 if (SSL_ConfigServerSessionIDCache(0, 0, 0, nullptr) != SECSuccess) {
558 PrintPRError("SSL_ConfigServerSessionIDCache failed");
559 return 1;
562 UniquePRFileDesc serverSocket(PR_NewTCPSocket());
563 if (!serverSocket) {
564 PrintPRError("PR_NewTCPSocket failed");
565 return 1;
568 PRSocketOptionData socketOption;
569 socketOption.option = PR_SockOpt_Reuseaddr;
570 socketOption.value.reuse_addr = true;
571 PR_SetSocketOption(serverSocket.get(), &socketOption);
573 PRNetAddr serverAddr;
574 PR_InitializeNetAddr(PR_IpAddrLoopback, LISTEN_PORT, &serverAddr);
575 if (PR_Bind(serverSocket.get(), &serverAddr) != PR_SUCCESS) {
576 PrintPRError("PR_Bind failed");
577 return 1;
580 if (PR_Listen(serverSocket.get(), 1) != PR_SUCCESS) {
581 PrintPRError("PR_Listen failed");
582 return 1;
585 UniquePRFileDesc rawModelSocket(PR_NewTCPSocket());
586 if (!rawModelSocket) {
587 PrintPRError("PR_NewTCPSocket failed for rawModelSocket");
588 return 1;
591 UniquePRFileDesc modelSocket(SSL_ImportFD(nullptr, rawModelSocket.release()));
592 if (!modelSocket) {
593 PrintPRError("SSL_ImportFD of rawModelSocket failed");
594 return 1;
597 SSLVersionRange range = {0, 0};
598 if (SSL_VersionRangeGet(modelSocket.get(), &range) != SECSuccess) {
599 PrintPRError("SSL_VersionRangeGet failed");
600 return 1;
603 if (range.max < SSL_LIBRARY_VERSION_TLS_1_3) {
604 range.max = SSL_LIBRARY_VERSION_TLS_1_3;
605 if (SSL_VersionRangeSet(modelSocket.get(), &range) != SECSuccess) {
606 PrintPRError("SSL_VersionRangeSet failed");
607 return 1;
611 if (PR_GetEnv("MOZ_TLS_SERVER_0RTT")) {
612 if (SSL_CreateAntiReplayContext(PR_Now(), 1L * PR_USEC_PER_SEC, 7, 14,
613 &antiReplay) != SECSuccess) {
614 PrintPRError("Unable to create anti-replay context for 0-RTT.");
615 return 1;
619 if (SSL_SNISocketConfigHook(modelSocket.get(), sniSocketConfig,
620 sniSocketConfigArg) != SECSuccess) {
621 PrintPRError("SSL_SNISocketConfigHook failed");
622 return 1;
625 // We have to configure the server with a certificate, but it's not one
626 // we're actually going to end up using. In the SNI callback, we pick
627 // the right certificate for the connection.
629 // Provide an empty |extra_data| to force config via SSL_ConfigServerCert.
630 // This is a temporary mechanism to work around inconsistent setting of
631 // |authType| in the deprecated API (preventing the default cert from
632 // being removed in favor of the SNI-selected cert). This may be removed
633 // after Bug 1569222 removes the deprecated mechanism.
634 SSLExtraServerCertData extra_data = {ssl_auth_null, nullptr, nullptr,
635 nullptr, nullptr, nullptr};
636 if (ConfigSecureServerWithNamedCert(modelSocket.get(), DEFAULT_CERT_NICKNAME,
637 nullptr, nullptr,
638 &extra_data) != SECSuccess) {
639 return 1;
642 // Call back to implementation-defined configuration func, if provided.
643 if (configFunc) {
644 if (((configFunc)(modelSocket.get())) != SECSuccess) {
645 PrintPRError("configFunc failed");
646 return 1;
650 if (gCallbackPort != 0) {
651 if (DoCallback()) {
652 return 1;
656 std::thread([ppid] {
657 if (!ppid) {
658 if (gDebugLevel >= DEBUG_ERRORS) {
659 fprintf(stderr, "invalid ppid\n");
661 return;
663 #ifdef XP_WIN
664 HANDLE parent = OpenProcess(SYNCHRONIZE, false, ppid);
665 if (!parent) {
666 if (gDebugLevel >= DEBUG_ERRORS) {
667 fprintf(stderr, "OpenProcess failed\n");
669 return;
671 WaitForSingleObject(parent, INFINITE);
672 CloseHandle(parent);
673 #else
674 while (getppid() == ppid) {
675 sleep(1);
677 #endif
678 if (gDebugLevel >= DEBUG_ERRORS) {
679 fprintf(stderr, "Parent process crashed\n");
681 exit(1);
682 }).detach();
684 while (true) {
685 PRNetAddr clientAddr;
686 PRFileDesc* clientSocket =
687 PR_Accept(serverSocket.get(), &clientAddr, PR_INTERVAL_NO_TIMEOUT);
688 HandleConnection(clientSocket, modelSocket);
692 } // namespace test
693 } // namespace mozilla