testing/mochitest/ssltunnel/ssltunnel.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     1 /* -*- Mode: C++; tab-width: 2; 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/. */
     6 /*
     7  * WARNING: DO NOT USE THIS CODE IN PRODUCTION SYSTEMS.  It is highly likely to
     8  *          be plagued with the usual problems endemic to C (buffer overflows
     9  *          and the like).  We don't especially care here (but would accept
    10  *          patches!) because this is only intended for use in our test
    11  *          harnesses in controlled situations where input is guaranteed not to
    12  *          be malicious.
    13  */
    15 #include "ScopedNSSTypes.h"
    16 #include <assert.h>
    17 #include <stdio.h>
    18 #include <string>
    19 #include <vector>
    20 #include <algorithm>
    21 #include <stdarg.h>
    22 #include "prinit.h"
    23 #include "prerror.h"
    24 #include "prenv.h"
    25 #include "prnetdb.h"
    26 #include "prtpool.h"
    27 #include "nsAlgorithm.h"
    28 #include "nss.h"
    29 #include "key.h"
    30 #include "ssl.h"
    31 #include "plhash.h"
    33 using namespace mozilla;
    34 using namespace mozilla::psm;
    35 using std::string;
    36 using std::vector;
    38 #define IS_DELIM(m, c)          ((m)[(c) >> 3] & (1 << ((c) & 7)))
    39 #define SET_DELIM(m, c)         ((m)[(c) >> 3] |= (1 << ((c) & 7)))
    40 #define DELIM_TABLE_SIZE        32
    42 // You can set the level of logging by env var SSLTUNNEL_LOG_LEVEL=n, where n
    43 // is 0 through 3.  The default is 1, INFO level logging.
    44 enum LogLevel {
    45   LEVEL_DEBUG = 0,
    46   LEVEL_INFO = 1,
    47   LEVEL_ERROR = 2,
    48   LEVEL_SILENT = 3
    49 } gLogLevel, gLastLogLevel;
    51 #define _LOG_OUTPUT(level, func, params) \
    52 PR_BEGIN_MACRO \
    53   if (level >= gLogLevel) { \
    54     gLastLogLevel = level; \
    55     func params;\
    56   } \
    57 PR_END_MACRO
    59 // The most verbose output
    60 #define LOG_DEBUG(params) \
    61   _LOG_OUTPUT(LEVEL_DEBUG, printf, params)
    63 // Top level informative messages
    64 #define LOG_INFO(params) \
    65   _LOG_OUTPUT(LEVEL_INFO, printf, params)
    67 // Serious errors that must be logged always until completely gag
    68 #define LOG_ERROR(params) \
    69   _LOG_OUTPUT(LEVEL_ERROR, eprintf, params)
    71 // Same as LOG_ERROR, but when logging is set to LEVEL_DEBUG, the message 
    72 // will be put to the stdout instead of stderr to keep continuity with other 
    73 // LOG_DEBUG message output
    74 #define LOG_ERRORD(params) \
    75 PR_BEGIN_MACRO \
    76   if (gLogLevel == LEVEL_DEBUG) \
    77     _LOG_OUTPUT(LEVEL_ERROR, printf, params); \
    78   else \
    79     _LOG_OUTPUT(LEVEL_ERROR, eprintf, params); \
    80 PR_END_MACRO
    82 // If there is any output written between LOG_BEGIN_BLOCK() and
    83 // LOG_END_BLOCK() then a new line will be put to the proper output (out/err)
    84 #define LOG_BEGIN_BLOCK() \
    85   gLastLogLevel = LEVEL_SILENT;
    87 #define LOG_END_BLOCK() \
    88 PR_BEGIN_MACRO \
    89   if (gLastLogLevel == LEVEL_ERROR) \
    90     LOG_ERROR(("\n")); \
    91   if (gLastLogLevel < LEVEL_ERROR) \
    92     _LOG_OUTPUT(gLastLogLevel, printf, ("\n")); \
    93 PR_END_MACRO
    95 int eprintf(const char* str, ...)
    96 {
    97   va_list ap;
    98   va_start(ap, str);
    99   int result = vfprintf(stderr, str, ap);
   100   va_end(ap);
   101   return result;
   102 }
   104 // Copied from nsCRT
   105 char* strtok2(char* string, const char* delims, char* *newStr)
   106 {
   107   PR_ASSERT(string);
   109   char delimTable[DELIM_TABLE_SIZE];
   110   uint32_t i;
   111   char* result;
   112   char* str = string;
   114   for (i = 0; i < DELIM_TABLE_SIZE; i++)
   115     delimTable[i] = '\0';
   117   for (i = 0; delims[i]; i++) {
   118     SET_DELIM(delimTable, static_cast<uint8_t>(delims[i]));
   119   }
   121   // skip to beginning
   122   while (*str && IS_DELIM(delimTable, static_cast<uint8_t>(*str))) {
   123     str++;
   124   }
   125   result = str;
   127   // fix up the end of the token
   128   while (*str) {
   129     if (IS_DELIM(delimTable, static_cast<uint8_t>(*str))) {
   130       *str++ = '\0';
   131       break;
   132     }
   133     str++;
   134   }
   135   *newStr = str;
   137   return str == result ? nullptr : result;
   138 }
   142 enum client_auth_option {
   143   caNone = 0,
   144   caRequire = 1,
   145   caRequest = 2
   146 };
   148 // Structs for passing data into jobs on the thread pool
   149 typedef struct {
   150   int32_t listen_port;
   151   string cert_nickname;
   152   PLHashTable* host_cert_table;
   153   PLHashTable* host_clientauth_table;
   154   PLHashTable* host_redir_table;
   155 } server_info_t;
   157 typedef struct {
   158   PRFileDesc* client_sock;
   159   PRNetAddr client_addr;
   160   server_info_t* server_info;
   161   // the original host in the Host: header for this connection is
   162   // stored here, for proxied connections
   163   string original_host;
   164   // true if no SSL should be used for this connection
   165   bool http_proxy_only;
   166   // true if this connection is for a WebSocket
   167   bool iswebsocket;
   168 } connection_info_t;
   170 typedef struct {
   171   string fullHost;
   172   bool matched;
   173 } server_match_t;
   175 const int32_t BUF_SIZE = 16384;
   176 const int32_t BUF_MARGIN = 1024;
   177 const int32_t BUF_TOTAL = BUF_SIZE + BUF_MARGIN;
   179 struct relayBuffer
   180 {
   181   char *buffer, *bufferhead, *buffertail, *bufferend;
   183   relayBuffer()
   184   {
   185     // Leave 1024 bytes more for request line manipulations
   186     bufferhead = buffertail = buffer = new char[BUF_TOTAL];
   187     bufferend = buffer + BUF_SIZE;
   188   }
   190   ~relayBuffer()
   191   {
   192     delete [] buffer;
   193   }
   195   void compact() {
   196     if (buffertail == bufferhead)
   197       buffertail = bufferhead = buffer;
   198   }
   200   bool empty() { return bufferhead == buffertail; }
   201   size_t areafree() { return bufferend - buffertail; }
   202   size_t margin() { return areafree() + BUF_MARGIN; }
   203   size_t present() { return buffertail - bufferhead; }
   204 };
   206 // These numbers are multiplied by the number of listening ports (actual
   207 // servers running).  According the thread pool implementation there is no
   208 // need to limit the number of threads initially, threads are allocated
   209 // dynamically and stored in a linked list.  Initial number of 2 is chosen
   210 // to allocate a thread for socket accept and preallocate one for the first
   211 // connection that is with high probability expected to come.
   212 const uint32_t INITIAL_THREADS = 2;
   213 const uint32_t MAX_THREADS = 100;
   214 const uint32_t DEFAULT_STACKSIZE = (512 * 1024);
   216 // global data
   217 string nssconfigdir;
   218 vector<server_info_t> servers;
   219 PRNetAddr remote_addr;
   220 PRNetAddr websocket_server;
   221 PRThreadPool* threads = nullptr;
   222 PRLock* shutdown_lock = nullptr;
   223 PRCondVar* shutdown_condvar = nullptr;
   224 // Not really used, unless something fails to start
   225 bool shutdown_server = false;
   226 bool do_http_proxy = false;
   227 bool any_host_spec_config = false;
   229 int ClientAuthValueComparator(const void *v1, const void *v2)
   230 {
   231   int a = *static_cast<const client_auth_option*>(v1) -
   232           *static_cast<const client_auth_option*>(v2);
   233   if (a == 0)
   234     return 0;
   235   if (a > 0)
   236     return 1;
   237   else // (a < 0)
   238     return -1;
   239 }
   241 static int match_hostname(PLHashEntry *he, int index, void* arg)
   242 {
   243   server_match_t *match = (server_match_t*)arg;
   244   if (match->fullHost.find((char*)he->key) != string::npos)
   245     match->matched = true;
   246   return HT_ENUMERATE_NEXT;
   247 }
   249 /*
   250  * Signal the main thread that the application should shut down.
   251  */
   252 void SignalShutdown()
   253 {
   254   PR_Lock(shutdown_lock);
   255   PR_NotifyCondVar(shutdown_condvar);
   256   PR_Unlock(shutdown_lock);
   257 }
   259 bool ReadConnectRequest(server_info_t* server_info, 
   260     relayBuffer& buffer, int32_t* result, string& certificate,
   261     client_auth_option* clientauth, string& host, string& location)
   262 {
   263   if (buffer.present() < 4) {
   264     LOG_DEBUG((" !! only %d bytes present in the buffer", (int)buffer.present()));
   265     return false;
   266   }
   267   if (strncmp(buffer.buffertail-4, "\r\n\r\n", 4)) {
   268     LOG_ERRORD((" !! request is not tailed with CRLFCRLF but with %x %x %x %x", 
   269                *(buffer.buffertail-4),
   270                *(buffer.buffertail-3),
   271                *(buffer.buffertail-2),
   272                *(buffer.buffertail-1)));
   273     return false;
   274   }
   276   LOG_DEBUG((" parsing initial connect request, dump:\n%.*s\n", (int)buffer.present(), buffer.bufferhead));
   278   *result = 400;
   280   char* token;
   281   char* _caret;
   282   token = strtok2(buffer.bufferhead, " ", &_caret);
   283   if (!token) {
   284     LOG_ERRORD((" no space found"));
   285     return true;
   286   }
   287   if (strcmp(token, "CONNECT")) {
   288     LOG_ERRORD((" not CONNECT request but %s", token));
   289     return true;
   290   }
   292   token = strtok2(_caret, " ", &_caret);
   293   void* c = PL_HashTableLookup(server_info->host_cert_table, token);
   294   if (c)
   295     certificate = static_cast<char*>(c);
   297   host = "https://";
   298   host += token;
   300   c = PL_HashTableLookup(server_info->host_clientauth_table, token);
   301   if (c)
   302     *clientauth = *static_cast<client_auth_option*>(c);
   303   else
   304     *clientauth = caNone;
   306   void *redir = PL_HashTableLookup(server_info->host_redir_table, token);
   307   if (redir)
   308     location = static_cast<char*>(redir);
   310   token = strtok2(_caret, "/", &_caret);
   311   if (strcmp(token, "HTTP")) {  
   312     LOG_ERRORD((" not tailed with HTTP but with %s", token));
   313     return true;
   314   }
   316   *result = (redir) ? 302 : 200;
   317   return true;
   318 }
   320 bool ConfigureSSLServerSocket(PRFileDesc* socket, server_info_t* si, string &certificate, client_auth_option clientAuth)
   321 {
   322   const char* certnick = certificate.empty() ?
   323       si->cert_nickname.c_str() : certificate.c_str();
   325   ScopedCERTCertificate cert(PK11_FindCertFromNickname(certnick, nullptr));
   326   if (!cert) {
   327     LOG_ERROR(("Failed to find cert %s\n", certnick));
   328     return false;
   329   }
   331   ScopedSECKEYPrivateKey privKey(PK11_FindKeyByAnyCert(cert, nullptr));
   332   if (!privKey) {
   333     LOG_ERROR(("Failed to find private key\n"));
   334     return false;
   335   }
   337   PRFileDesc* ssl_socket = SSL_ImportFD(nullptr, socket);
   338   if (!ssl_socket) {
   339     LOG_ERROR(("Error importing SSL socket\n"));
   340     return false;
   341   }
   343   SSLKEAType certKEA = NSS_FindCertKEAType(cert);
   344   if (SSL_ConfigSecureServer(ssl_socket, cert, privKey, certKEA)
   345       != SECSuccess) {
   346     LOG_ERROR(("Error configuring SSL server socket\n"));
   347     return false;
   348   }
   350   SSL_OptionSet(ssl_socket, SSL_SECURITY, true);
   351   SSL_OptionSet(ssl_socket, SSL_HANDSHAKE_AS_CLIENT, false);
   352   SSL_OptionSet(ssl_socket, SSL_HANDSHAKE_AS_SERVER, true);
   354   if (clientAuth != caNone)
   355   {
   356     SSL_OptionSet(ssl_socket, SSL_REQUEST_CERTIFICATE, true);
   357     SSL_OptionSet(ssl_socket, SSL_REQUIRE_CERTIFICATE, clientAuth == caRequire);
   358   }
   360   SSL_ResetHandshake(ssl_socket, true);
   362   return true;
   363 }
   365 /**
   366  * This function examines the buffer for a Sec-WebSocket-Location: field, 
   367  * and if it's present, it replaces the hostname in that field with the
   368  * value in the server's original_host field.  This function works
   369  * in the reverse direction as AdjustWebSocketHost(), replacing the real
   370  * hostname of a response with the potentially fake hostname that is expected
   371  * by the browser (e.g., mochi.test).
   372  *
   373  * @return true if the header was adjusted successfully, or not found, false
   374  * if the header is present but the url is not, which should indicate
   375  * that more data needs to be read from the socket
   376  */
   377 bool AdjustWebSocketLocation(relayBuffer& buffer, connection_info_t *ci)
   378 {
   379   assert(buffer.margin());
   380   buffer.buffertail[1] = '\0';
   382   char* wsloc = strstr(buffer.bufferhead, "Sec-WebSocket-Location:");
   383   if (!wsloc)
   384     return true;
   385   // advance pointer to the start of the hostname
   386   wsloc = strstr(wsloc, "ws://");
   387   if (!wsloc)
   388     return false;
   389   wsloc += 5;
   390   // find the end of the hostname
   391   char* wslocend = strchr(wsloc + 1, '/');
   392   if (!wslocend)
   393     return false;
   394   char *crlf = strstr(wsloc, "\r\n");
   395   if (!crlf)
   396     return false;
   397   if (ci->original_host.empty())
   398     return true;
   400   int diff = ci->original_host.length() - (wslocend-wsloc);
   401   if (diff > 0)
   402     assert(size_t(diff) <= buffer.margin());
   403   memmove(wslocend + diff, wslocend, buffer.buffertail - wsloc - diff);
   404   buffer.buffertail += diff;
   406   memcpy(wsloc, ci->original_host.c_str(), ci->original_host.length());
   407   return true;
   408 }
   410 /**
   411  * This function examines the buffer for a Host: field, and if it's present,
   412  * it replaces the hostname in that field with the hostname in the server's
   413  * remote_addr field.  This is needed because proxy requests may be coming
   414  * from mochitest with fake hosts, like mochi.test, and these need to be
   415  * replaced with the host that the destination server is actually running
   416  * on.
   417  */
   418 bool AdjustWebSocketHost(relayBuffer& buffer, connection_info_t *ci)
   419 {
   420   const char HEADER_UPGRADE[] = "Upgrade:";
   421   const char HEADER_HOST[] = "Host:";
   423   PRNetAddr inet_addr = (websocket_server.inet.port ? websocket_server :
   424     remote_addr);
   426   assert(buffer.margin());
   428   // Cannot use strnchr so add a null char at the end. There is always some
   429   // space left because we preserve a margin.
   430   buffer.buffertail[1] = '\0';
   432   // Verify this is a WebSocket header.
   433   char* h1 = strstr(buffer.bufferhead, HEADER_UPGRADE);
   434   if (!h1)
   435     return false;
   436   h1 += strlen(HEADER_UPGRADE);
   437   h1 += strspn(h1, " \t");
   438   char* h2 = strstr(h1, "WebSocket\r\n");
   439   if (!h2) h2 = strstr(h1, "websocket\r\n");
   440   if (!h2) h2 = strstr(h1, "Websocket\r\n");
   441   if (!h2)
   442     return false;
   444   char* host = strstr(buffer.bufferhead, HEADER_HOST);
   445   if (!host)
   446     return false;
   447   // advance pointer to beginning of hostname
   448   host += strlen(HEADER_HOST);
   449   host += strspn(host, " \t");
   451   char* endhost = strstr(host, "\r\n");
   452   if (!endhost)
   453     return false;
   455   // Save the original host, so we can use it later on responses from the
   456   // server.
   457   ci->original_host.assign(host, endhost-host);
   459   char newhost[40];
   460   PR_NetAddrToString(&inet_addr, newhost, sizeof(newhost));
   461   assert(strlen(newhost) < sizeof(newhost) - 7);
   462   sprintf(newhost, "%s:%d", newhost, PR_ntohs(inet_addr.inet.port));
   464   int diff = strlen(newhost) - (endhost-host);
   465   if (diff > 0)
   466     assert(size_t(diff) <= buffer.margin());
   467   memmove(endhost + diff, endhost, buffer.buffertail - host - diff);
   468   buffer.buffertail += diff;
   470   memcpy(host, newhost, strlen(newhost));
   471   return true;
   472 }
   474 /**
   475  * This function prefixes Request-URI path with a full scheme-host-port
   476  * string.
   477  */
   478 bool AdjustRequestURI(relayBuffer& buffer, string *host)
   479 {
   480   assert(buffer.margin());
   482   // Cannot use strnchr so add a null char at the end. There is always some space left
   483   // because we preserve a margin.
   484   buffer.buffertail[1] = '\0';
   485   LOG_DEBUG((" incoming request to adjust:\n%s\n", buffer.bufferhead));
   487   char *token, *path;
   488   path = strchr(buffer.bufferhead, ' ') + 1;
   489   if (!path)
   490     return false;
   492   // If the path doesn't start with a slash don't change it, it is probably '*' or a full
   493   // path already. Return true, we are done with this request adjustment.
   494   if (*path != '/')
   495     return true;
   497   token = strchr(path, ' ') + 1;
   498   if (!token)
   499     return false;
   501   if (strncmp(token, "HTTP/", 5))
   502     return false;
   504   size_t hostlength = host->length();
   505   assert(hostlength <= buffer.margin());
   507   memmove(path + hostlength, path, buffer.buffertail - path);
   508   memcpy(path, host->c_str(), hostlength);
   509   buffer.buffertail += hostlength;
   511   return true;
   512 }
   514 bool ConnectSocket(PRFileDesc *fd, const PRNetAddr *addr, PRIntervalTime timeout)
   515 {
   516   PRStatus stat = PR_Connect(fd, addr, timeout);
   517   if (stat != PR_SUCCESS)
   518     return false;
   520   PRSocketOptionData option;
   521   option.option = PR_SockOpt_Nonblocking;
   522   option.value.non_blocking = true;
   523   PR_SetSocketOption(fd, &option);
   525   return true;
   526 }
   528 /*
   529  * Handle an incoming client connection. The server thread has already
   530  * accepted the connection, so we just need to connect to the remote
   531  * port and then proxy data back and forth.
   532  * The data parameter is a connection_info_t*, and must be deleted
   533  * by this function.
   534  */
   535 void HandleConnection(void* data)
   536 {
   537   connection_info_t* ci = static_cast<connection_info_t*>(data);
   538   PRIntervalTime connect_timeout = PR_SecondsToInterval(30);
   540   ScopedPRFileDesc other_sock(PR_NewTCPSocket());
   541   bool client_done = false;
   542   bool client_error = false;
   543   bool connect_accepted = !do_http_proxy;
   544   bool ssl_updated = !do_http_proxy;
   545   bool expect_request_start = do_http_proxy;
   546   string certificateToUse;
   547   string locationHeader;
   548   client_auth_option clientAuth;
   549   string fullHost;
   551   LOG_DEBUG(("SSLTUNNEL(%p)): incoming connection csock(0)=%p, ssock(1)=%p\n",
   552          static_cast<void*>(data),
   553          static_cast<void*>(ci->client_sock),
   554          static_cast<void*>(other_sock)));
   555   if (other_sock) 
   556   {
   557     int32_t numberOfSockets = 1;
   559     relayBuffer buffers[2];
   561     if (!do_http_proxy)
   562     {
   563       if (!ConfigureSSLServerSocket(ci->client_sock, ci->server_info, certificateToUse, caNone))
   564         client_error = true;
   565       else if (!ConnectSocket(other_sock, &remote_addr, connect_timeout))
   566         client_error = true;
   567       else
   568         numberOfSockets = 2;
   569     }
   571     PRPollDesc sockets[2] = 
   572     { 
   573       {ci->client_sock, PR_POLL_READ, 0},
   574       {other_sock, PR_POLL_READ, 0}
   575     };
   576     bool socketErrorState[2] = {false, false};
   578     while (!((client_error||client_done) && buffers[0].empty() && buffers[1].empty()))
   579     {
   580       sockets[0].in_flags |= PR_POLL_EXCEPT;
   581       sockets[1].in_flags |= PR_POLL_EXCEPT;
   582       LOG_DEBUG(("SSLTUNNEL(%p)): polling flags csock(0)=%c%c, ssock(1)=%c%c\n",
   583                  static_cast<void*>(data),
   584                  sockets[0].in_flags & PR_POLL_READ  ? 'R' : '-',
   585                  sockets[0].in_flags & PR_POLL_WRITE ? 'W' : '-',
   586                  sockets[1].in_flags & PR_POLL_READ  ? 'R' : '-',
   587                  sockets[1].in_flags & PR_POLL_WRITE ? 'W' : '-'));
   588       int32_t pollStatus = PR_Poll(sockets, numberOfSockets, PR_MillisecondsToInterval(1000));
   589       if (pollStatus < 0)
   590       {
   591         LOG_DEBUG(("SSLTUNNEL(%p)): pollStatus=%d, exiting\n",
   592                    static_cast<void*>(data), pollStatus));
   593         client_error = true;
   594         break;
   595       }
   597       if (pollStatus == 0)
   598       {
   599         // timeout
   600         LOG_DEBUG(("SSLTUNNEL(%p)): poll timeout, looping\n",
   601                    static_cast<void*>(data)));
   602         continue;
   603       }
   605       for (int32_t s = 0; s < numberOfSockets; ++s)
   606       {
   607         int32_t s2 = s == 1 ? 0 : 1;
   608         int16_t out_flags = sockets[s].out_flags;
   609         int16_t &in_flags = sockets[s].in_flags;
   610         int16_t &in_flags2 = sockets[s2].in_flags;
   611         sockets[s].out_flags = 0;
   613         LOG_BEGIN_BLOCK();
   614         LOG_DEBUG(("SSLTUNNEL(%p)): %csock(%d)=%p out_flags=%d",
   615                    static_cast<void*>(data),
   616                    s == 0 ? 'c' : 's',
   617                    s,
   618                    static_cast<void*>(sockets[s].fd),
   619                    out_flags));
   620         if (out_flags & (PR_POLL_EXCEPT | PR_POLL_ERR | PR_POLL_HUP))
   621         {
   622           LOG_DEBUG((" :exception\n"));
   623           client_error = true;
   624           socketErrorState[s] = true;
   625           // We got a fatal error state on the socket. Clear the output buffer
   626           // for this socket to break the main loop, we will never more be able
   627           // to send those data anyway.
   628           buffers[s2].bufferhead = buffers[s2].buffertail = buffers[s2].buffer;
   629           continue;
   630         } // PR_POLL_EXCEPT, PR_POLL_ERR, PR_POLL_HUP handling
   632         if (out_flags & PR_POLL_READ && !buffers[s].areafree())
   633         {
   634            LOG_DEBUG((" no place in read buffer but got read flag, dropping it now!"));
   635            in_flags &= ~PR_POLL_READ;
   636         }
   638         if (out_flags & PR_POLL_READ && buffers[s].areafree())
   639         {
   640           LOG_DEBUG((" :reading"));
   641           int32_t bytesRead = PR_Recv(sockets[s].fd, buffers[s].buffertail, 
   642               buffers[s].areafree(), 0, PR_INTERVAL_NO_TIMEOUT);
   644           if (bytesRead == 0)
   645           {
   646             LOG_DEBUG((" socket gracefully closed"));
   647             client_done = true;
   648             in_flags &= ~PR_POLL_READ;
   649           }
   650           else if (bytesRead < 0)
   651           {
   652             if (PR_GetError() != PR_WOULD_BLOCK_ERROR)
   653             {
   654               LOG_DEBUG((" error=%d", PR_GetError()));
   655               // We are in error state, indicate that the connection was 
   656               // not closed gracefully
   657               client_error = true;
   658               socketErrorState[s] = true;
   659               // Wipe out our send buffer, we cannot send it anyway.
   660               buffers[s2].bufferhead = buffers[s2].buffertail = buffers[s2].buffer;
   661             }
   662             else
   663               LOG_DEBUG((" would block"));
   664           }
   665           else
   666           {
   667             // If the other socket is in error state (unable to send/receive)
   668             // throw this data away and continue loop
   669             if (socketErrorState[s2])
   670             {
   671               LOG_DEBUG((" have read but other socket is in error state\n"));
   672               continue;
   673             }
   675             buffers[s].buffertail += bytesRead;
   676             LOG_DEBUG((", read %d bytes", bytesRead));
   678             // We have to accept and handle the initial CONNECT request here
   679             int32_t response;
   680             if (!connect_accepted && ReadConnectRequest(ci->server_info, buffers[s],
   681                 &response, certificateToUse, &clientAuth, fullHost, locationHeader))
   682             {
   683               // Mark this as a proxy-only connection (no SSL) if the CONNECT
   684               // request didn't come for port 443 or from any of the server's
   685               // cert or clientauth hostnames.
   686               if (fullHost.find(":443") == string::npos)
   687               {
   688                 server_match_t match;
   689                 match.fullHost = fullHost;
   690                 match.matched = false;
   691                 PL_HashTableEnumerateEntries(ci->server_info->host_cert_table, 
   692                                              match_hostname, 
   693                                              &match);
   694                 PL_HashTableEnumerateEntries(ci->server_info->host_clientauth_table, 
   695                                              match_hostname, 
   696                                              &match);
   697                 ci->http_proxy_only = !match.matched;
   698               }
   699               else
   700               {
   701                 ci->http_proxy_only = false;
   702               }
   704               // Clean the request as it would be read
   705               buffers[s].bufferhead = buffers[s].buffertail = buffers[s].buffer;
   706               in_flags |= PR_POLL_WRITE;
   707               connect_accepted = true;
   709               // Store response to the oposite buffer
   710               if (response == 200)
   711               {
   712                   LOG_DEBUG((" accepted CONNECT request, connected to the server, sending OK to the client\n"));
   713                   strcpy(buffers[s2].buffer, "HTTP/1.1 200 Connected\r\nConnection: keep-alive\r\n\r\n");
   714               }
   715               else if (response == 302)
   716               {
   717                   LOG_DEBUG((" accepted CONNECT request with redirection, "
   718                              "sending location and 302 to the client\n"));
   719                   client_done = true;
   720                   sprintf(buffers[s2].buffer, 
   721                           "HTTP/1.1 302 Moved\r\n"
   722                           "Location: https://%s/\r\n"
   723                           "Connection: close\r\n\r\n",
   724                           locationHeader.c_str());
   725               }
   726               else
   727               {
   728                 LOG_ERRORD((" could not read the connect request, closing connection with %d", response));
   729                 client_done = true;
   730                 sprintf(buffers[s2].buffer, "HTTP/1.1 %d ERROR\r\nConnection: close\r\n\r\n", response);
   732                 break;
   733               }
   735               buffers[s2].buffertail = buffers[s2].buffer + strlen(buffers[s2].buffer);
   737               // Send the response to the client socket
   738               break;
   739             } // end of CONNECT handling
   741             if (!buffers[s].areafree())
   742             {
   743               // Do not poll for read when the buffer is full
   744               LOG_DEBUG((" no place in our read buffer, stop reading"));
   745               in_flags &= ~PR_POLL_READ;
   746             }
   748             if (ssl_updated)
   749             {
   750               if (s == 0 && expect_request_start) 
   751               {
   752                 if (!strstr(buffers[s].bufferhead, "\r\n\r\n"))
   753                 {
   754                   // We haven't received the complete header yet, so wait.
   755                   continue;
   756                 }
   757                 else
   758                 {
   759                   ci->iswebsocket = AdjustWebSocketHost(buffers[s], ci);
   760                   expect_request_start = !(ci->iswebsocket || 
   761                                            AdjustRequestURI(buffers[s], &fullHost));
   762                   PRNetAddr* addr = &remote_addr;
   763                   if (ci->iswebsocket && websocket_server.inet.port)
   764                     addr = &websocket_server;
   765                   if (!ConnectSocket(other_sock, addr, connect_timeout))
   766                   {
   767                     LOG_ERRORD((" could not open connection to the real server\n"));
   768                     client_error = true;
   769                     break;
   770                   }
   771                   LOG_DEBUG(("\n connected to remote server\n"));
   772                   numberOfSockets = 2;
   773                 }
   774               }
   775               else if (s == 1 && ci->iswebsocket)
   776               {
   777                 if (!AdjustWebSocketLocation(buffers[s], ci))
   778                   continue;
   779               }
   781               in_flags2 |= PR_POLL_WRITE;
   782               LOG_DEBUG((" telling the other socket to write"));
   783             }
   784             else
   785               LOG_DEBUG((" we have something for the other socket to write, but ssl has not been administered on it"));
   786           }
   787         } // PR_POLL_READ handling
   789         if (out_flags & PR_POLL_WRITE)
   790         {
   791           LOG_DEBUG((" :writing"));
   792           int32_t bytesWrite = PR_Send(sockets[s].fd, buffers[s2].bufferhead, 
   793               buffers[s2].present(), 0, PR_INTERVAL_NO_TIMEOUT);
   795           if (bytesWrite < 0)
   796           {
   797             if (PR_GetError() != PR_WOULD_BLOCK_ERROR) {
   798               LOG_DEBUG((" error=%d", PR_GetError()));
   799               client_error = true;
   800               socketErrorState[s] = true;
   801               // We got a fatal error while writting the buffer. Clear it to break
   802               // the main loop, we will never more be able to send it.
   803               buffers[s2].bufferhead = buffers[s2].buffertail = buffers[s2].buffer;
   804             }
   805             else
   806               LOG_DEBUG((" would block"));
   807           }
   808           else
   809           {
   810             LOG_DEBUG((", written %d bytes", bytesWrite));
   811             buffers[s2].buffertail[1] = '\0';
   812             LOG_DEBUG((" dump:\n%.*s\n", bytesWrite, buffers[s2].bufferhead));
   814             buffers[s2].bufferhead += bytesWrite;
   815             if (buffers[s2].present())
   816             {
   817               LOG_DEBUG((" still have to write %d bytes", (int)buffers[s2].present()));
   818               in_flags |= PR_POLL_WRITE;
   819             }              
   820             else
   821             {
   822               if (!ssl_updated)
   823               {
   824                 LOG_DEBUG((" proxy response sent to the client"));
   825                 // Proxy response has just been writen, update to ssl
   826                 ssl_updated = true;
   827                 if (ci->http_proxy_only)
   828                 {
   829                   LOG_DEBUG((" not updating to SSL based on http_proxy_only for this socket"));
   830                 }
   831                 else if (!ConfigureSSLServerSocket(ci->client_sock, ci->server_info, 
   832                                                    certificateToUse, clientAuth))
   833                 {
   834                   LOG_ERRORD((" failed to config server socket\n"));
   835                   client_error = true;
   836                   break;
   837                 }
   838                 else
   839                 {
   840                   LOG_DEBUG((" client socket updated to SSL"));
   841                 }
   842               } // sslUpdate
   844               LOG_DEBUG((" dropping our write flag and setting other socket read flag"));
   845               in_flags &= ~PR_POLL_WRITE;
   846               in_flags2 |= PR_POLL_READ;
   847               buffers[s2].compact();
   848             }
   849           }
   850         } // PR_POLL_WRITE handling
   851         LOG_END_BLOCK(); // end the log
   852       } // for...
   853     } // while, poll
   854   }
   855   else
   856     client_error = true;
   858   LOG_DEBUG(("SSLTUNNEL(%p)): exiting root function for csock=%p, ssock=%p\n",
   859              static_cast<void*>(data),
   860              static_cast<void*>(ci->client_sock),
   861              static_cast<void*>(other_sock)));
   862   if (!client_error)
   863     PR_Shutdown(ci->client_sock, PR_SHUTDOWN_SEND);
   864   PR_Close(ci->client_sock);
   866   delete ci;
   867 }
   869 /*
   870  * Start listening for SSL connections on a specified port, handing
   871  * them off to client threads after accepting the connection.
   872  * The data parameter is a server_info_t*, owned by the calling
   873  * function.
   874  */
   875 void StartServer(void* data)
   876 {
   877   server_info_t* si = static_cast<server_info_t*>(data);
   879   //TODO: select ciphers?
   880   ScopedPRFileDesc listen_socket(PR_NewTCPSocket());
   881   if (!listen_socket) {
   882     LOG_ERROR(("failed to create socket\n"));
   883     SignalShutdown();
   884     return;
   885   }
   887   // In case the socket is still open in the TIME_WAIT state from a previous
   888   // instance of ssltunnel we ask to reuse the port.
   889   PRSocketOptionData socket_option;
   890   socket_option.option = PR_SockOpt_Reuseaddr;
   891   socket_option.value.reuse_addr = true;
   892   PR_SetSocketOption(listen_socket, &socket_option);
   894   PRNetAddr server_addr;
   895   PR_InitializeNetAddr(PR_IpAddrAny, si->listen_port, &server_addr);
   896   if (PR_Bind(listen_socket, &server_addr) != PR_SUCCESS) {
   897     LOG_ERROR(("failed to bind socket\n"));
   898     SignalShutdown();
   899     return;
   900   }
   902   if (PR_Listen(listen_socket, 1) != PR_SUCCESS) {
   903     LOG_ERROR(("failed to listen on socket\n"));
   904     SignalShutdown();
   905     return;
   906   }
   908   LOG_INFO(("Server listening on port %d with cert %s\n", si->listen_port,
   909          si->cert_nickname.c_str()));
   911   while (!shutdown_server) {
   912     connection_info_t* ci = new connection_info_t();
   913     ci->server_info = si;
   914     ci->http_proxy_only = do_http_proxy;
   915     // block waiting for connections
   916     ci->client_sock = PR_Accept(listen_socket, &ci->client_addr,
   917                                 PR_INTERVAL_NO_TIMEOUT);
   919     PRSocketOptionData option;
   920     option.option = PR_SockOpt_Nonblocking;
   921     option.value.non_blocking = true;
   922     PR_SetSocketOption(ci->client_sock, &option);
   924     if (ci->client_sock)
   925       // Not actually using this PRJob*...
   926       //PRJob* job =
   927       PR_QueueJob(threads, HandleConnection, ci, true);
   928     else
   929       delete ci;
   930   }
   931 }
   933 // bogus password func, just don't use passwords. :-P
   934 char* password_func(PK11SlotInfo* slot, PRBool retry, void* arg)
   935 {
   936   if (retry)
   937     return nullptr;
   939   return PL_strdup("");
   940 }
   942 server_info_t* findServerInfo(int portnumber)
   943 {
   944   for (vector<server_info_t>::iterator it = servers.begin();
   945        it != servers.end(); it++) 
   946   {
   947     if (it->listen_port == portnumber)
   948       return &(*it);
   949   }
   951   return nullptr;
   952 }
   954 int processConfigLine(char* configLine)
   955 {
   956   if (*configLine == 0 || *configLine == '#')
   957     return 0;
   959   char* _caret;
   960   char* keyword = strtok2(configLine, ":", &_caret);
   962   // Configure usage of http/ssl tunneling proxy behavior
   963   if (!strcmp(keyword, "httpproxy"))
   964   {
   965     char* value = strtok2(_caret, ":", &_caret);
   966     if (!strcmp(value, "1"))
   967       do_http_proxy = true;
   969     return 0;
   970   }
   972   if (!strcmp(keyword, "websocketserver"))
   973   {
   974     char* ipstring = strtok2(_caret, ":", &_caret);
   975     if (PR_StringToNetAddr(ipstring, &websocket_server) != PR_SUCCESS) {
   976       LOG_ERROR(("Invalid IP address in proxy config: %s\n", ipstring));
   977       return 1;
   978     }
   979     char* remoteport = strtok2(_caret, ":", &_caret);
   980     int port = atoi(remoteport);
   981     if (port <= 0) {
   982       LOG_ERROR(("Invalid remote port in proxy config: %s\n", remoteport));
   983       return 1;
   984     }
   985     websocket_server.inet.port = PR_htons(port);
   986     return 0;
   987   }
   989   // Configure the forward address of the target server
   990   if (!strcmp(keyword, "forward"))
   991   {
   992     char* ipstring = strtok2(_caret, ":", &_caret);
   993     if (PR_StringToNetAddr(ipstring, &remote_addr) != PR_SUCCESS) {
   994       LOG_ERROR(("Invalid remote IP address: %s\n", ipstring));
   995       return 1;
   996     }
   997     char* serverportstring = strtok2(_caret, ":", &_caret);
   998     int port = atoi(serverportstring);
   999     if (port <= 0) {
  1000       LOG_ERROR(("Invalid remote port: %s\n", serverportstring));
  1001       return 1;
  1003     remote_addr.inet.port = PR_htons(port);
  1005     return 0;
  1008   // Configure all listen sockets and port+certificate bindings
  1009   if (!strcmp(keyword, "listen"))
  1011     char* hostname = strtok2(_caret, ":", &_caret);
  1012     char* hostportstring = nullptr;
  1013     if (strcmp(hostname, "*"))
  1015       any_host_spec_config = true;
  1016       hostportstring = strtok2(_caret, ":", &_caret);
  1019     char* serverportstring = strtok2(_caret, ":", &_caret);
  1020     char* certnick = strtok2(_caret, ":", &_caret);
  1022     int port = atoi(serverportstring);
  1023     if (port <= 0) {
  1024       LOG_ERROR(("Invalid port specified: %s\n", serverportstring));
  1025       return 1;
  1028     if (server_info_t* existingServer = findServerInfo(port))
  1030       char *certnick_copy = new char[strlen(certnick)+1];
  1031       char *hostname_copy = new char[strlen(hostname)+strlen(hostportstring)+2];
  1033       strcpy(hostname_copy, hostname);
  1034       strcat(hostname_copy, ":");
  1035       strcat(hostname_copy, hostportstring);
  1036       strcpy(certnick_copy, certnick);
  1038       PLHashEntry* entry = PL_HashTableAdd(existingServer->host_cert_table, hostname_copy, certnick_copy);
  1039       if (!entry) {
  1040         LOG_ERROR(("Out of memory"));
  1041         return 1;
  1044     else
  1046       server_info_t server;
  1047       server.cert_nickname = certnick;
  1048       server.listen_port = port;
  1049       server.host_cert_table = PL_NewHashTable(0, PL_HashString, PL_CompareStrings,
  1050                                                PL_CompareStrings, nullptr, nullptr);
  1051       if (!server.host_cert_table)
  1053         LOG_ERROR(("Internal, could not create hash table\n"));
  1054         return 1;
  1056       server.host_clientauth_table = PL_NewHashTable(0, PL_HashString, PL_CompareStrings,
  1057                                                      ClientAuthValueComparator, nullptr, nullptr);
  1058       if (!server.host_clientauth_table)
  1060         LOG_ERROR(("Internal, could not create hash table\n"));
  1061         return 1;
  1063       server.host_redir_table = PL_NewHashTable(0, PL_HashString, PL_CompareStrings,
  1064                                                 PL_CompareStrings, nullptr, nullptr);
  1065       if (!server.host_redir_table)
  1067         LOG_ERROR(("Internal, could not create hash table\n"));
  1068         return 1;
  1070       servers.push_back(server);
  1073     return 0;
  1076   if (!strcmp(keyword, "clientauth"))
  1078     char* hostname = strtok2(_caret, ":", &_caret);
  1079     char* hostportstring = strtok2(_caret, ":", &_caret);
  1080     char* serverportstring = strtok2(_caret, ":", &_caret);
  1082     int port = atoi(serverportstring);
  1083     if (port <= 0) {
  1084       LOG_ERROR(("Invalid port specified: %s\n", serverportstring));
  1085       return 1;
  1088     if (server_info_t* existingServer = findServerInfo(port))
  1090       char* authoptionstring = strtok2(_caret, ":", &_caret);
  1091       client_auth_option* authoption = new client_auth_option;
  1092       if (!authoption) {
  1093         LOG_ERROR(("Out of memory"));
  1094         return 1;
  1097       if (!strcmp(authoptionstring, "require"))
  1098         *authoption = caRequire;
  1099       else if (!strcmp(authoptionstring, "request"))
  1100         *authoption = caRequest;
  1101       else if (!strcmp(authoptionstring, "none"))
  1102         *authoption = caNone;
  1103       else
  1105         LOG_ERROR(("Incorrect client auth option modifier for host '%s'", hostname));
  1106         return 1;
  1109       any_host_spec_config = true;
  1111       char *hostname_copy = new char[strlen(hostname)+strlen(hostportstring)+2];
  1112       if (!hostname_copy) {
  1113         LOG_ERROR(("Out of memory"));
  1114         return 1;
  1117       strcpy(hostname_copy, hostname);
  1118       strcat(hostname_copy, ":");
  1119       strcat(hostname_copy, hostportstring);
  1121       PLHashEntry* entry = PL_HashTableAdd(existingServer->host_clientauth_table, hostname_copy, authoption);
  1122       if (!entry) {
  1123         LOG_ERROR(("Out of memory"));
  1124         return 1;
  1127     else
  1129       LOG_ERROR(("Server on port %d for client authentication option is not defined, use 'listen' option first", port));
  1130       return 1;
  1133     return 0;
  1136   if (!strcmp(keyword, "redirhost"))
  1138     char* hostname = strtok2(_caret, ":", &_caret);
  1139     char* hostportstring = strtok2(_caret, ":", &_caret);
  1140     char* serverportstring = strtok2(_caret, ":", &_caret);
  1142     int port = atoi(serverportstring);
  1143     if (port <= 0) {
  1144       LOG_ERROR(("Invalid port specified: %s\n", serverportstring));
  1145       return 1;
  1148     if (server_info_t* existingServer = findServerInfo(port))
  1150       char* redirhoststring = strtok2(_caret, ":", &_caret);
  1152       any_host_spec_config = true;
  1154       char *hostname_copy = new char[strlen(hostname)+strlen(hostportstring)+2];
  1155       if (!hostname_copy) {
  1156         LOG_ERROR(("Out of memory"));
  1157         return 1;
  1160       strcpy(hostname_copy, hostname);
  1161       strcat(hostname_copy, ":");
  1162       strcat(hostname_copy, hostportstring);
  1164       char *redir_copy = new char[strlen(redirhoststring)+1];
  1165       strcpy(redir_copy, redirhoststring);
  1166       PLHashEntry* entry = PL_HashTableAdd(existingServer->host_redir_table, hostname_copy, redir_copy);
  1167       if (!entry) {
  1168         LOG_ERROR(("Out of memory"));
  1169         return 1;
  1172     else
  1174       LOG_ERROR(("Server on port %d for redirhost option is not defined, use 'listen' option first", port));
  1175       return 1;
  1178     return 0;
  1181   // Configure the NSS certificate database directory
  1182   if (!strcmp(keyword, "certdbdir"))
  1184     nssconfigdir = strtok2(_caret, "\n", &_caret);
  1185     return 0;
  1188   LOG_ERROR(("Error: keyword \"%s\" unexpected\n", keyword));
  1189   return 1;
  1192 int parseConfigFile(const char* filePath)
  1194   FILE* f = fopen(filePath, "r");
  1195   if (!f)
  1196     return 1;
  1198   char buffer[1024], *b = buffer;
  1199   while (!feof(f))
  1201     char c;
  1202     fscanf(f, "%c", &c);
  1203     switch (c)
  1205     case '\n':
  1206       *b++ = 0;
  1207       if (processConfigLine(buffer))
  1208         return 1;
  1209       b = buffer;
  1210     case '\r':
  1211       continue;
  1212     default:
  1213       *b++ = c;
  1217   fclose(f);
  1219   // Check mandatory items
  1220   if (nssconfigdir.empty())
  1222     LOG_ERROR(("Error: missing path to NSS certification database\n,use certdbdir:<path> in the config file\n"));
  1223     return 1;
  1226   if (any_host_spec_config && !do_http_proxy)
  1228     LOG_ERROR(("Warning: any host-specific configurations are ignored, add httpproxy:1 to allow them\n"));
  1231   return 0;
  1234 int freeHostCertHashItems(PLHashEntry *he, int i, void *arg)
  1236   delete [] (char*)he->key;
  1237   delete [] (char*)he->value;
  1238   return HT_ENUMERATE_REMOVE;
  1241 int freeHostRedirHashItems(PLHashEntry *he, int i, void *arg)
  1243   delete [] (char*)he->key;
  1244   delete [] (char*)he->value;
  1245   return HT_ENUMERATE_REMOVE;
  1248 int freeClientAuthHashItems(PLHashEntry *he, int i, void *arg)
  1250   delete [] (char*)he->key;
  1251   delete (client_auth_option*)he->value;
  1252   return HT_ENUMERATE_REMOVE;
  1255 int main(int argc, char** argv)
  1257   const char* configFilePath;
  1259   const char* logLevelEnv = PR_GetEnv("SSLTUNNEL_LOG_LEVEL");
  1260   gLogLevel = logLevelEnv ? (LogLevel)atoi(logLevelEnv) : LEVEL_INFO;
  1262   if (argc == 1)
  1263     configFilePath = "ssltunnel.cfg";
  1264   else
  1265     configFilePath = argv[1];
  1267   memset(&websocket_server, 0, sizeof(PRNetAddr));
  1269   if (parseConfigFile(configFilePath)) {
  1270     LOG_ERROR(("Error: config file \"%s\" missing or formating incorrect\n"
  1271       "Specify path to the config file as parameter to ssltunnel or \n"
  1272       "create ssltunnel.cfg in the working directory.\n\n"
  1273       "Example format of the config file:\n\n"
  1274       "       # Enable http/ssl tunneling proxy-like behavior.\n"
  1275       "       # If not specified ssltunnel simply does direct forward.\n"
  1276       "       httpproxy:1\n\n"
  1277       "       # Specify path to the certification database used.\n"
  1278       "       certdbdir:/path/to/certdb\n\n"
  1279       "       # Forward/proxy all requests in raw to 127.0.0.1:8888.\n"
  1280       "       forward:127.0.0.1:8888\n\n"
  1281       "       # Accept connections on port 4443 or 5678 resp. and authenticate\n"
  1282       "       # to any host ('*') using the 'server cert' or 'server cert 2' resp.\n"
  1283       "       listen:*:4443:server cert\n"
  1284       "       listen:*:5678:server cert 2\n\n"
  1285       "       # Accept connections on port 4443 and authenticate using\n"
  1286       "       # 'a different cert' when target host is 'my.host.name:443'.\n"
  1287       "       # This only works in httpproxy mode and has higher priority\n"
  1288       "       # than the previous option.\n"
  1289       "       listen:my.host.name:443:4443:a different cert\n\n"
  1290       "       # To make a specific host require or just request a client certificate\n"
  1291       "       # to authenticate use the following options. This can only be used\n"
  1292       "       # in httpproxy mode and only after the 'listen' option has been\n"
  1293       "       # specified. You also have to specify the tunnel listen port.\n"
  1294       "       clientauth:requesting-client-cert.host.com:443:4443:request\n"
  1295       "       clientauth:requiring-client-cert.host.com:443:4443:require\n"
  1296       "       # Proxy WebSocket traffic to the server at 127.0.0.1:9999,\n"
  1297       "       # instead of the server specified in the 'forward' option.\n"
  1298       "       websocketserver:127.0.0.1:9999\n",
  1299       configFilePath));
  1300     return 1;
  1303   // create a thread pool to handle connections
  1304   threads = PR_CreateThreadPool(INITIAL_THREADS * servers.size(),
  1305                                 MAX_THREADS * servers.size(),
  1306                                 DEFAULT_STACKSIZE);
  1307   if (!threads) {
  1308     LOG_ERROR(("Failed to create thread pool\n"));
  1309     return 1;
  1312   shutdown_lock = PR_NewLock();
  1313   if (!shutdown_lock) {
  1314     LOG_ERROR(("Failed to create lock\n"));
  1315     PR_ShutdownThreadPool(threads);
  1316     return 1;
  1318   shutdown_condvar = PR_NewCondVar(shutdown_lock);
  1319   if (!shutdown_condvar) {
  1320     LOG_ERROR(("Failed to create condvar\n"));
  1321     PR_ShutdownThreadPool(threads);
  1322     PR_DestroyLock(shutdown_lock);
  1323     return 1;
  1326   PK11_SetPasswordFunc(password_func);
  1328   // Initialize NSS
  1329   if (NSS_Init(nssconfigdir.c_str()) != SECSuccess) {
  1330     int32_t errorlen = PR_GetErrorTextLength();
  1331     char* err = new char[errorlen+1];
  1332     PR_GetErrorText(err);
  1333     LOG_ERROR(("Failed to init NSS: %s", err));
  1334     delete[] err;
  1335     PR_ShutdownThreadPool(threads);
  1336     PR_DestroyCondVar(shutdown_condvar);
  1337     PR_DestroyLock(shutdown_lock);
  1338     return 1;
  1341   if (NSS_SetDomesticPolicy() != SECSuccess) {
  1342     LOG_ERROR(("NSS_SetDomesticPolicy failed\n"));
  1343     PR_ShutdownThreadPool(threads);
  1344     PR_DestroyCondVar(shutdown_condvar);
  1345     PR_DestroyLock(shutdown_lock);
  1346     NSS_Shutdown();
  1347     return 1;
  1350   // these values should make NSS use the defaults
  1351   if (SSL_ConfigServerSessionIDCache(0, 0, 0, nullptr) != SECSuccess) {
  1352     LOG_ERROR(("SSL_ConfigServerSessionIDCache failed\n"));
  1353     PR_ShutdownThreadPool(threads);
  1354     PR_DestroyCondVar(shutdown_condvar);
  1355     PR_DestroyLock(shutdown_lock);
  1356     NSS_Shutdown();
  1357     return 1;
  1360   for (vector<server_info_t>::iterator it = servers.begin();
  1361        it != servers.end(); it++) {
  1362     // Not actually using this PRJob*...
  1363     // PRJob* server_job =
  1364     PR_QueueJob(threads, StartServer, &(*it), true);
  1366   // now wait for someone to tell us to quit
  1367   PR_Lock(shutdown_lock);
  1368   PR_WaitCondVar(shutdown_condvar, PR_INTERVAL_NO_TIMEOUT);
  1369   PR_Unlock(shutdown_lock);
  1370   shutdown_server = true;
  1371   LOG_INFO(("Shutting down...\n"));
  1372   // cleanup
  1373   PR_ShutdownThreadPool(threads);
  1374   PR_JoinThreadPool(threads);
  1375   PR_DestroyCondVar(shutdown_condvar);
  1376   PR_DestroyLock(shutdown_lock);
  1377   if (NSS_Shutdown() == SECFailure) {
  1378     LOG_DEBUG(("Leaked NSS objects!\n"));
  1381   for (vector<server_info_t>::iterator it = servers.begin();
  1382        it != servers.end(); it++) 
  1384     PL_HashTableEnumerateEntries(it->host_cert_table, freeHostCertHashItems, nullptr);
  1385     PL_HashTableEnumerateEntries(it->host_clientauth_table, freeClientAuthHashItems, nullptr);
  1386     PL_HashTableEnumerateEntries(it->host_redir_table, freeHostRedirHashItems, nullptr);
  1387     PL_HashTableDestroy(it->host_cert_table);
  1388     PL_HashTableDestroy(it->host_clientauth_table);
  1389     PL_HashTableDestroy(it->host_redir_table);
  1392   PR_Cleanup();
  1393   return 0;

mercurial