tor-browser

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

httpserv.c (45886B)


      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 <stdio.h>
      6 #include <string.h>
      7 
      8 #include "secutil.h"
      9 
     10 #if defined(XP_UNIX)
     11 #include <unistd.h>
     12 #endif
     13 
     14 #if defined(_WINDOWS)
     15 #include <process.h> /* for getpid() */
     16 #endif
     17 
     18 #include <signal.h>
     19 #include <stdlib.h>
     20 #include <errno.h>
     21 #include <fcntl.h>
     22 #include <stdarg.h>
     23 
     24 #include "nspr.h"
     25 #include "prio.h"
     26 #include "prerror.h"
     27 #include "prnetdb.h"
     28 #include "prclist.h"
     29 #include "plgetopt.h"
     30 #include "pk11func.h"
     31 #include "nss.h"
     32 #include "nssb64.h"
     33 #include "sechash.h"
     34 #include "cert.h"
     35 #include "certdb.h"
     36 #include "ocsp.h"
     37 #include "ocspti.h"
     38 #include "ocspi.h"
     39 
     40 #ifndef PORT_Strstr
     41 #define PORT_Strstr strstr
     42 #endif
     43 
     44 #ifndef PORT_Malloc
     45 #define PORT_Malloc PR_Malloc
     46 #endif
     47 
     48 static int handle_connection(PRFileDesc *, PRFileDesc *, int);
     49 
     50 /* data and structures for shutdown */
     51 static int stopping;
     52 
     53 static PRBool noDelay;
     54 static int verbose;
     55 
     56 static PRThread *acceptorThread;
     57 
     58 static PRLogModuleInfo *lm;
     59 
     60 #define PRINTF   \
     61    if (verbose) \
     62    printf
     63 #define FPRINTF  \
     64    if (verbose) \
     65    fprintf
     66 #define FLUSH           \
     67    if (verbose) {      \
     68        fflush(stdout); \
     69        fflush(stderr); \
     70    }
     71 #define VLOG(arg) PR_LOG(lm, PR_LOG_DEBUG, arg)
     72 
     73 static void
     74 Usage(const char *progName)
     75 {
     76    fprintf(stderr,
     77 
     78            "Usage: %s -p port [-Dbv]\n"
     79            "         [-t threads] [-i pid_file]\n"
     80            "         [-A nickname -C crl-filename]... [-O method]\n"
     81            "         [-d dbdir] [-f password_file] [-w password] [-P dbprefix]\n"
     82            "-D means disable Nagle delays in TCP\n"
     83            "-b means try binding to the port and exit\n"
     84            "-v means verbose output\n"
     85            "-t threads -- specify the number of threads to use for connections.\n"
     86            "-i pid_file file to write the process id of httpserv\n"
     87            "Parameters -A, -C and -O are used to provide an OCSP server at /ocsp?\n"
     88            "-A a nickname of a CA certificate\n"
     89            "-C a CRL filename corresponding to the preceding CA nickname\n"
     90            "-O allowed HTTP methods for OCSP requests: get, post, all, random, get-unknown\n"
     91            "   random means: randomly fail if request method is GET, POST always works\n"
     92            "   get-unknown means: status unknown for GET, correct status for POST\n"
     93            "Multiple pairs of parameters -A and -C are allowed.\n"
     94            "If status for a cert from an unknown CA is requested, the cert from the\n"
     95            "first -A parameter will be used to sign the unknown status response.\n"
     96            "NSS database parameters are used only if OCSP parameters are used.\n",
     97            progName);
     98 }
     99 
    100 static const char *
    101 errWarn(char *funcString)
    102 {
    103    PRErrorCode perr = PR_GetError();
    104    const char *errString = SECU_Strerror(perr);
    105 
    106    fprintf(stderr, "httpserv: %s returned error %d:\n%s\n",
    107            funcString, perr, errString);
    108    return errString;
    109 }
    110 
    111 static void
    112 errExit(char *funcString)
    113 {
    114    errWarn(funcString);
    115    exit(3);
    116 }
    117 
    118 #define MAX_VIRT_SERVER_NAME_ARRAY_INDEX 10
    119 
    120 /**************************************************************************
    121 ** Begin thread management routines and data.
    122 **************************************************************************/
    123 #define MIN_THREADS 3
    124 #define DEFAULT_THREADS 8
    125 #define MAX_THREADS 4096
    126 #define MAX_PROCS 25
    127 static int maxThreads = DEFAULT_THREADS;
    128 
    129 typedef struct jobStr {
    130    PRCList link;
    131    PRFileDesc *tcp_sock;
    132    PRFileDesc *model_sock;
    133    int requestCert;
    134 } JOB;
    135 
    136 static PZLock *qLock;             /* this lock protects all data immediately below */
    137 static PRLock *lastLoadedCrlLock; /* this lock protects lastLoadedCrl variable */
    138 static PZCondVar *jobQNotEmptyCv;
    139 static PZCondVar *freeListNotEmptyCv;
    140 static PZCondVar *threadCountChangeCv;
    141 static int threadCount;
    142 static PRCList jobQ;
    143 static PRCList freeJobs;
    144 static JOB *jobTable;
    145 
    146 SECStatus
    147 setupJobs(int maxJobs)
    148 {
    149    int i;
    150 
    151    jobTable = (JOB *)PR_Calloc(maxJobs, sizeof(JOB));
    152    if (!jobTable)
    153        return SECFailure;
    154 
    155    PR_INIT_CLIST(&jobQ);
    156    PR_INIT_CLIST(&freeJobs);
    157 
    158    for (i = 0; i < maxJobs; ++i) {
    159        JOB *pJob = jobTable + i;
    160        PR_APPEND_LINK(&pJob->link, &freeJobs);
    161    }
    162    return SECSuccess;
    163 }
    164 
    165 typedef int startFn(PRFileDesc *a, PRFileDesc *b, int c);
    166 
    167 typedef enum { rs_idle = 0,
    168               rs_running = 1,
    169               rs_zombie = 2 } runState;
    170 
    171 typedef struct perThreadStr {
    172    PRFileDesc *a;
    173    PRFileDesc *b;
    174    int c;
    175    int rv;
    176    startFn *startFunc;
    177    PRThread *prThread;
    178    runState state;
    179 } perThread;
    180 
    181 static perThread *threads;
    182 
    183 void
    184 thread_wrapper(void *arg)
    185 {
    186    perThread *slot = (perThread *)arg;
    187 
    188    slot->rv = (*slot->startFunc)(slot->a, slot->b, slot->c);
    189 
    190    /* notify the thread exit handler. */
    191    PZ_Lock(qLock);
    192    slot->state = rs_zombie;
    193    --threadCount;
    194    PZ_NotifyAllCondVar(threadCountChangeCv);
    195    PZ_Unlock(qLock);
    196 }
    197 
    198 int
    199 jobLoop(PRFileDesc *a, PRFileDesc *b, int c)
    200 {
    201    PRCList *myLink = 0;
    202    JOB *myJob;
    203 
    204    PZ_Lock(qLock);
    205    do {
    206        myLink = 0;
    207        while (PR_CLIST_IS_EMPTY(&jobQ) && !stopping) {
    208            PZ_WaitCondVar(jobQNotEmptyCv, PR_INTERVAL_NO_TIMEOUT);
    209        }
    210        if (!PR_CLIST_IS_EMPTY(&jobQ)) {
    211            myLink = PR_LIST_HEAD(&jobQ);
    212            PR_REMOVE_AND_INIT_LINK(myLink);
    213        }
    214        PZ_Unlock(qLock);
    215        myJob = (JOB *)myLink;
    216        /* myJob will be null when stopping is true and jobQ is empty */
    217        if (!myJob)
    218            break;
    219        handle_connection(myJob->tcp_sock, myJob->model_sock,
    220                          myJob->requestCert);
    221        PZ_Lock(qLock);
    222        PR_APPEND_LINK(myLink, &freeJobs);
    223        PZ_NotifyCondVar(freeListNotEmptyCv);
    224    } while (PR_TRUE);
    225    return 0;
    226 }
    227 
    228 SECStatus
    229 launch_threads(
    230    startFn *startFunc,
    231    PRFileDesc *a,
    232    PRFileDesc *b,
    233    int c,
    234    PRBool local)
    235 {
    236    int i;
    237    SECStatus rv = SECSuccess;
    238 
    239    /* create the thread management serialization structs */
    240    qLock = PZ_NewLock(nssILockSelfServ);
    241    jobQNotEmptyCv = PZ_NewCondVar(qLock);
    242    freeListNotEmptyCv = PZ_NewCondVar(qLock);
    243    threadCountChangeCv = PZ_NewCondVar(qLock);
    244 
    245    /* create monitor for crl reload procedure */
    246    lastLoadedCrlLock = PR_NewLock();
    247 
    248    /* allocate the array of thread slots */
    249    threads = PR_Calloc(maxThreads, sizeof(perThread));
    250    if (NULL == threads) {
    251        fprintf(stderr, "Oh Drat! Can't allocate the perThread array\n");
    252        return SECFailure;
    253    }
    254    /* 5 is a little extra, intended to keep the jobQ from underflowing.
    255    ** That is, from going empty while not stopping and clients are still
    256    ** trying to contact us.
    257    */
    258    rv = setupJobs(maxThreads + 5);
    259    if (rv != SECSuccess)
    260        return rv;
    261 
    262    PZ_Lock(qLock);
    263    for (i = 0; i < maxThreads; ++i) {
    264        perThread *slot = threads + i;
    265 
    266        slot->state = rs_running;
    267        slot->a = a;
    268        slot->b = b;
    269        slot->c = c;
    270        slot->startFunc = startFunc;
    271        slot->prThread = PR_CreateThread(PR_USER_THREAD,
    272                                         thread_wrapper, slot, PR_PRIORITY_NORMAL,
    273                                         (PR_TRUE ==
    274                                          local)
    275                                             ? PR_LOCAL_THREAD
    276                                             : PR_GLOBAL_THREAD,
    277                                         PR_JOINABLE_THREAD, 0);
    278        if (slot->prThread == NULL) {
    279            printf("httpserv: Failed to launch thread!\n");
    280            slot->state = rs_idle;
    281            rv = SECFailure;
    282            break;
    283        }
    284 
    285        ++threadCount;
    286    }
    287    PZ_Unlock(qLock);
    288 
    289    return rv;
    290 }
    291 
    292 #define DESTROY_CONDVAR(name)    \
    293    if (name) {                  \
    294        PZ_DestroyCondVar(name); \
    295        name = NULL;             \
    296    }
    297 #define DESTROY_LOCK(name)    \
    298    if (name) {               \
    299        PZ_DestroyLock(name); \
    300        name = NULL;          \
    301    }
    302 
    303 void
    304 terminateWorkerThreads(void)
    305 {
    306    int i;
    307 
    308    VLOG(("httpserv: server_thread: waiting on stopping"));
    309    PZ_Lock(qLock);
    310    PZ_NotifyAllCondVar(jobQNotEmptyCv);
    311    PZ_Unlock(qLock);
    312 
    313    /* Wait for worker threads to terminate. */
    314    for (i = 0; i < maxThreads; ++i) {
    315        perThread *slot = threads + i;
    316        if (slot->prThread) {
    317            PR_JoinThread(slot->prThread);
    318        }
    319    }
    320 
    321    /* The worker threads empty the jobQ before they terminate. */
    322    PZ_Lock(qLock);
    323    PORT_Assert(threadCount == 0);
    324    PORT_Assert(PR_CLIST_IS_EMPTY(&jobQ));
    325    PZ_Unlock(qLock);
    326 
    327    DESTROY_CONDVAR(jobQNotEmptyCv);
    328    DESTROY_CONDVAR(freeListNotEmptyCv);
    329    DESTROY_CONDVAR(threadCountChangeCv);
    330 
    331    PR_DestroyLock(lastLoadedCrlLock);
    332    DESTROY_LOCK(qLock);
    333    PR_Free(jobTable);
    334    PR_Free(threads);
    335 }
    336 
    337 /**************************************************************************
    338 ** End   thread management routines.
    339 **************************************************************************/
    340 
    341 PRBool NoReuse = PR_FALSE;
    342 PRBool disableLocking = PR_FALSE;
    343 static secuPWData pwdata = { PW_NONE, 0 };
    344 
    345 struct caRevoInfoStr {
    346    PRCList link;
    347    char *nickname;
    348    char *crlFilename;
    349    CERTCertificate *cert;
    350    CERTOCSPCertID *id;
    351    CERTSignedCrl *crl;
    352 };
    353 typedef struct caRevoInfoStr caRevoInfo;
    354 /* Created during app init. No locks necessary,
    355 * because later on, only read access will occur. */
    356 static caRevoInfo *caRevoInfos = NULL;
    357 
    358 static enum {
    359    ocspGetOnly,
    360    ocspPostOnly,
    361    ocspGetAndPost,
    362    ocspRandomGetFailure,
    363    ocspGetUnknown
    364 } ocspMethodsAllowed = ocspGetAndPost;
    365 
    366 static const char stopCmd[] = { "GET /stop " };
    367 static const char getCmd[] = { "GET " };
    368 static const char outHeader[] = {
    369    "HTTP/1.0 200 OK\r\n"
    370    "Server: Generic Web Server\r\n"
    371    "Date: Tue, 26 Aug 1997 22:10:05 GMT\r\n"
    372    "Content-type: text/plain\r\n"
    373    "\r\n"
    374 };
    375 static const char outOcspHeader[] = {
    376    "HTTP/1.0 200 OK\r\n"
    377    "Server: Generic OCSP Server\r\n"
    378    "Content-type: application/ocsp-response\r\n"
    379    "\r\n"
    380 };
    381 static const char outBadRequestHeader[] = {
    382    "HTTP/1.0 400 Bad Request\r\n"
    383    "Server: Generic OCSP Server\r\n"
    384    "\r\n"
    385 };
    386 
    387 void
    388 stop_server()
    389 {
    390    stopping = 1;
    391    PR_Interrupt(acceptorThread);
    392    PZ_TraceFlush();
    393 }
    394 
    395 /* Will only work if the original input to url encoding was
    396 * a base64 encoded buffer. Will only decode the sequences used
    397 * for encoding the special base64 characters, and fail if any
    398 * other encoded chars are found.
    399 * Will return SECSuccess if input could be processed.
    400 * Coversion is done in place.
    401 */
    402 static SECStatus
    403 urldecode_base64chars_inplace(char *buf)
    404 {
    405    char *walk;
    406    size_t remaining_bytes;
    407 
    408    if (!buf || !*buf)
    409        return SECFailure;
    410 
    411    walk = buf;
    412    remaining_bytes = strlen(buf) + 1; /* include terminator */
    413 
    414    while (*walk) {
    415        if (*walk == '%') {
    416            if (!PL_strncasecmp(walk, "%2B", 3)) {
    417                *walk = '+';
    418            } else if (!PL_strncasecmp(walk, "%2F", 3)) {
    419                *walk = '/';
    420            } else if (!PL_strncasecmp(walk, "%3D", 3)) {
    421                *walk = '=';
    422            } else {
    423                return SECFailure;
    424            }
    425            remaining_bytes -= 3;
    426            ++walk;
    427            memmove(walk, walk + 2, remaining_bytes);
    428        } else {
    429            ++walk;
    430            --remaining_bytes;
    431        }
    432    }
    433    return SECSuccess;
    434 }
    435 
    436 int
    437 handle_connection(
    438    PRFileDesc *tcp_sock,
    439    PRFileDesc *model_sock,
    440    int requestCert)
    441 {
    442    PRFileDesc *ssl_sock = NULL;
    443    PRFileDesc *local_file_fd = NULL;
    444    char *pBuf; /* unused space at end of buf */
    445    const char *errString;
    446    PRStatus status;
    447    int bufRem;    /* unused bytes at end of buf */
    448    int bufDat;    /* characters received in buf */
    449    int newln = 0; /* # of consecutive newlns */
    450    int firstTime = 1;
    451    int reqLen;
    452    int rv;
    453    int numIOVs;
    454    PRSocketOptionData opt;
    455    PRIOVec iovs[16];
    456    char msgBuf[160];
    457    char buf[10240];
    458    char fileName[513];
    459    char *getData = NULL; /* inplace conversion */
    460    SECItem postData;
    461    PRBool isOcspRequest = PR_FALSE;
    462    PRBool isPost = PR_FALSE;
    463 
    464    postData.data = NULL;
    465    postData.len = 0;
    466 
    467    pBuf = buf;
    468    bufRem = sizeof buf;
    469 
    470    VLOG(("httpserv: handle_connection: starting"));
    471    opt.option = PR_SockOpt_Nonblocking;
    472    opt.value.non_blocking = PR_FALSE;
    473    PR_SetSocketOption(tcp_sock, &opt);
    474 
    475    VLOG(("httpserv: handle_connection: starting\n"));
    476    ssl_sock = tcp_sock;
    477 
    478    if (noDelay) {
    479        opt.option = PR_SockOpt_NoDelay;
    480        opt.value.no_delay = PR_TRUE;
    481        status = PR_SetSocketOption(ssl_sock, &opt);
    482        if (status != PR_SUCCESS) {
    483            errWarn("PR_SetSocketOption(PR_SockOpt_NoDelay, PR_TRUE)");
    484            if (ssl_sock) {
    485                PR_Close(ssl_sock);
    486            }
    487            return SECFailure;
    488        }
    489    }
    490 
    491    while (1) {
    492        const char *post;
    493        const char *foundStr = NULL;
    494        const char *tmp = NULL;
    495 
    496        newln = 0;
    497        reqLen = 0;
    498 
    499        rv = PR_Read(ssl_sock, pBuf, bufRem - 1);
    500        if (rv == 0 ||
    501            (rv < 0 && PR_END_OF_FILE_ERROR == PR_GetError())) {
    502            if (verbose)
    503                errWarn("HDX PR_Read hit EOF");
    504            break;
    505        }
    506        if (rv < 0) {
    507            errWarn("HDX PR_Read");
    508            goto cleanup;
    509        }
    510        /* NULL termination */
    511        pBuf[rv] = 0;
    512        if (firstTime) {
    513            firstTime = 0;
    514        }
    515 
    516        pBuf += rv;
    517        bufRem -= rv;
    518        bufDat = pBuf - buf;
    519        /* Parse the input, starting at the beginning of the buffer.
    520         * Stop when we detect two consecutive \n's (or \r\n's)
    521         * as this signifies the end of the GET or POST portion.
    522         * The posted data follows.
    523         */
    524        while (reqLen < bufDat && newln < 2) {
    525            int octet = buf[reqLen++];
    526            if (octet == '\n') {
    527                newln++;
    528            } else if (octet != '\r') {
    529                newln = 0;
    530            }
    531        }
    532 
    533        /* came to the end of the buffer, or second newln
    534         * If we didn't get an empty line (CRLFCRLF) then keep on reading.
    535         */
    536        if (newln < 2)
    537            continue;
    538 
    539        /* we're at the end of the HTTP request.
    540         * If the request is a POST, then there will be one more
    541         * line of data.
    542         * This parsing is a hack, but ok for SSL test purposes.
    543         */
    544        post = PORT_Strstr(buf, "POST ");
    545        if (!post || *post != 'P')
    546            break;
    547 
    548        postData.data = (void *)(buf + reqLen);
    549 
    550        tmp = "content-length: ";
    551        foundStr = PL_strcasestr(buf, tmp);
    552        if (foundStr) {
    553            int expectedPostLen;
    554            int havePostLen;
    555 
    556            expectedPostLen = atoi(foundStr + strlen(tmp));
    557            havePostLen = bufDat - reqLen;
    558            if (havePostLen >= expectedPostLen) {
    559                postData.len = expectedPostLen;
    560                break;
    561            }
    562        } else {
    563            /* use legacy hack */
    564            /* It's a post, so look for the next and final CR/LF. */
    565            while (reqLen < bufDat && newln < 3) {
    566                int octet = buf[reqLen++];
    567                if (octet == '\n') {
    568                    newln++;
    569                }
    570            }
    571            if (newln == 3)
    572                break;
    573        }
    574    } /* read loop */
    575 
    576    bufDat = pBuf - buf;
    577    if (bufDat)
    578        do { /* just close if no data */
    579            /* Have either (a) a complete get, (b) a complete post, (c) EOF */
    580            if (reqLen > 0) {
    581                PRBool isGetOrPost = PR_FALSE;
    582                unsigned skipChars = 0;
    583                isPost = PR_FALSE;
    584 
    585                if (!strncmp(buf, getCmd, sizeof getCmd - 1)) {
    586                    isGetOrPost = PR_TRUE;
    587                    skipChars = 4;
    588                } else if (!strncmp(buf, "POST ", 5)) {
    589                    isGetOrPost = PR_TRUE;
    590                    isPost = PR_TRUE;
    591                    skipChars = 5;
    592                }
    593 
    594                if (isGetOrPost) {
    595                    char *fnBegin = buf;
    596                    char *fnEnd;
    597                    char *fnstart = NULL;
    598                    PRFileInfo info;
    599 
    600                    fnBegin += skipChars;
    601 
    602                    fnEnd = strpbrk(fnBegin, " \r\n");
    603                    if (fnEnd) {
    604                        int fnLen = fnEnd - fnBegin;
    605                        if (fnLen < sizeof fileName) {
    606                            strncpy(fileName, fnBegin, fnLen);
    607                            fileName[fnLen] = 0; /* null terminate */
    608                            fnstart = fileName;
    609                            /* strip initial / because our root is the current directory*/
    610                            while (*fnstart && *fnstart == '/')
    611                                ++fnstart;
    612                        }
    613                    }
    614                    if (fnstart) {
    615                        if (!strncmp(fnstart, "ocsp", 4)) {
    616                            if (isPost) {
    617                                if (postData.data) {
    618                                    isOcspRequest = PR_TRUE;
    619                                }
    620                            } else {
    621                                if (!strncmp(fnstart, "ocsp/", 5)) {
    622                                    isOcspRequest = PR_TRUE;
    623                                    getData = fnstart + 5;
    624                                }
    625                            }
    626                        } else {
    627                            /* try to open the file named.
    628                             * If successful, then write it to the client.
    629                             */
    630                            status = PR_GetFileInfo(fnstart, &info);
    631                            if (status == PR_SUCCESS &&
    632                                info.type == PR_FILE_FILE &&
    633                                info.size >= 0) {
    634                                local_file_fd = PR_Open(fnstart, PR_RDONLY, 0);
    635                            }
    636                        }
    637                    }
    638                }
    639            }
    640 
    641            numIOVs = 0;
    642 
    643            iovs[numIOVs].iov_base = (char *)outHeader;
    644            iovs[numIOVs].iov_len = (sizeof(outHeader)) - 1;
    645            numIOVs++;
    646 
    647            if (isOcspRequest && caRevoInfos) {
    648                CERTOCSPRequest *request = NULL;
    649                PRBool failThisRequest = PR_FALSE;
    650                PLArenaPool *arena = NULL;
    651 
    652                if (ocspMethodsAllowed == ocspGetOnly && postData.len) {
    653                    failThisRequest = PR_TRUE;
    654                } else if (ocspMethodsAllowed == ocspPostOnly && getData) {
    655                    failThisRequest = PR_TRUE;
    656                } else if (ocspMethodsAllowed == ocspRandomGetFailure && getData) {
    657                    if (!(rand() % 2)) {
    658                        failThisRequest = PR_TRUE;
    659                    }
    660                }
    661 
    662                if (failThisRequest) {
    663                    PR_Write(ssl_sock, outBadRequestHeader, strlen(outBadRequestHeader));
    664                    break;
    665                }
    666                /* get is base64, post is binary.
    667                 * If we have base64, convert into the (empty) postData array.
    668                 */
    669                if (getData) {
    670                    if (urldecode_base64chars_inplace(getData) == SECSuccess) {
    671                        /* The code below can handle a NULL arena */
    672                        arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
    673                        NSSBase64_DecodeBuffer(arena, &postData, getData, strlen(getData));
    674                    }
    675                }
    676                if (postData.len) {
    677                    request = CERT_DecodeOCSPRequest(&postData);
    678                }
    679                if (arena) {
    680                    PORT_FreeArena(arena, PR_FALSE);
    681                    arena = NULL;
    682                }
    683                if (!request || !request->tbsRequest ||
    684                    !request->tbsRequest->requestList ||
    685                    !request->tbsRequest->requestList[0]) {
    686                    snprintf(msgBuf, sizeof(msgBuf), "Cannot decode OCSP request.\r\n");
    687 
    688                    iovs[numIOVs].iov_base = msgBuf;
    689                    iovs[numIOVs].iov_len = PORT_Strlen(msgBuf);
    690                    numIOVs++;
    691                } else {
    692                    /* TODO: support more than one request entry */
    693                    CERTOCSPCertID *reqid = request->tbsRequest->requestList[0]->reqCert;
    694                    const caRevoInfo *revoInfo = NULL;
    695                    PRBool unknown = PR_FALSE;
    696                    PRBool revoked = PR_FALSE;
    697                    PRTime nextUpdate = 0;
    698                    PRTime revoDate = 0;
    699                    PRCList *caRevoIter;
    700 
    701                    caRevoIter = &caRevoInfos->link;
    702                    do {
    703                        CERTOCSPCertID *caid;
    704 
    705                        revoInfo = (caRevoInfo *)caRevoIter;
    706                        caid = revoInfo->id;
    707 
    708                        if (SECOID_CompareAlgorithmID(&reqid->hashAlgorithm,
    709                                                      &caid->hashAlgorithm) == SECEqual &&
    710                            SECITEM_CompareItem(&reqid->issuerNameHash,
    711                                                &caid->issuerNameHash) == SECEqual &&
    712                            SECITEM_CompareItem(&reqid->issuerKeyHash,
    713                                                &caid->issuerKeyHash) == SECEqual) {
    714                            break;
    715                        }
    716                        revoInfo = NULL;
    717                        caRevoIter = PR_NEXT_LINK(caRevoIter);
    718                    } while (caRevoIter != &caRevoInfos->link);
    719 
    720                    if (!revoInfo) {
    721                        unknown = PR_TRUE;
    722                        revoInfo = caRevoInfos;
    723                    } else {
    724                        CERTCrl *crl = &revoInfo->crl->crl;
    725                        CERTCrlEntry *entry = NULL;
    726                        DER_DecodeTimeChoice(&nextUpdate, &crl->nextUpdate);
    727                        if (crl->entries) {
    728                            int iv = 0;
    729                            /* assign, not compare */
    730                            while ((entry = crl->entries[iv++])) {
    731                                if (SECITEM_CompareItem(&reqid->serialNumber,
    732                                                        &entry->serialNumber) == SECEqual) {
    733                                    break;
    734                                }
    735                            }
    736                        }
    737                        if (entry) {
    738                            /* revoked status response */
    739                            revoked = PR_TRUE;
    740                            DER_DecodeTimeChoice(&revoDate, &entry->revocationDate);
    741                        } else {
    742                            /* else good status response */
    743                            if (!isPost && ocspMethodsAllowed == ocspGetUnknown) {
    744                                unknown = PR_TRUE;
    745                                nextUpdate = PR_Now() + (PRTime)60 * 60 * 24 * PR_USEC_PER_SEC; /*tomorrow*/
    746                                revoDate = PR_Now() - (PRTime)60 * 60 * 24 * PR_USEC_PER_SEC;   /*yesterday*/
    747                            }
    748                        }
    749                    }
    750 
    751                    {
    752                        PRTime now = PR_Now();
    753                        CERTOCSPSingleResponse *sr;
    754                        CERTOCSPSingleResponse **singleResponses;
    755                        SECItem *ocspResponse;
    756 
    757                        PORT_Assert(!arena);
    758                        arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
    759 
    760                        if (unknown) {
    761                            sr = CERT_CreateOCSPSingleResponseUnknown(arena, reqid, now,
    762                                                                      &nextUpdate);
    763                        } else if (revoked) {
    764                            sr = CERT_CreateOCSPSingleResponseRevoked(arena, reqid, now,
    765                                                                      &nextUpdate, revoDate, NULL);
    766                        } else {
    767                            sr = CERT_CreateOCSPSingleResponseGood(arena, reqid, now,
    768                                                                   &nextUpdate);
    769                        }
    770 
    771                        /* meaning of value 2: one entry + one end marker */
    772                        singleResponses = PORT_ArenaNewArray(arena, CERTOCSPSingleResponse *, 2);
    773                        singleResponses[0] = sr;
    774                        singleResponses[1] = NULL;
    775                        ocspResponse = CERT_CreateEncodedOCSPSuccessResponse(arena,
    776                                                                             revoInfo->cert, ocspResponderID_byName, now,
    777                                                                             singleResponses, &pwdata);
    778 
    779                        if (!ocspResponse) {
    780                            snprintf(msgBuf, sizeof(msgBuf), "Failed to encode response\r\n");
    781                            iovs[numIOVs].iov_base = msgBuf;
    782                            iovs[numIOVs].iov_len = PORT_Strlen(msgBuf);
    783                            numIOVs++;
    784                        } else {
    785                            PR_Write(ssl_sock, outOcspHeader, strlen(outOcspHeader));
    786                            PR_Write(ssl_sock, ocspResponse->data, ocspResponse->len);
    787                        }
    788                        PORT_FreeArena(arena, PR_FALSE);
    789                    }
    790                    CERT_DestroyOCSPRequest(request);
    791                    break;
    792                }
    793            } else if (local_file_fd) {
    794                PRInt32 bytes;
    795                int errLen;
    796                bytes = PR_TransmitFile(ssl_sock, local_file_fd, outHeader,
    797                                        sizeof outHeader - 1,
    798                                        PR_TRANSMITFILE_KEEP_OPEN,
    799                                        PR_INTERVAL_NO_TIMEOUT);
    800                if (bytes >= 0) {
    801                    bytes -= sizeof outHeader - 1;
    802                    FPRINTF(stderr,
    803                            "httpserv: PR_TransmitFile wrote %d bytes from %s\n",
    804                            bytes, fileName);
    805                    break;
    806                }
    807                errString = errWarn("PR_TransmitFile");
    808                errLen = PORT_Strlen(errString);
    809                errLen = PR_MIN(errLen, sizeof msgBuf - 1);
    810                PORT_Memcpy(msgBuf, errString, errLen);
    811                msgBuf[errLen] = 0;
    812 
    813                iovs[numIOVs].iov_base = msgBuf;
    814                iovs[numIOVs].iov_len = PORT_Strlen(msgBuf);
    815                numIOVs++;
    816            } else if (reqLen <= 0) { /* hit eof */
    817                snprintf(msgBuf, sizeof(msgBuf), "Get or Post incomplete after %d bytes.\r\n",
    818                         bufDat);
    819 
    820                iovs[numIOVs].iov_base = msgBuf;
    821                iovs[numIOVs].iov_len = PORT_Strlen(msgBuf);
    822                numIOVs++;
    823            } else if (reqLen < bufDat) {
    824                snprintf(msgBuf, sizeof(msgBuf), "Discarded %d characters.\r\n",
    825                         bufDat - reqLen);
    826 
    827                iovs[numIOVs].iov_base = msgBuf;
    828                iovs[numIOVs].iov_len = PORT_Strlen(msgBuf);
    829                numIOVs++;
    830            }
    831 
    832            if (reqLen > 0) {
    833                if (verbose > 1)
    834                    fwrite(buf, 1, reqLen, stdout); /* display it */
    835 
    836                iovs[numIOVs].iov_base = buf;
    837                iovs[numIOVs].iov_len = reqLen;
    838                numIOVs++;
    839            }
    840 
    841            rv = PR_Writev(ssl_sock, iovs, numIOVs, PR_INTERVAL_NO_TIMEOUT);
    842            if (rv < 0) {
    843                errWarn("PR_Writev");
    844                break;
    845            }
    846 
    847        } while (0);
    848 
    849 cleanup:
    850    if (ssl_sock) {
    851        PR_Close(ssl_sock);
    852    } else if (tcp_sock) {
    853        PR_Close(tcp_sock);
    854    }
    855    if (local_file_fd)
    856        PR_Close(local_file_fd);
    857    VLOG(("httpserv: handle_connection: exiting\n"));
    858 
    859    /* do a nice shutdown if asked. */
    860    if (!strncmp(buf, stopCmd, sizeof stopCmd - 1)) {
    861        VLOG(("httpserv: handle_connection: stop command"));
    862        stop_server();
    863    }
    864    VLOG(("httpserv: handle_connection: exiting"));
    865    return SECSuccess; /* success */
    866 }
    867 
    868 #ifdef XP_UNIX
    869 
    870 void
    871 sigusr1_handler(int sig)
    872 {
    873    VLOG(("httpserv: sigusr1_handler: stop server"));
    874    stop_server();
    875 }
    876 
    877 #endif
    878 
    879 SECStatus
    880 do_accepts(
    881    PRFileDesc *listen_sock,
    882    PRFileDesc *model_sock,
    883    int requestCert)
    884 {
    885    PRNetAddr addr;
    886    PRErrorCode perr;
    887 #ifdef XP_UNIX
    888    struct sigaction act;
    889 #endif
    890 
    891    VLOG(("httpserv: do_accepts: starting"));
    892    PR_SetThreadPriority(PR_GetCurrentThread(), PR_PRIORITY_HIGH);
    893 
    894    acceptorThread = PR_GetCurrentThread();
    895 #ifdef XP_UNIX
    896    /* set up the signal handler */
    897    act.sa_handler = sigusr1_handler;
    898    sigemptyset(&act.sa_mask);
    899    act.sa_flags = 0;
    900    if (sigaction(SIGUSR1, &act, NULL)) {
    901        fprintf(stderr, "Error installing signal handler.\n");
    902        exit(1);
    903    }
    904 #endif
    905    while (!stopping) {
    906        PRFileDesc *tcp_sock;
    907        PRCList *myLink;
    908 
    909        FPRINTF(stderr, "\n\n\nhttpserv: About to call accept.\n");
    910        tcp_sock = PR_Accept(listen_sock, &addr, PR_INTERVAL_NO_TIMEOUT);
    911        if (tcp_sock == NULL) {
    912            perr = PR_GetError();
    913            if ((perr != PR_CONNECT_RESET_ERROR &&
    914                 perr != PR_PENDING_INTERRUPT_ERROR) ||
    915                verbose) {
    916                errWarn("PR_Accept");
    917            }
    918            if (perr == PR_CONNECT_RESET_ERROR) {
    919                FPRINTF(stderr,
    920                        "Ignoring PR_CONNECT_RESET_ERROR error - continue\n");
    921                continue;
    922            }
    923            stopping = 1;
    924            break;
    925        }
    926 
    927        VLOG(("httpserv: do_accept: Got connection\n"));
    928 
    929        PZ_Lock(qLock);
    930        while (PR_CLIST_IS_EMPTY(&freeJobs) && !stopping) {
    931            PZ_WaitCondVar(freeListNotEmptyCv, PR_INTERVAL_NO_TIMEOUT);
    932        }
    933        if (stopping) {
    934            PZ_Unlock(qLock);
    935            if (tcp_sock) {
    936                PR_Close(tcp_sock);
    937            }
    938            break;
    939        }
    940        myLink = PR_LIST_HEAD(&freeJobs);
    941        PR_REMOVE_AND_INIT_LINK(myLink);
    942        /* could release qLock here and reaquire it 7 lines below, but
    943        ** why bother for 4 assignment statements?
    944        */
    945        {
    946            JOB *myJob = (JOB *)myLink;
    947            myJob->tcp_sock = tcp_sock;
    948            myJob->model_sock = model_sock;
    949            myJob->requestCert = requestCert;
    950        }
    951 
    952        PR_APPEND_LINK(myLink, &jobQ);
    953        PZ_NotifyCondVar(jobQNotEmptyCv);
    954        PZ_Unlock(qLock);
    955    }
    956 
    957    FPRINTF(stderr, "httpserv: Closing listen socket.\n");
    958    VLOG(("httpserv: do_accepts: exiting"));
    959    if (listen_sock) {
    960        PR_Close(listen_sock);
    961    }
    962    return SECSuccess;
    963 }
    964 
    965 PRFileDesc *
    966 getBoundListenSocket(unsigned short port)
    967 {
    968    PRFileDesc *listen_sock;
    969    int listenQueueDepth = 5 + (2 * maxThreads);
    970    PRStatus prStatus;
    971    PRNetAddr addr;
    972    PRSocketOptionData opt;
    973 
    974    addr.inet.family = PR_AF_INET;
    975    addr.inet.ip = PR_htonl(PR_INADDR_ANY);
    976    addr.inet.port = PR_htons(port);
    977 
    978    listen_sock = PR_NewTCPSocket();
    979    if (listen_sock == NULL) {
    980        errExit("PR_NewTCPSocket");
    981    }
    982 
    983    opt.option = PR_SockOpt_Nonblocking;
    984    opt.value.non_blocking = PR_FALSE;
    985    prStatus = PR_SetSocketOption(listen_sock, &opt);
    986    if (prStatus < 0) {
    987        PR_Close(listen_sock);
    988        errExit("PR_SetSocketOption(PR_SockOpt_Nonblocking)");
    989    }
    990 
    991    opt.option = PR_SockOpt_Reuseaddr;
    992    opt.value.reuse_addr = PR_TRUE;
    993    prStatus = PR_SetSocketOption(listen_sock, &opt);
    994    if (prStatus < 0) {
    995        PR_Close(listen_sock);
    996        errExit("PR_SetSocketOption(PR_SockOpt_Reuseaddr)");
    997    }
    998 
    999    /* Set PR_SockOpt_Linger because it helps prevent a server bind issue
   1000     * after clean shutdown . See bug 331413 .*/
   1001    opt.option = PR_SockOpt_Linger;
   1002    opt.value.linger.polarity = PR_TRUE;
   1003    opt.value.linger.linger = PR_SecondsToInterval(1);
   1004    prStatus = PR_SetSocketOption(listen_sock, &opt);
   1005    if (prStatus < 0) {
   1006        PR_Close(listen_sock);
   1007        errExit("PR_SetSocketOption(PR_SockOpt_Linger)");
   1008    }
   1009 
   1010    prStatus = PR_Bind(listen_sock, &addr);
   1011    if (prStatus < 0) {
   1012        PR_Close(listen_sock);
   1013        errExit("PR_Bind");
   1014    }
   1015 
   1016    prStatus = PR_Listen(listen_sock, listenQueueDepth);
   1017    if (prStatus < 0) {
   1018        PR_Close(listen_sock);
   1019        errExit("PR_Listen");
   1020    }
   1021    return listen_sock;
   1022 }
   1023 
   1024 void
   1025 server_main(
   1026    PRFileDesc *listen_sock,
   1027    int requestCert,
   1028    SECKEYPrivateKey **privKey,
   1029    CERTCertificate **cert,
   1030    const char *expectedHostNameVal)
   1031 {
   1032    PRFileDesc *model_sock = NULL;
   1033 
   1034    /* Now, do the accepting, here in the main thread. */
   1035    do_accepts(listen_sock, model_sock, requestCert);
   1036 
   1037    terminateWorkerThreads();
   1038 
   1039    if (model_sock) {
   1040        PR_Close(model_sock);
   1041    }
   1042 }
   1043 
   1044 int numChildren;
   1045 PRProcess *child[MAX_PROCS];
   1046 
   1047 PRProcess *
   1048 haveAChild(int argc, char **argv, PRProcessAttr *attr)
   1049 {
   1050    PRProcess *newProcess;
   1051 
   1052    newProcess = PR_CreateProcess(argv[0], argv, NULL, attr);
   1053    if (!newProcess) {
   1054        errWarn("Can't create new process.");
   1055    } else {
   1056        child[numChildren++] = newProcess;
   1057    }
   1058    return newProcess;
   1059 }
   1060 
   1061 /* slightly adjusted version of ocsp_CreateCertID (not using issuer) */
   1062 static CERTOCSPCertID *
   1063 ocsp_CreateSelfCAID(PLArenaPool *arena, CERTCertificate *cert, PRTime time)
   1064 {
   1065    CERTOCSPCertID *certID;
   1066    void *mark = PORT_ArenaMark(arena);
   1067    SECStatus rv;
   1068 
   1069    PORT_Assert(arena != NULL);
   1070 
   1071    certID = PORT_ArenaZNew(arena, CERTOCSPCertID);
   1072    if (certID == NULL) {
   1073        goto loser;
   1074    }
   1075 
   1076    rv = SECOID_SetAlgorithmID(arena, &certID->hashAlgorithm, SEC_OID_SHA1,
   1077                               NULL);
   1078    if (rv != SECSuccess) {
   1079        goto loser;
   1080    }
   1081 
   1082    if (CERT_GetSubjectNameDigest(arena, cert, SEC_OID_SHA1,
   1083                                  &(certID->issuerNameHash)) == NULL) {
   1084        goto loser;
   1085    }
   1086    certID->issuerSHA1NameHash.data = certID->issuerNameHash.data;
   1087    certID->issuerSHA1NameHash.len = certID->issuerNameHash.len;
   1088 
   1089    if (CERT_GetSubjectNameDigest(arena, cert, SEC_OID_MD5,
   1090                                  &(certID->issuerMD5NameHash)) == NULL) {
   1091        goto loser;
   1092    }
   1093 
   1094    if (CERT_GetSubjectNameDigest(arena, cert, SEC_OID_MD2,
   1095                                  &(certID->issuerMD2NameHash)) == NULL) {
   1096        goto loser;
   1097    }
   1098 
   1099    if (CERT_GetSubjectPublicKeyDigest(arena, cert, SEC_OID_SHA1,
   1100                                       &certID->issuerKeyHash) == NULL) {
   1101        goto loser;
   1102    }
   1103    certID->issuerSHA1KeyHash.data = certID->issuerKeyHash.data;
   1104    certID->issuerSHA1KeyHash.len = certID->issuerKeyHash.len;
   1105    /* cache the other two hash algorithms as well */
   1106    if (CERT_GetSubjectPublicKeyDigest(arena, cert, SEC_OID_MD5,
   1107                                       &certID->issuerMD5KeyHash) == NULL) {
   1108        goto loser;
   1109    }
   1110    if (CERT_GetSubjectPublicKeyDigest(arena, cert, SEC_OID_MD2,
   1111                                       &certID->issuerMD2KeyHash) == NULL) {
   1112        goto loser;
   1113    }
   1114 
   1115    PORT_ArenaUnmark(arena, mark);
   1116    return certID;
   1117 
   1118 loser:
   1119    PORT_ArenaRelease(arena, mark);
   1120    return NULL;
   1121 }
   1122 
   1123 /* slightly adjusted version of CERT_CreateOCSPCertID */
   1124 CERTOCSPCertID *
   1125 cert_CreateSelfCAID(CERTCertificate *cert, PRTime time)
   1126 {
   1127    PLArenaPool *arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
   1128    CERTOCSPCertID *certID;
   1129    PORT_Assert(arena != NULL);
   1130    if (!arena)
   1131        return NULL;
   1132 
   1133    certID = ocsp_CreateSelfCAID(arena, cert, time);
   1134    if (!certID) {
   1135        PORT_FreeArena(arena, PR_FALSE);
   1136        return NULL;
   1137    }
   1138    certID->poolp = arena;
   1139    return certID;
   1140 }
   1141 
   1142 int
   1143 main(int argc, char **argv)
   1144 {
   1145    char *progName = NULL;
   1146    const char *dir = ".";
   1147    char *passwd = NULL;
   1148    char *pwfile = NULL;
   1149    const char *pidFile = NULL;
   1150    char *tmp;
   1151    PRFileDesc *listen_sock;
   1152    int optionsFound = 0;
   1153    unsigned short port = 0;
   1154    SECStatus rv;
   1155    PRStatus prStatus;
   1156    PRBool bindOnly = PR_FALSE;
   1157    PRBool useLocalThreads = PR_FALSE;
   1158    PLOptState *optstate;
   1159    PLOptStatus status;
   1160    char emptyString[] = { "" };
   1161    char *certPrefix = emptyString;
   1162    caRevoInfo *revoInfo = NULL;
   1163    PRCList *caRevoIter = NULL;
   1164    PRBool provideOcsp = PR_FALSE;
   1165 
   1166    tmp = strrchr(argv[0], '/');
   1167    tmp = tmp ? tmp + 1 : argv[0];
   1168    progName = strrchr(tmp, '\\');
   1169    progName = progName ? progName + 1 : tmp;
   1170 
   1171    PR_Init(PR_SYSTEM_THREAD, PR_PRIORITY_NORMAL, 1);
   1172 
   1173    /* please keep this list of options in ASCII collating sequence.
   1174    ** numbers, then capital letters, then lower case, alphabetical.
   1175    */
   1176    optstate = PL_CreateOptState(argc, argv,
   1177                                 "A:C:DO:P:bd:f:hi:p:t:vw:");
   1178    while ((status = PL_GetNextOpt(optstate)) == PL_OPT_OK) {
   1179        ++optionsFound;
   1180        switch (optstate->option) {
   1181            /* A first, must be followed by C. Any other order is an error.
   1182             * A creates the object. C completes and moves into list.
   1183             */
   1184            case 'A':
   1185                provideOcsp = PR_TRUE;
   1186                if (revoInfo) {
   1187                    Usage(progName);
   1188                    exit(0);
   1189                }
   1190                revoInfo = PORT_New(caRevoInfo);
   1191                revoInfo->nickname = PORT_Strdup(optstate->value);
   1192                break;
   1193            case 'C':
   1194                if (!revoInfo) {
   1195                    Usage(progName);
   1196                    exit(0);
   1197                }
   1198                revoInfo->crlFilename = PORT_Strdup(optstate->value);
   1199                if (!caRevoInfos) {
   1200                    PR_INIT_CLIST(&revoInfo->link);
   1201                    caRevoInfos = revoInfo;
   1202                } else {
   1203                    PR_APPEND_LINK(&revoInfo->link, &caRevoInfos->link);
   1204                }
   1205                revoInfo = NULL;
   1206                break;
   1207 
   1208            case 'O':
   1209                if (!PL_strcasecmp(optstate->value, "all")) {
   1210                    ocspMethodsAllowed = ocspGetAndPost;
   1211                } else if (!PL_strcasecmp(optstate->value, "get")) {
   1212                    ocspMethodsAllowed = ocspGetOnly;
   1213                } else if (!PL_strcasecmp(optstate->value, "post")) {
   1214                    ocspMethodsAllowed = ocspPostOnly;
   1215                } else if (!PL_strcasecmp(optstate->value, "random")) {
   1216                    ocspMethodsAllowed = ocspRandomGetFailure;
   1217                } else if (!PL_strcasecmp(optstate->value, "get-unknown")) {
   1218                    ocspMethodsAllowed = ocspGetUnknown;
   1219                } else {
   1220                    Usage(progName);
   1221                    exit(0);
   1222                }
   1223                break;
   1224 
   1225            case 'D':
   1226                noDelay = PR_TRUE;
   1227                break;
   1228 
   1229            case 'P':
   1230                certPrefix = PORT_Strdup(optstate->value);
   1231                break;
   1232 
   1233            case 'b':
   1234                bindOnly = PR_TRUE;
   1235                break;
   1236 
   1237            case 'd':
   1238                dir = optstate->value;
   1239                break;
   1240 
   1241            case 'f':
   1242                pwdata.source = PW_FROMFILE;
   1243                pwdata.data = pwfile = PORT_Strdup(optstate->value);
   1244                break;
   1245 
   1246            case 'h':
   1247                Usage(progName);
   1248                exit(0);
   1249                break;
   1250 
   1251            case 'i':
   1252                pidFile = optstate->value;
   1253                break;
   1254 
   1255            case 'p':
   1256                port = PORT_Atoi(optstate->value);
   1257                break;
   1258 
   1259            case 't':
   1260                maxThreads = PORT_Atoi(optstate->value);
   1261                if (maxThreads > MAX_THREADS)
   1262                    maxThreads = MAX_THREADS;
   1263                if (maxThreads < MIN_THREADS)
   1264                    maxThreads = MIN_THREADS;
   1265                break;
   1266 
   1267            case 'v':
   1268                verbose++;
   1269                break;
   1270 
   1271            case 'w':
   1272                pwdata.source = PW_PLAINTEXT;
   1273                pwdata.data = passwd = PORT_Strdup(optstate->value);
   1274                break;
   1275 
   1276            default:
   1277            case '?':
   1278                fprintf(stderr, "Unrecognized or bad option specified.\n");
   1279                fprintf(stderr, "Run '%s -h' for usage information.\n", progName);
   1280                exit(4);
   1281                break;
   1282        }
   1283    }
   1284    PL_DestroyOptState(optstate);
   1285    if (status == PL_OPT_BAD) {
   1286        fprintf(stderr, "Unrecognized or bad option specified.\n");
   1287        fprintf(stderr, "Run '%s -h' for usage information.\n", progName);
   1288        exit(5);
   1289    }
   1290    if (!optionsFound) {
   1291        Usage(progName);
   1292        exit(51);
   1293    }
   1294 
   1295    /* The -b (bindOnly) option is only used by the ssl.sh test
   1296     * script on Linux to determine whether a previous httpserv
   1297     * process has fully died and freed the port.  (Bug 129701)
   1298     */
   1299    if (bindOnly) {
   1300        listen_sock = getBoundListenSocket(port);
   1301        if (!listen_sock) {
   1302            exit(1);
   1303        }
   1304        if (listen_sock) {
   1305            PR_Close(listen_sock);
   1306        }
   1307        exit(0);
   1308    }
   1309 
   1310    if (port == 0) {
   1311        fprintf(stderr, "Required argument 'port' must be non-zero value\n");
   1312        exit(7);
   1313    }
   1314 
   1315    if (pidFile) {
   1316        FILE *tmpfile = fopen(pidFile, "w+");
   1317 
   1318        if (tmpfile) {
   1319            fprintf(tmpfile, "%d", getpid());
   1320            fclose(tmpfile);
   1321        }
   1322    }
   1323 
   1324    tmp = PR_GetEnvSecure("TMP");
   1325    if (!tmp)
   1326        tmp = PR_GetEnvSecure("TMPDIR");
   1327    if (!tmp)
   1328        tmp = PR_GetEnvSecure("TEMP");
   1329    /* we're an ordinary single process server. */
   1330    listen_sock = getBoundListenSocket(port);
   1331    prStatus = PR_SetFDInheritable(listen_sock, PR_FALSE);
   1332    if (prStatus != PR_SUCCESS)
   1333        errExit("PR_SetFDInheritable");
   1334 
   1335    lm = PR_NewLogModule("TestCase");
   1336 
   1337    /* set our password function */
   1338    PK11_SetPasswordFunc(SECU_GetModulePassword);
   1339 
   1340    if (provideOcsp) {
   1341        /* Call the NSS initialization routines */
   1342        rv = NSS_Initialize(dir, certPrefix, certPrefix, SECMOD_DB, NSS_INIT_READONLY);
   1343        if (rv != SECSuccess) {
   1344            fputs("NSS_Init failed.\n", stderr);
   1345            exit(8);
   1346        }
   1347 
   1348        if (caRevoInfos) {
   1349            caRevoIter = &caRevoInfos->link;
   1350            do {
   1351                PRFileDesc *inFile;
   1352                SECItem crlDER;
   1353                crlDER.data = NULL;
   1354 
   1355                revoInfo = (caRevoInfo *)caRevoIter;
   1356                revoInfo->cert = CERT_FindCertByNickname(
   1357                    CERT_GetDefaultCertDB(), revoInfo->nickname);
   1358                if (!revoInfo->cert) {
   1359                    fprintf(stderr, "cannot find cert with nickname %s\n",
   1360                            revoInfo->nickname);
   1361                    exit(1);
   1362                }
   1363                inFile = PR_Open(revoInfo->crlFilename, PR_RDONLY, 0);
   1364                if (inFile) {
   1365                    rv = SECU_ReadDERFromFile(&crlDER, inFile, PR_FALSE, PR_FALSE);
   1366                    PR_Close(inFile);
   1367                    inFile = NULL;
   1368                }
   1369                if (rv != SECSuccess) {
   1370                    fprintf(stderr, "unable to read crl file %s\n",
   1371                            revoInfo->crlFilename);
   1372                    exit(1);
   1373                }
   1374                revoInfo->crl =
   1375                    CERT_DecodeDERCrlWithFlags(NULL, &crlDER, SEC_CRL_TYPE,
   1376                                               CRL_DECODE_DEFAULT_OPTIONS);
   1377                SECITEM_FreeItem(&crlDER, PR_FALSE);
   1378                if (!revoInfo->crl) {
   1379                    fprintf(stderr, "unable to decode crl file %s\n",
   1380                            revoInfo->crlFilename);
   1381                    exit(1);
   1382                }
   1383                if (CERT_CompareName(&revoInfo->crl->crl.name,
   1384                                     &revoInfo->cert->subject) != SECEqual) {
   1385                    fprintf(stderr, "CRL %s doesn't match cert identified by preceding nickname %s\n",
   1386                            revoInfo->crlFilename, revoInfo->nickname);
   1387                    exit(1);
   1388                }
   1389                revoInfo->id = cert_CreateSelfCAID(revoInfo->cert, PR_Now());
   1390                caRevoIter = PR_NEXT_LINK(caRevoIter);
   1391            } while (caRevoIter != &caRevoInfos->link);
   1392        }
   1393    }
   1394 
   1395    /* allocate the array of thread slots, and launch the worker threads. */
   1396    rv = launch_threads(&jobLoop, 0, 0, 0, useLocalThreads);
   1397 
   1398    if (rv == SECSuccess) {
   1399        server_main(listen_sock, 0, 0, 0,
   1400                    0);
   1401    }
   1402 
   1403    VLOG(("httpserv: server_thread: exiting"));
   1404 
   1405    if (provideOcsp) {
   1406        if (caRevoInfos) {
   1407            caRevoIter = &caRevoInfos->link;
   1408            do {
   1409                revoInfo = (caRevoInfo *)caRevoIter;
   1410                if (revoInfo->nickname)
   1411                    PORT_Free(revoInfo->nickname);
   1412                if (revoInfo->crlFilename)
   1413                    PORT_Free(revoInfo->crlFilename);
   1414                if (revoInfo->cert)
   1415                    CERT_DestroyCertificate(revoInfo->cert);
   1416                if (revoInfo->id)
   1417                    CERT_DestroyOCSPCertID(revoInfo->id);
   1418                if (revoInfo->crl)
   1419                    SEC_DestroyCrl(revoInfo->crl);
   1420 
   1421                caRevoIter = PR_NEXT_LINK(caRevoIter);
   1422            } while (caRevoIter != &caRevoInfos->link);
   1423        }
   1424        if (NSS_Shutdown() != SECSuccess) {
   1425            SECU_PrintError(progName, "NSS_Shutdown");
   1426            PR_Cleanup();
   1427            exit(1);
   1428        }
   1429    }
   1430    if (passwd) {
   1431        PORT_Free(passwd);
   1432    }
   1433    if (pwfile) {
   1434        PORT_Free(pwfile);
   1435    }
   1436    if (certPrefix && certPrefix != emptyString) {
   1437        PORT_Free(certPrefix);
   1438    }
   1439    PR_Cleanup();
   1440    printf("httpserv: normal termination\n");
   1441    return 0;
   1442 }