tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

TLSServer.cpp (21544B)


      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/. */
      4 
      5 #include "TLSServer.h"
      6 
      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
     18 
     19 #include <utility>
     20 
     21 #include "base64.h"
     22 #include "certdb.h"
     23 #include "mozilla/Sprintf.h"
     24 #include "nspr.h"
     25 #include "nss.h"
     26 #include "plarenas.h"
     27 #include "prenv.h"
     28 #include "prerror.h"
     29 #include "prnetdb.h"
     30 #include "prtime.h"
     31 #include "ssl.h"
     32 #include "sslexp.h"
     33 #include "sslproto.h"
     34 
     35 namespace mozilla {
     36 namespace test {
     37 
     38 static const uint16_t LISTEN_PORT = 8443;
     39 
     40 SSLAntiReplayContext* antiReplay = nullptr;
     41 
     42 DebugLevel gDebugLevel = DEBUG_ERRORS;
     43 uint16_t gCallbackPort = 0;
     44 
     45 static const char kPEMBegin[] = "-----BEGIN ";
     46 static const char kPEMEnd[] = "-----END ";
     47 const char DEFAULT_CERT_NICKNAME[] = "default-ee";
     48 
     49 struct Connection {
     50  PRFileDesc* mSocket;
     51  char mByte;
     52 
     53  explicit Connection(PRFileDesc* aSocket);
     54  ~Connection();
     55 };
     56 
     57 Connection::Connection(PRFileDesc* aSocket) : mSocket(aSocket), mByte(0) {}
     58 
     59 Connection::~Connection() {
     60  if (mSocket) {
     61    PR_Close(mSocket);
     62  }
     63 }
     64 
     65 void PrintPRError(const char* aPrefix) {
     66  const char* err = PR_ErrorToName(PR_GetError());
     67  if (err) {
     68    if (gDebugLevel >= DEBUG_ERRORS) {
     69      fprintf(stderr, "%s: %s\n", aPrefix, err);
     70    }
     71  } else {
     72    if (gDebugLevel >= DEBUG_ERRORS) {
     73      fprintf(stderr, "%s\n", aPrefix);
     74    }
     75  }
     76 }
     77 
     78 // This decodes a PEM file into `item`. The line endings need to be
     79 // UNIX-style, or there will be cross-platform issues.
     80 static bool DecodePEMFile(const std::string& filename, SECItem* item) {
     81  std::ifstream in(filename);
     82  if (in.bad()) {
     83    return false;
     84  }
     85 
     86  char buf[1024];
     87  in.getline(buf, sizeof(buf));
     88  if (in.bad()) {
     89    return false;
     90  }
     91 
     92  if (strncmp(buf, kPEMBegin, std::string::traits_type::length(kPEMBegin)) !=
     93      0) {
     94    return false;
     95  }
     96 
     97  std::string value;
     98  for (;;) {
     99    in.getline(buf, sizeof(buf));
    100    if (in.bad()) {
    101      return false;
    102    }
    103 
    104    if (strncmp(buf, kPEMEnd, std::string::traits_type::length(kPEMEnd)) == 0) {
    105      break;
    106    }
    107 
    108    value += buf;
    109  }
    110 
    111  unsigned int binLength;
    112  UniquePORTString bin(BitwiseCast<char*, unsigned char*>(
    113      ATOB_AsciiToData(value.c_str(), &binLength)));
    114  if (!bin || binLength == 0) {
    115    PrintPRError("ATOB_AsciiToData failed");
    116    return false;
    117  }
    118 
    119  if (SECITEM_AllocItem(nullptr, item, binLength) == nullptr) {
    120    return false;
    121  }
    122 
    123  PORT_Memcpy(item->data, bin.get(), binLength);
    124  return true;
    125 }
    126 
    127 static SECStatus AddKeyFromFile(const std::string& path,
    128                                const std::string& filename) {
    129  ScopedAutoSECItem item;
    130 
    131  std::string file = path + "/" + filename;
    132  if (!DecodePEMFile(file, &item)) {
    133    return SECFailure;
    134  }
    135 
    136  UniquePK11SlotInfo slot(PK11_GetInternalKeySlot());
    137  if (!slot) {
    138    PrintPRError("PK11_GetInternalKeySlot failed");
    139    return SECFailure;
    140  }
    141 
    142  if (PK11_NeedUserInit(slot.get())) {
    143    if (PK11_InitPin(slot.get(), nullptr, nullptr) != SECSuccess) {
    144      PrintPRError("PK11_InitPin failed");
    145      return SECFailure;
    146    }
    147  }
    148 
    149  SECKEYPrivateKey* privateKey = nullptr;
    150  SECItem nick = {siBuffer,
    151                  BitwiseCast<unsigned char*, const char*>(filename.data()),
    152                  static_cast<unsigned int>(filename.size())};
    153  if (PK11_ImportDERPrivateKeyInfoAndReturnKey(
    154          slot.get(), &item, &nick, nullptr, true, false, KU_ALL, &privateKey,
    155          nullptr) != SECSuccess) {
    156    PrintPRError("PK11_ImportDERPrivateKeyInfoAndReturnKey failed");
    157    return SECFailure;
    158  }
    159 
    160  SECKEY_DestroyPrivateKey(privateKey);
    161  return SECSuccess;
    162 }
    163 
    164 static SECStatus AddCertificateFromFile(const std::string& path,
    165                                        const std::string& filename) {
    166  ScopedAutoSECItem item;
    167 
    168  std::string file = path + "/" + filename;
    169  if (!DecodePEMFile(file, &item)) {
    170    return SECFailure;
    171  }
    172 
    173  UniqueCERTCertificate cert(CERT_NewTempCertificate(
    174      CERT_GetDefaultCertDB(), &item, nullptr, false, true));
    175  if (!cert) {
    176    PrintPRError("CERT_NewTempCertificate failed");
    177    return SECFailure;
    178  }
    179 
    180  UniquePK11SlotInfo slot(PK11_GetInternalKeySlot());
    181  if (!slot) {
    182    PrintPRError("PK11_GetInternalKeySlot failed");
    183    return SECFailure;
    184  }
    185  // The nickname is the filename without '.pem'.
    186  std::string nickname = filename.substr(0, filename.length() - 4);
    187  SECStatus rv = PK11_ImportCert(slot.get(), cert.get(), CK_INVALID_HANDLE,
    188                                 nickname.c_str(), false);
    189  if (rv != SECSuccess) {
    190    PrintPRError("PK11_ImportCert failed");
    191    return rv;
    192  }
    193 
    194  // By convention, the file `test-ca.pem` is a trust anchor.
    195  if (!filename.compare("test-ca.pem")) {
    196    if (PK11_NeedUserInit(slot.get())) {
    197      rv = PK11_InitPin(slot.get(), nullptr, nullptr);
    198      if (rv != SECSuccess) {
    199        PrintPRError("PK11_InitPin failed");
    200        return rv;
    201      }
    202    }
    203    rv = PK11_CheckUserPassword(slot.get(), "");
    204    if (rv != SECSuccess) {
    205      PrintPRError("PK11_CheckUserPassword failed");
    206      return rv;
    207    }
    208    CERTCertTrust trust{
    209        CERTDB_TERMINAL_RECORD | CERTDB_TRUSTED_CA | CERTDB_TRUSTED_CLIENT_CA,
    210        0, 0};
    211    rv = CERT_ChangeCertTrust(nullptr, cert.get(), &trust);
    212    if (rv != SECSuccess) {
    213      PrintPRError("CERT_ChangeCertTrust failed");
    214      return rv;
    215    }
    216  }
    217 
    218  return SECSuccess;
    219 }
    220 
    221 SECStatus LoadCertificatesAndKeys(const char* basePath) {
    222  // The NSS cert DB path could have been specified as "sql:path". Trim off
    223  // the leading "sql:" if so.
    224  if (strncmp(basePath, "sql:", 4) == 0) {
    225    basePath = basePath + 4;
    226  }
    227 
    228  UniquePRDir fdDir(PR_OpenDir(basePath));
    229  if (!fdDir) {
    230    PrintPRError("PR_OpenDir failed");
    231    return SECFailure;
    232  }
    233  // On the B2G ICS emulator, operations taken in AddCertificateFromFile
    234  // appear to interact poorly with readdir (more specifically, something is
    235  // causing readdir to never return null - it indefinitely loops through every
    236  // file in the directory, which causes timeouts). Rather than waste more time
    237  // chasing this down, loading certificates and keys happens in two phases:
    238  // filename collection and then loading. (This is probably a good
    239  // idea anyway because readdir isn't reentrant. Something could change later
    240  // such that it gets called as a result of calling AddCertificateFromFile or
    241  // AddKeyFromFile.)
    242  std::vector<std::string> certificates;
    243  std::vector<std::string> keys;
    244  for (PRDirEntry* dirEntry = PR_ReadDir(fdDir.get(), PR_SKIP_BOTH); dirEntry;
    245       dirEntry = PR_ReadDir(fdDir.get(), PR_SKIP_BOTH)) {
    246    size_t nameLength = strlen(dirEntry->name);
    247    if (nameLength > 4) {
    248      if (strncmp(dirEntry->name + nameLength - 4, ".pem", 4) == 0) {
    249        certificates.push_back(dirEntry->name);
    250      } else if (strncmp(dirEntry->name + nameLength - 4, ".key", 4) == 0) {
    251        keys.push_back(dirEntry->name);
    252      }
    253    }
    254  }
    255  SECStatus rv;
    256  for (std::string& certificate : certificates) {
    257    rv = AddCertificateFromFile(basePath, certificate.c_str());
    258    if (rv != SECSuccess) {
    259      return rv;
    260    }
    261  }
    262  for (std::string& key : keys) {
    263    rv = AddKeyFromFile(basePath, key.c_str());
    264    if (rv != SECSuccess) {
    265      return rv;
    266    }
    267  }
    268  return SECSuccess;
    269 }
    270 
    271 SECStatus InitializeNSS(const char* nssCertDBDir) {
    272  // Try initializing an existing DB.
    273  if (NSS_Init(nssCertDBDir) == SECSuccess) {
    274    return SECSuccess;
    275  }
    276 
    277  // Create a new DB if there is none...
    278  SECStatus rv = NSS_Initialize(nssCertDBDir, nullptr, nullptr, nullptr, 0);
    279  if (rv != SECSuccess) {
    280    return rv;
    281  }
    282 
    283  // ...and load all certificates into it.
    284  return LoadCertificatesAndKeys(nssCertDBDir);
    285 }
    286 
    287 nsresult SendAll(PRFileDesc* aSocket, const char* aData, size_t aDataLen) {
    288  if (gDebugLevel >= DEBUG_VERBOSE) {
    289    fprintf(stderr, "sending '%s'\n", aData);
    290  }
    291 
    292  while (aDataLen > 0) {
    293    int32_t bytesSent =
    294        PR_Send(aSocket, aData, aDataLen, 0, PR_INTERVAL_NO_TIMEOUT);
    295    if (bytesSent == -1) {
    296      PrintPRError("PR_Send failed");
    297      return NS_ERROR_FAILURE;
    298    }
    299 
    300    aDataLen -= bytesSent;
    301    aData += bytesSent;
    302  }
    303 
    304  return NS_OK;
    305 }
    306 
    307 nsresult ReplyToRequest(Connection* aConn) {
    308  // For debugging purposes, SendAll can print out what it's sending.
    309  // So, any strings we give to it to send need to be null-terminated.
    310  char buf[2] = {aConn->mByte, 0};
    311  return SendAll(aConn->mSocket, buf, 1);
    312 }
    313 
    314 nsresult SetupTLS(Connection* aConn, PRFileDesc* aModelSocket) {
    315  PRFileDesc* sslSocket = SSL_ImportFD(aModelSocket, aConn->mSocket);
    316  if (!sslSocket) {
    317    PrintPRError("SSL_ImportFD failed");
    318    return NS_ERROR_FAILURE;
    319  }
    320  aConn->mSocket = sslSocket;
    321 
    322  /* anti-replay must be configured to accept 0RTT */
    323  if (antiReplay) {
    324    SECStatus rv = SSL_SetAntiReplayContext(sslSocket, antiReplay);
    325    if (rv != SECSuccess) {
    326      PrintPRError("error configuring anti-replay ");
    327      return NS_ERROR_FAILURE;
    328    }
    329  }
    330 
    331  SSL_OptionSet(sslSocket, SSL_SECURITY, true);
    332  SSL_OptionSet(sslSocket, SSL_HANDSHAKE_AS_CLIENT, false);
    333  SSL_OptionSet(sslSocket, SSL_HANDSHAKE_AS_SERVER, true);
    334  // Unconditionally enabling 0RTT makes test_session_resumption.js fail
    335  SSL_OptionSet(sslSocket, SSL_ENABLE_0RTT_DATA,
    336                !!PR_GetEnv("MOZ_TLS_SERVER_0RTT"));
    337 
    338  SSL_ResetHandshake(sslSocket, /* asServer */ 1);
    339 
    340  return NS_OK;
    341 }
    342 
    343 nsresult ReadRequest(Connection* aConn) {
    344  int32_t bytesRead =
    345      PR_Recv(aConn->mSocket, &aConn->mByte, 1, 0, PR_INTERVAL_NO_TIMEOUT);
    346  if (bytesRead < 0) {
    347    PrintPRError("PR_Recv failed");
    348    return NS_ERROR_FAILURE;
    349  } else if (bytesRead == 0) {
    350    PR_SetError(PR_IO_ERROR, 0);
    351    PrintPRError("PR_Recv EOF in ReadRequest");
    352    return NS_ERROR_FAILURE;
    353  } else {
    354    if (gDebugLevel >= DEBUG_VERBOSE) {
    355      fprintf(stderr, "read '0x%hhx'\n", aConn->mByte);
    356    }
    357  }
    358  return NS_OK;
    359 }
    360 
    361 void HandleConnection(PRFileDesc* aSocket,
    362                      const UniquePRFileDesc& aModelSocket) {
    363  Connection conn(aSocket);
    364  nsresult rv = SetupTLS(&conn, aModelSocket.get());
    365  if (NS_FAILED(rv)) {
    366    PR_SetError(PR_INVALID_STATE_ERROR, 0);
    367    PrintPRError("PR_Recv failed");
    368    exit(1);
    369  }
    370 
    371  // TODO: On tests that are expected to fail (e.g. due to a revoked
    372  // certificate), the client will close the connection wtihout sending us the
    373  // request byte. In those cases, we should keep going. But, in the cases
    374  // where the connection is supposed to suceed, we should verify that we
    375  // successfully receive the request and send the response.
    376  rv = ReadRequest(&conn);
    377  if (NS_SUCCEEDED(rv)) {
    378    rv = ReplyToRequest(&conn);
    379  }
    380 }
    381 
    382 // returns 0 on success, non-zero on error
    383 int DoCallback() {
    384  UniquePRFileDesc socket(PR_NewTCPSocket());
    385  if (!socket) {
    386    PrintPRError("PR_NewTCPSocket failed");
    387    return 1;
    388  }
    389 
    390  PRNetAddr addr;
    391  PR_InitializeNetAddr(PR_IpAddrLoopback, gCallbackPort, &addr);
    392  if (PR_Connect(socket.get(), &addr, PR_INTERVAL_NO_TIMEOUT) != PR_SUCCESS) {
    393    PrintPRError("PR_Connect failed");
    394    return 1;
    395  }
    396 
    397  const char* request = "GET / HTTP/1.0\r\n\r\n";
    398  SendAll(socket.get(), request, strlen(request));
    399  char buf[4096];
    400  memset(buf, 0, sizeof(buf));
    401  int32_t bytesRead =
    402      PR_Recv(socket.get(), buf, sizeof(buf) - 1, 0, PR_INTERVAL_NO_TIMEOUT);
    403  if (bytesRead < 0) {
    404    PrintPRError("PR_Recv failed 1");
    405    return 1;
    406  }
    407  if (bytesRead == 0) {
    408    fprintf(stderr, "PR_Recv eof 1\n");
    409    return 1;
    410  }
    411  fprintf(stderr, "%s\n", buf);
    412  return 0;
    413 }
    414 
    415 SECStatus ConfigSecureServerWithNamedCert(
    416    PRFileDesc* fd, const char* certName,
    417    /*optional*/ UniqueCERTCertificate* certOut,
    418    /*optional*/ SSLKEAType* keaOut,
    419    /*optional*/ SSLExtraServerCertData* extraData) {
    420  UniqueCERTCertificate cert(PK11_FindCertFromNickname(certName, nullptr));
    421  if (!cert) {
    422    PrintPRError("PK11_FindCertFromNickname failed");
    423    return SECFailure;
    424  }
    425  // If an intermediate certificate issued the server certificate (rather than
    426  // directly by a trust anchor), we want to send it along in the handshake so
    427  // we don't encounter unknown issuer errors when that's not what we're
    428  // testing.
    429  UniqueCERTCertificateList certList;
    430  UniqueCERTCertificate issuerCert(
    431      CERT_FindCertByName(CERT_GetDefaultCertDB(), &cert->derIssuer));
    432  // If we can't find the issuer cert, continue without it.
    433  if (issuerCert) {
    434    // Sadly, CERTCertificateList does not have a CERT_NewCertificateList
    435    // utility function, so we must create it ourselves. This consists
    436    // of creating an arena, allocating space for the CERTCertificateList,
    437    // and then transferring ownership of the arena to that list.
    438    UniquePLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE));
    439    if (!arena) {
    440      PrintPRError("PORT_NewArena failed");
    441      return SECFailure;
    442    }
    443    certList.reset(static_cast<CERTCertificateList*>(
    444        PORT_ArenaAlloc(arena.get(), sizeof(CERTCertificateList))));
    445    if (!certList) {
    446      PrintPRError("PORT_ArenaAlloc failed");
    447      return SECFailure;
    448    }
    449    certList->arena = arena.release();
    450    // We also have to manually copy the certificates we care about to the
    451    // list, because there aren't any utility functions for that either.
    452    certList->certs = static_cast<SECItem*>(
    453        PORT_ArenaAlloc(certList->arena, 2 * sizeof(SECItem)));
    454    if (SECITEM_CopyItem(certList->arena, certList->certs, &cert->derCert) !=
    455        SECSuccess) {
    456      PrintPRError("SECITEM_CopyItem failed");
    457      return SECFailure;
    458    }
    459    if (SECITEM_CopyItem(certList->arena, certList->certs + 1,
    460                         &issuerCert->derCert) != SECSuccess) {
    461      PrintPRError("SECITEM_CopyItem failed");
    462      return SECFailure;
    463    }
    464    certList->len = 2;
    465  }
    466 
    467  UniquePK11SlotInfo slot(PK11_GetInternalKeySlot());
    468  if (!slot) {
    469    PrintPRError("PK11_GetInternalKeySlot failed");
    470    return SECFailure;
    471  }
    472  UniqueSECKEYPrivateKey key(
    473      PK11_FindKeyByDERCert(slot.get(), cert.get(), nullptr));
    474  if (!key) {
    475    PrintPRError("PK11_FindKeyByDERCert failed");
    476    return SECFailure;
    477  }
    478 
    479  if (extraData) {
    480    SSLExtraServerCertData dataCopy = {ssl_auth_null, nullptr, nullptr,
    481                                       nullptr,       nullptr, nullptr};
    482    memcpy(&dataCopy, extraData, sizeof(dataCopy));
    483    dataCopy.certChain = certList.get();
    484 
    485    if (SSL_ConfigServerCert(fd, cert.get(), key.get(), &dataCopy,
    486                             sizeof(dataCopy)) != SECSuccess) {
    487      PrintPRError("SSL_ConfigServerCert failed");
    488      return SECFailure;
    489    }
    490 
    491  } else {
    492    // This is the deprecated setup mechanism, to be cleaned up in Bug 1569222
    493    SSLKEAType certKEA = NSS_FindCertKEAType(cert.get());
    494    if (SSL_ConfigSecureServerWithCertChain(fd, cert.get(), certList.get(),
    495                                            key.get(), certKEA) != SECSuccess) {
    496      PrintPRError("SSL_ConfigSecureServer failed");
    497      return SECFailure;
    498    }
    499 
    500    if (keaOut) {
    501      *keaOut = certKEA;
    502    }
    503  }
    504 
    505  if (certOut) {
    506    *certOut = std::move(cert);
    507  }
    508 
    509  SSL_OptionSet(fd, SSL_NO_CACHE, false);
    510  SSL_OptionSet(fd, SSL_ENABLE_SESSION_TICKETS, true);
    511  // Unconditionally enabling 0RTT makes test_session_resumption.js fail
    512  SSL_OptionSet(fd, SSL_ENABLE_0RTT_DATA, !!PR_GetEnv("MOZ_TLS_SERVER_0RTT"));
    513 
    514  return SECSuccess;
    515 }
    516 
    517 #ifdef XP_WIN
    518 using PidType = DWORD;
    519 constexpr bool IsValidPid(long long pid) {
    520  // Excluding `(DWORD)-1` because it is not a valid process ID.
    521  // See https://devblogs.microsoft.com/oldnewthing/20040223-00/?p=40503
    522  return pid > 0 && pid < std::numeric_limits<PidType>::max();
    523 }
    524 #else
    525 using PidType = pid_t;
    526 constexpr bool IsValidPid(long long pid) {
    527  return pid > 0 && pid <= std::numeric_limits<PidType>::max();
    528 }
    529 #endif
    530 
    531 PidType ConvertPid(const char* pidStr) {
    532  long long pid = strtoll(pidStr, nullptr, 10);
    533  if (!IsValidPid(pid)) {
    534    return 0;
    535  }
    536  return static_cast<PidType>(pid);
    537 }
    538 
    539 int StartServer(int argc, char* argv[], SSLSNISocketConfig sniSocketConfig,
    540                void* sniSocketConfigArg, ServerConfigFunc configFunc) {
    541  if (argc != 3) {
    542    fprintf(stderr, "usage: %s <NSS DB directory> <ppid>\n", argv[0]);
    543    return 1;
    544  }
    545  const char* nssCertDBDir = argv[1];
    546  PidType ppid = ConvertPid(argv[2]);
    547 
    548  const char* debugLevel = PR_GetEnv("MOZ_TLS_SERVER_DEBUG_LEVEL");
    549  if (debugLevel) {
    550    int level = atoi(debugLevel);
    551    switch (level) {
    552      case DEBUG_ERRORS:
    553        gDebugLevel = DEBUG_ERRORS;
    554        break;
    555      case DEBUG_WARNINGS:
    556        gDebugLevel = DEBUG_WARNINGS;
    557        break;
    558      case DEBUG_VERBOSE:
    559        gDebugLevel = DEBUG_VERBOSE;
    560        break;
    561      default:
    562        PrintPRError("invalid MOZ_TLS_SERVER_DEBUG_LEVEL");
    563        return 1;
    564    }
    565  }
    566 
    567  const char* callbackPort = PR_GetEnv("MOZ_TLS_SERVER_CALLBACK_PORT");
    568  if (callbackPort) {
    569    gCallbackPort = atoi(callbackPort);
    570  }
    571 
    572  if (InitializeNSS(nssCertDBDir) != SECSuccess) {
    573    PR_fprintf(PR_STDERR, "InitializeNSS failed");
    574    return 1;
    575  }
    576 
    577  if (NSS_SetDomesticPolicy() != SECSuccess) {
    578    PrintPRError("NSS_SetDomesticPolicy failed");
    579    return 1;
    580  }
    581 
    582  /* Disabling NSS_KEY_SIZE_POLICY as we operate with short keys. */
    583  NSS_OptionSet(NSS_KEY_SIZE_POLICY_FLAGS, 0);
    584 
    585  if (SSL_ConfigServerSessionIDCache(0, 0, 0, nullptr) != SECSuccess) {
    586    PrintPRError("SSL_ConfigServerSessionIDCache failed");
    587    return 1;
    588  }
    589 
    590  UniquePRFileDesc serverSocket(PR_NewTCPSocket());
    591  if (!serverSocket) {
    592    PrintPRError("PR_NewTCPSocket failed");
    593    return 1;
    594  }
    595 
    596  PRSocketOptionData socketOption;
    597  socketOption.option = PR_SockOpt_Reuseaddr;
    598  socketOption.value.reuse_addr = true;
    599  PR_SetSocketOption(serverSocket.get(), &socketOption);
    600 
    601  PRNetAddr serverAddr;
    602  PR_InitializeNetAddr(PR_IpAddrLoopback, LISTEN_PORT, &serverAddr);
    603  if (PR_Bind(serverSocket.get(), &serverAddr) != PR_SUCCESS) {
    604    PrintPRError("PR_Bind failed");
    605    return 1;
    606  }
    607 
    608  if (PR_Listen(serverSocket.get(), 1) != PR_SUCCESS) {
    609    PrintPRError("PR_Listen failed");
    610    return 1;
    611  }
    612 
    613  UniquePRFileDesc rawModelSocket(PR_NewTCPSocket());
    614  if (!rawModelSocket) {
    615    PrintPRError("PR_NewTCPSocket failed for rawModelSocket");
    616    return 1;
    617  }
    618 
    619  UniquePRFileDesc modelSocket(SSL_ImportFD(nullptr, rawModelSocket.release()));
    620  if (!modelSocket) {
    621    PrintPRError("SSL_ImportFD of rawModelSocket failed");
    622    return 1;
    623  }
    624 
    625  SSLVersionRange range = {0, 0};
    626  if (SSL_VersionRangeGet(modelSocket.get(), &range) != SECSuccess) {
    627    PrintPRError("SSL_VersionRangeGet failed");
    628    return 1;
    629  }
    630 
    631  if (range.max < SSL_LIBRARY_VERSION_TLS_1_3) {
    632    range.max = SSL_LIBRARY_VERSION_TLS_1_3;
    633    if (SSL_VersionRangeSet(modelSocket.get(), &range) != SECSuccess) {
    634      PrintPRError("SSL_VersionRangeSet failed");
    635      return 1;
    636    }
    637  }
    638 
    639  if (PR_GetEnv("MOZ_TLS_SERVER_0RTT")) {
    640    if (SSL_CreateAntiReplayContext(PR_Now(), 1L * PR_USEC_PER_SEC, 7, 14,
    641                                    &antiReplay) != SECSuccess) {
    642      PrintPRError("Unable to create anti-replay context for 0-RTT.");
    643      return 1;
    644    }
    645  }
    646 
    647  if (SSL_SNISocketConfigHook(modelSocket.get(), sniSocketConfig,
    648                              sniSocketConfigArg) != SECSuccess) {
    649    PrintPRError("SSL_SNISocketConfigHook failed");
    650    return 1;
    651  }
    652 
    653  // We have to configure the server with a certificate, but it's not one
    654  // we're actually going to end up using. In the SNI callback, we pick
    655  // the right certificate for the connection.
    656  //
    657  // Provide an empty |extra_data| to force config via SSL_ConfigServerCert.
    658  // This is a temporary mechanism to work around inconsistent setting of
    659  // |authType| in the deprecated API (preventing the default cert from
    660  // being removed in favor of the SNI-selected cert). This may be removed
    661  // after Bug 1569222 removes the deprecated mechanism.
    662  SSLExtraServerCertData extra_data = {ssl_auth_null, nullptr, nullptr,
    663                                       nullptr,       nullptr, nullptr};
    664  if (ConfigSecureServerWithNamedCert(modelSocket.get(), DEFAULT_CERT_NICKNAME,
    665                                      nullptr, nullptr,
    666                                      &extra_data) != SECSuccess) {
    667    return 1;
    668  }
    669 
    670  // Call back to implementation-defined configuration func, if provided.
    671  if (configFunc) {
    672    if (((configFunc)(modelSocket.get())) != SECSuccess) {
    673      PrintPRError("configFunc failed");
    674      return 1;
    675    }
    676  }
    677 
    678  if (gCallbackPort != 0) {
    679    if (DoCallback()) {
    680      return 1;
    681    }
    682  }
    683 
    684  std::thread([ppid] {
    685    if (!ppid) {
    686      if (gDebugLevel >= DEBUG_ERRORS) {
    687        fprintf(stderr, "invalid ppid\n");
    688      }
    689      return;
    690    }
    691 #ifdef XP_WIN
    692    HANDLE parent = OpenProcess(SYNCHRONIZE, false, ppid);
    693    if (!parent) {
    694      if (gDebugLevel >= DEBUG_ERRORS) {
    695        fprintf(stderr, "OpenProcess failed\n");
    696      }
    697      return;
    698    }
    699    WaitForSingleObject(parent, INFINITE);
    700    CloseHandle(parent);
    701 #else
    702    while (getppid() == ppid) {
    703      sleep(1);
    704    }
    705 #endif
    706    if (gDebugLevel >= DEBUG_ERRORS) {
    707      fprintf(stderr, "Parent process crashed\n");
    708    }
    709    exit(1);
    710  }).detach();
    711 
    712  while (true) {
    713    PRNetAddr clientAddr;
    714    PRFileDesc* clientSocket =
    715        PR_Accept(serverSocket.get(), &clientAddr, PR_INTERVAL_NO_TIMEOUT);
    716    HandleConnection(clientSocket, modelSocket);
    717  }
    718 }
    719 
    720 }  // namespace test
    721 }  // namespace mozilla