From 7de245071815ca31b3a6973c3aa8e05c6feadb7a Mon Sep 17 00:00:00 2001 From: "kaie%kuix.de" Date: Thu, 28 Jun 2012 11:11:06 +0000 Subject: [PATCH] Bug 747947 - Implement an "http only" variation of selfserv, r=rrelyea git-svn-id: svn://10.0.0.236/trunk@263998 18797224-902f-48f8-a5cc-f745e15eee43 --- mozilla/security/nss/cmd/httpserv/Makefile | 46 + mozilla/security/nss/cmd/httpserv/httpserv.c | 887 ++++++++++++++++++ mozilla/security/nss/cmd/httpserv/manifest.mn | 20 + mozilla/security/nss/cmd/manifest.mn | 1 + 4 files changed, 954 insertions(+) create mode 100644 mozilla/security/nss/cmd/httpserv/Makefile create mode 100644 mozilla/security/nss/cmd/httpserv/httpserv.c create mode 100644 mozilla/security/nss/cmd/httpserv/manifest.mn diff --git a/mozilla/security/nss/cmd/httpserv/Makefile b/mozilla/security/nss/cmd/httpserv/Makefile new file mode 100644 index 00000000000..7b74b369c70 --- /dev/null +++ b/mozilla/security/nss/cmd/httpserv/Makefile @@ -0,0 +1,46 @@ +#! gmake +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +####################################################################### +# (1) Include initial platform-independent assignments (MANDATORY). # +####################################################################### + +include manifest.mn + +####################################################################### +# (2) Include "global" configuration information. (OPTIONAL) # +####################################################################### + +include $(CORE_DEPTH)/coreconf/config.mk + +####################################################################### +# (3) Include "component" configuration information. (OPTIONAL) # +####################################################################### + +####################################################################### +# (4) Include "local" platform-dependent assignments (OPTIONAL). # +####################################################################### +include ../platlibs.mk + +####################################################################### +# (5) Execute "global" rules. (OPTIONAL) # +####################################################################### + +include $(CORE_DEPTH)/coreconf/rules.mk + +####################################################################### +# (6) Execute "component" rules. (OPTIONAL) # +####################################################################### + + + +####################################################################### +# (7) Execute "local" rules. (OPTIONAL). # +####################################################################### + + +include ../platrules.mk + diff --git a/mozilla/security/nss/cmd/httpserv/httpserv.c b/mozilla/security/nss/cmd/httpserv/httpserv.c new file mode 100644 index 00000000000..9f8b98917d0 --- /dev/null +++ b/mozilla/security/nss/cmd/httpserv/httpserv.c @@ -0,0 +1,887 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include +#include + +#include "secutil.h" + +#if defined(XP_UNIX) +#include +#endif + +#if defined(_WINDOWS) +#include /* for getpid() */ +#endif + +#include +#include +#include +#include +#include + +#include "nspr.h" +#include "prio.h" +#include "prerror.h" +#include "prnetdb.h" +#include "prclist.h" +#include "plgetopt.h" + +#ifndef PORT_Sprintf +#define PORT_Sprintf sprintf +#endif + +#ifndef PORT_Strstr +#define PORT_Strstr strstr +#endif + +#ifndef PORT_Malloc +#define PORT_Malloc PR_Malloc +#endif + +static int handle_connection( PRFileDesc *, PRFileDesc *, int ); + +static const char inheritableSockName[] = { "SELFSERV_LISTEN_SOCKET" }; + +#define DEFAULT_BULK_TEST 16384 +#define MAX_BULK_TEST 1048576 /* 1 MB */ +static PRBool testBulk; + +/* data and structures for shutdown */ +static int stopping; + +static PRBool noDelay; +static int verbose; + +static PRThread * acceptorThread; + +static PRLogModuleInfo *lm; + +#define PRINTF if (verbose) printf +#define FPRINTF if (verbose) fprintf +#define FLUSH if (verbose) { fflush(stdout); fflush(stderr); } +#define VLOG(arg) PR_LOG(lm,PR_LOG_DEBUG,arg) + +static void +Usage(const char *progName) +{ + fprintf(stderr, + +"Usage: %s -p port [-Dbv]\n" +" [-t threads] [-i pid_file]\n" +"-D means disable Nagle delays in TCP\n" +"-b means try binding to the port and exit\n" +"-v means verbose output\n" +"-t threads -- specify the number of threads to use for connections.\n" +"-i pid_file file to write the process id of selfserve\n" + ,progName); +} + +static const char * +errWarn(char * funcString) +{ + PRErrorCode perr = PR_GetError(); + const char * errString = SECU_Strerror(perr); + + fprintf(stderr, "selfserv: %s returned error %d:\n%s\n", + funcString, perr, errString); + return errString; +} + +static void +errExit(char * funcString) +{ + errWarn(funcString); + exit(3); +} + + +#define MAX_VIRT_SERVER_NAME_ARRAY_INDEX 10 + +/************************************************************************** +** Begin thread management routines and data. +**************************************************************************/ +#define MIN_THREADS 3 +#define DEFAULT_THREADS 8 +#define MAX_THREADS 4096 +#define MAX_PROCS 25 +static int maxThreads = DEFAULT_THREADS; + + +typedef struct jobStr { + PRCList link; + PRFileDesc *tcp_sock; + PRFileDesc *model_sock; + int requestCert; +} JOB; + +static PZLock * qLock; /* this lock protects all data immediately below */ +static PRLock * lastLoadedCrlLock; /* this lock protects lastLoadedCrl variable */ +static PZCondVar * jobQNotEmptyCv; +static PZCondVar * freeListNotEmptyCv; +static PZCondVar * threadCountChangeCv; +static int threadCount; +static PRCList jobQ; +static PRCList freeJobs; +static JOB *jobTable; + +SECStatus +setupJobs(int maxJobs) +{ + int i; + + jobTable = (JOB *)PR_Calloc(maxJobs, sizeof(JOB)); + if (!jobTable) + return SECFailure; + + PR_INIT_CLIST(&jobQ); + PR_INIT_CLIST(&freeJobs); + + for (i = 0; i < maxJobs; ++i) { + JOB * pJob = jobTable + i; + PR_APPEND_LINK(&pJob->link, &freeJobs); + } + return SECSuccess; +} + +typedef int startFn(PRFileDesc *a, PRFileDesc *b, int c); + +typedef enum { rs_idle = 0, rs_running = 1, rs_zombie = 2 } runState; + +typedef struct perThreadStr { + PRFileDesc *a; + PRFileDesc *b; + int c; + int rv; + startFn * startFunc; + PRThread * prThread; + runState state; +} perThread; + +static perThread *threads; + +void +thread_wrapper(void * arg) +{ + perThread * slot = (perThread *)arg; + + slot->rv = (* slot->startFunc)(slot->a, slot->b, slot->c); + + /* notify the thread exit handler. */ + PZ_Lock(qLock); + slot->state = rs_zombie; + --threadCount; + PZ_NotifyAllCondVar(threadCountChangeCv); + PZ_Unlock(qLock); +} + +int +jobLoop(PRFileDesc *a, PRFileDesc *b, int c) +{ + PRCList * myLink = 0; + JOB * myJob; + + PZ_Lock(qLock); + do { + myLink = 0; + while (PR_CLIST_IS_EMPTY(&jobQ) && !stopping) { + PZ_WaitCondVar(jobQNotEmptyCv, PR_INTERVAL_NO_TIMEOUT); + } + if (!PR_CLIST_IS_EMPTY(&jobQ)) { + myLink = PR_LIST_HEAD(&jobQ); + PR_REMOVE_AND_INIT_LINK(myLink); + } + PZ_Unlock(qLock); + myJob = (JOB *)myLink; + /* myJob will be null when stopping is true and jobQ is empty */ + if (!myJob) + break; + handle_connection( myJob->tcp_sock, myJob->model_sock, + myJob->requestCert); + PZ_Lock(qLock); + PR_APPEND_LINK(myLink, &freeJobs); + PZ_NotifyCondVar(freeListNotEmptyCv); + } while (PR_TRUE); + return 0; +} + + +SECStatus +launch_threads( + startFn *startFunc, + PRFileDesc *a, + PRFileDesc *b, + int c, + PRBool local) +{ + int i; + SECStatus rv = SECSuccess; + + /* create the thread management serialization structs */ + qLock = PZ_NewLock(nssILockSelfServ); + jobQNotEmptyCv = PZ_NewCondVar(qLock); + freeListNotEmptyCv = PZ_NewCondVar(qLock); + threadCountChangeCv = PZ_NewCondVar(qLock); + + /* create monitor for crl reload procedure */ + lastLoadedCrlLock = PR_NewLock(); + + /* allocate the array of thread slots */ + threads = PR_Calloc(maxThreads, sizeof(perThread)); + if ( NULL == threads ) { + fprintf(stderr, "Oh Drat! Can't allocate the perThread array\n"); + return SECFailure; + } + /* 5 is a little extra, intended to keep the jobQ from underflowing. + ** That is, from going empty while not stopping and clients are still + ** trying to contact us. + */ + rv = setupJobs(maxThreads + 5); + if (rv != SECSuccess) + return rv; + + PZ_Lock(qLock); + for (i = 0; i < maxThreads; ++i) { + perThread * slot = threads + i; + + slot->state = rs_running; + slot->a = a; + slot->b = b; + slot->c = c; + slot->startFunc = startFunc; + slot->prThread = PR_CreateThread(PR_USER_THREAD, + thread_wrapper, slot, PR_PRIORITY_NORMAL, + (PR_TRUE==local)?PR_LOCAL_THREAD:PR_GLOBAL_THREAD, + PR_UNJOINABLE_THREAD, 0); + if (slot->prThread == NULL) { + printf("selfserv: Failed to launch thread!\n"); + slot->state = rs_idle; + rv = SECFailure; + break; + } + + ++threadCount; + } + PZ_Unlock(qLock); + + return rv; +} + +#define DESTROY_CONDVAR(name) if (name) { \ + PZ_DestroyCondVar(name); name = NULL; } +#define DESTROY_LOCK(name) if (name) { \ + PZ_DestroyLock(name); name = NULL; } + + +void +terminateWorkerThreads(void) +{ + VLOG(("selfserv: server_thead: waiting on stopping")); + PZ_Lock(qLock); + PZ_NotifyAllCondVar(jobQNotEmptyCv); + while (threadCount > 0) { + PZ_WaitCondVar(threadCountChangeCv, PR_INTERVAL_NO_TIMEOUT); + } + /* The worker threads empty the jobQ before they terminate. */ + PORT_Assert(PR_CLIST_IS_EMPTY(&jobQ)); + PZ_Unlock(qLock); + + DESTROY_CONDVAR(jobQNotEmptyCv); + DESTROY_CONDVAR(freeListNotEmptyCv); + DESTROY_CONDVAR(threadCountChangeCv); + + PR_DestroyLock(lastLoadedCrlLock); + DESTROY_LOCK(qLock); + PR_Free(jobTable); + PR_Free(threads); +} + +/************************************************************************** +** End thread management routines. +**************************************************************************/ + +PRBool NoReuse = PR_FALSE; +PRBool disableLocking = PR_FALSE; +PRBool failedToNegotiateName = PR_FALSE; + + +static const char stopCmd[] = { "GET /stop " }; +static const char getCmd[] = { "GET " }; +static const char EOFmsg[] = { "EOF\r\n\r\n\r\n" }; +static const char outHeader[] = { + "HTTP/1.0 200 OK\r\n" + "Server: Generic Web Server\r\n" + "Date: Tue, 26 Aug 1997 22:10:05 GMT\r\n" + "Content-type: text/plain\r\n" + "\r\n" +}; + +void stop_server() +{ + stopping = 1; + PR_Interrupt(acceptorThread); + PZ_TraceFlush(); +} + +int +handle_connection( + PRFileDesc *tcp_sock, + PRFileDesc *model_sock, + int requestCert + ) +{ + PRFileDesc * ssl_sock = NULL; + PRFileDesc * local_file_fd = NULL; + char * post; + char * pBuf; /* unused space at end of buf */ + const char * errString; + PRStatus status; + int bufRem; /* unused bytes at end of buf */ + int bufDat; /* characters received in buf */ + int newln = 0; /* # of consecutive newlns */ + int firstTime = 1; + int reqLen; + int rv; + int numIOVs; + PRSocketOptionData opt; + PRIOVec iovs[16]; + char msgBuf[160]; + char buf[10240]; + char fileName[513]; + + pBuf = buf; + bufRem = sizeof buf; + + VLOG(("selfserv: handle_connection: starting")); + opt.option = PR_SockOpt_Nonblocking; + opt.value.non_blocking = PR_FALSE; + PR_SetSocketOption(tcp_sock, &opt); + + VLOG(("selfserv: handle_connection: starting\n")); + ssl_sock = tcp_sock; + + if (noDelay) { + opt.option = PR_SockOpt_NoDelay; + opt.value.no_delay = PR_TRUE; + status = PR_SetSocketOption(ssl_sock, &opt); + if (status != PR_SUCCESS) { + errWarn("PR_SetSocketOption(PR_SockOpt_NoDelay, PR_TRUE)"); + if (ssl_sock) { + PR_Close(ssl_sock); + } + return SECFailure; + } + } + + while (1) { + newln = 0; + reqLen = 0; + rv = PR_Read(ssl_sock, pBuf, bufRem - 1); + if (rv == 0 || + (rv < 0 && PR_END_OF_FILE_ERROR == PR_GetError())) { + if (verbose) + errWarn("HDX PR_Read hit EOF"); + break; + } + if (rv < 0) { + errWarn("HDX PR_Read"); + goto cleanup; + } + /* NULL termination */ + pBuf[rv] = 0; + if (firstTime) { + firstTime = 0; + } + + pBuf += rv; + bufRem -= rv; + bufDat = pBuf - buf; + /* Parse the input, starting at the beginning of the buffer. + * Stop when we detect two consecutive \n's (or \r\n's) + * as this signifies the end of the GET or POST portion. + * The posted data follows. + */ + while (reqLen < bufDat && newln < 2) { + int octet = buf[reqLen++]; + if (octet == '\n') { + newln++; + } else if (octet != '\r') { + newln = 0; + } + } + + /* came to the end of the buffer, or second newln + * If we didn't get an empty line (CRLFCRLF) then keep on reading. + */ + if (newln < 2) + continue; + + /* we're at the end of the HTTP request. + * If the request is a POST, then there will be one more + * line of data. + * This parsing is a hack, but ok for SSL test purposes. + */ + post = PORT_Strstr(buf, "POST "); + if (!post || *post != 'P') + break; + + /* It's a post, so look for the next and final CR/LF. */ + /* We should parse content length here, but ... */ + while (reqLen < bufDat && newln < 3) { + int octet = buf[reqLen++]; + if (octet == '\n') { + newln++; + } + } + if (newln == 3) + break; + } /* read loop */ + + bufDat = pBuf - buf; + if (bufDat) do { /* just close if no data */ + /* Have either (a) a complete get, (b) a complete post, (c) EOF */ + if (reqLen > 0 && !strncmp(buf, getCmd, sizeof getCmd - 1)) { + char * fnBegin = buf + 4; + char * fnEnd; + PRFileInfo info; + /* try to open the file named. + * If successful, then write it to the client. + */ + fnEnd = strpbrk(fnBegin, " \r\n"); + if (fnEnd) { + int fnLen = fnEnd - fnBegin; + if (fnLen < sizeof fileName) { + char *fnstart; + strncpy(fileName, fnBegin, fnLen); + fileName[fnLen] = 0; /* null terminate */ + fnstart = fileName; + /* strip initial / because our root is the current directory*/ + while (*fnstart && *fnstart=='/') + ++fnstart; + status = PR_GetFileInfo(fnstart, &info); + if (status == PR_SUCCESS && + info.type == PR_FILE_FILE && + info.size >= 0 ) { + local_file_fd = PR_Open(fnstart, PR_RDONLY, 0); + } + } + } + } + + numIOVs = 0; + + iovs[numIOVs].iov_base = (char *)outHeader; + iovs[numIOVs].iov_len = (sizeof(outHeader)) - 1; + numIOVs++; + + if (local_file_fd) { + PRInt32 bytes; + int errLen; + bytes = PR_TransmitFile(ssl_sock, local_file_fd, outHeader, + sizeof outHeader - 1, + PR_TRANSMITFILE_KEEP_OPEN, + PR_INTERVAL_NO_TIMEOUT); + if (bytes >= 0) { + bytes -= sizeof outHeader - 1; + FPRINTF(stderr, + "selfserv: PR_TransmitFile wrote %d bytes from %s\n", + bytes, fileName); + break; + } + errString = errWarn("PR_TransmitFile"); + errLen = PORT_Strlen(errString); + errLen = PR_MIN(errLen, sizeof msgBuf - 1); + PORT_Memcpy(msgBuf, errString, errLen); + msgBuf[errLen] = 0; + + iovs[numIOVs].iov_base = msgBuf; + iovs[numIOVs].iov_len = PORT_Strlen(msgBuf); + numIOVs++; + } else if (reqLen <= 0) { /* hit eof */ + PORT_Sprintf(msgBuf, "Get or Post incomplete after %d bytes.\r\n", + bufDat); + + iovs[numIOVs].iov_base = msgBuf; + iovs[numIOVs].iov_len = PORT_Strlen(msgBuf); + numIOVs++; + } else if (reqLen < bufDat) { + PORT_Sprintf(msgBuf, "Discarded %d characters.\r\n", + bufDat - reqLen); + + iovs[numIOVs].iov_base = msgBuf; + iovs[numIOVs].iov_len = PORT_Strlen(msgBuf); + numIOVs++; + } + + if (reqLen > 0) { + if (verbose > 1) + fwrite(buf, 1, reqLen, stdout); /* display it */ + + iovs[numIOVs].iov_base = buf; + iovs[numIOVs].iov_len = reqLen; + numIOVs++; + } + + /* Don't add the EOF if we want to test bulk encryption */ + if (!testBulk) { + iovs[numIOVs].iov_base = (char *)EOFmsg; + iovs[numIOVs].iov_len = sizeof EOFmsg - 1; + numIOVs++; + } + + rv = PR_Writev(ssl_sock, iovs, numIOVs, PR_INTERVAL_NO_TIMEOUT); + if (rv < 0) { + errWarn("PR_Writev"); + break; + } + + } while (0); + +cleanup: + if (ssl_sock) { + PR_Close(ssl_sock); + } else if (tcp_sock) { + PR_Close(tcp_sock); + } + if (local_file_fd) + PR_Close(local_file_fd); + VLOG(("selfserv: handle_connection: exiting\n")); + + /* do a nice shutdown if asked. */ + if (!strncmp(buf, stopCmd, sizeof stopCmd - 1)) { + VLOG(("selfserv: handle_connection: stop command")); + stop_server(); + } + VLOG(("selfserv: handle_connection: exiting")); + return SECSuccess; /* success */ +} + +#ifdef XP_UNIX + +void sigusr1_handler(int sig) +{ + VLOG(("selfserv: sigusr1_handler: stop server")); + stop_server(); +} + +#endif + +SECStatus +do_accepts( + PRFileDesc *listen_sock, + PRFileDesc *model_sock, + int requestCert + ) +{ + PRNetAddr addr; + PRErrorCode perr; +#ifdef XP_UNIX + struct sigaction act; +#endif + + VLOG(("selfserv: do_accepts: starting")); + PR_SetThreadPriority( PR_GetCurrentThread(), PR_PRIORITY_HIGH); + + acceptorThread = PR_GetCurrentThread(); +#ifdef XP_UNIX + /* set up the signal handler */ + act.sa_handler = sigusr1_handler; + sigemptyset(&act.sa_mask); + act.sa_flags = 0; + if (sigaction(SIGUSR1, &act, NULL)) { + fprintf(stderr, "Error installing signal handler.\n"); + exit(1); + } +#endif + while (!stopping) { + PRFileDesc *tcp_sock; + PRCList *myLink; + + FPRINTF(stderr, "\n\n\nselfserv: About to call accept.\n"); + tcp_sock = PR_Accept(listen_sock, &addr, PR_INTERVAL_NO_TIMEOUT); + if (tcp_sock == NULL) { + perr = PR_GetError(); + if ((perr != PR_CONNECT_RESET_ERROR && + perr != PR_PENDING_INTERRUPT_ERROR) || verbose) { + errWarn("PR_Accept"); + } + if (perr == PR_CONNECT_RESET_ERROR) { + FPRINTF(stderr, + "Ignoring PR_CONNECT_RESET_ERROR error - continue\n"); + continue; + } + stopping = 1; + break; + } + + VLOG(("selfserv: do_accept: Got connection\n")); + + PZ_Lock(qLock); + while (PR_CLIST_IS_EMPTY(&freeJobs) && !stopping) { + PZ_WaitCondVar(freeListNotEmptyCv, PR_INTERVAL_NO_TIMEOUT); + } + if (stopping) { + PZ_Unlock(qLock); + if (tcp_sock) { + PR_Close(tcp_sock); + } + break; + } + myLink = PR_LIST_HEAD(&freeJobs); + PR_REMOVE_AND_INIT_LINK(myLink); + /* could release qLock here and reaquire it 7 lines below, but + ** why bother for 4 assignment statements? + */ + { + JOB * myJob = (JOB *)myLink; + myJob->tcp_sock = tcp_sock; + myJob->model_sock = model_sock; + myJob->requestCert = requestCert; + } + + PR_APPEND_LINK(myLink, &jobQ); + PZ_NotifyCondVar(jobQNotEmptyCv); + PZ_Unlock(qLock); + } + + FPRINTF(stderr, "selfserv: Closing listen socket.\n"); + VLOG(("selfserv: do_accepts: exiting")); + if (listen_sock) { + PR_Close(listen_sock); + } + return SECSuccess; +} + +PRFileDesc * +getBoundListenSocket(unsigned short port) +{ + PRFileDesc * listen_sock; + int listenQueueDepth = 5 + (2 * maxThreads); + PRStatus prStatus; + PRNetAddr addr; + PRSocketOptionData opt; + + addr.inet.family = PR_AF_INET; + addr.inet.ip = PR_INADDR_ANY; + addr.inet.port = PR_htons(port); + + listen_sock = PR_NewTCPSocket(); + if (listen_sock == NULL) { + errExit("PR_NewTCPSocket"); + } + + opt.option = PR_SockOpt_Nonblocking; + opt.value.non_blocking = PR_FALSE; + prStatus = PR_SetSocketOption(listen_sock, &opt); + if (prStatus < 0) { + PR_Close(listen_sock); + errExit("PR_SetSocketOption(PR_SockOpt_Nonblocking)"); + } + + opt.option=PR_SockOpt_Reuseaddr; + opt.value.reuse_addr = PR_TRUE; + prStatus = PR_SetSocketOption(listen_sock, &opt); + if (prStatus < 0) { + PR_Close(listen_sock); + errExit("PR_SetSocketOption(PR_SockOpt_Reuseaddr)"); + } + +#ifndef WIN95 + /* Set PR_SockOpt_Linger because it helps prevent a server bind issue + * after clean shutdown . See bug 331413 . + * Don't do it in the WIN95 build configuration because clean shutdown is + * not implemented, and PR_SockOpt_Linger causes a hang in ssl.sh . + * See bug 332348 */ + opt.option=PR_SockOpt_Linger; + opt.value.linger.polarity = PR_TRUE; + opt.value.linger.linger = PR_SecondsToInterval(1); + prStatus = PR_SetSocketOption(listen_sock, &opt); + if (prStatus < 0) { + PR_Close(listen_sock); + errExit("PR_SetSocketOption(PR_SockOpt_Linger)"); + } +#endif + + prStatus = PR_Bind(listen_sock, &addr); + if (prStatus < 0) { + PR_Close(listen_sock); + errExit("PR_Bind"); + } + + prStatus = PR_Listen(listen_sock, listenQueueDepth); + if (prStatus < 0) { + PR_Close(listen_sock); + errExit("PR_Listen"); + } + return listen_sock; +} + +void +server_main( + PRFileDesc * listen_sock, + int requestCert, + SECKEYPrivateKey ** privKey, + CERTCertificate ** cert, + const char *expectedHostNameVal) +{ + PRFileDesc *model_sock = NULL; + + /* Now, do the accepting, here in the main thread. */ + do_accepts(listen_sock, model_sock, requestCert); + + terminateWorkerThreads(); + + if (model_sock) { + PR_Close(model_sock); + } + +} + +int numChildren; +PRProcess * child[MAX_PROCS]; + +PRProcess * +haveAChild(int argc, char **argv, PRProcessAttr * attr) +{ + PRProcess * newProcess; + + newProcess = PR_CreateProcess(argv[0], argv, NULL, attr); + if (!newProcess) { + errWarn("Can't create new process."); + } else { + child[numChildren++] = newProcess; + } + return newProcess; +} + +int +main(int argc, char **argv) +{ + char * progName = NULL; + const char * pidFile = NULL; + char * tmp; + PRFileDesc * listen_sock; + int optionsFound = 0; + unsigned short port = 0; + SECStatus rv; + PRStatus prStatus; + PRBool bindOnly = PR_FALSE; + PRBool useLocalThreads = PR_FALSE; + PLOptState *optstate; + PLOptStatus status; + + tmp = strrchr(argv[0], '/'); + tmp = tmp ? tmp + 1 : argv[0]; + progName = strrchr(tmp, '\\'); + progName = progName ? progName + 1 : tmp; + + PR_Init( PR_SYSTEM_THREAD, PR_PRIORITY_NORMAL, 1); + + /* please keep this list of options in ASCII collating sequence. + ** numbers, then capital letters, then lower case, alphabetical. + */ + optstate = PL_CreateOptState(argc, argv, + "Dbhi:p:t:v"); + while ((status = PL_GetNextOpt(optstate)) == PL_OPT_OK) { + ++optionsFound; + switch(optstate->option) { + case 'D': noDelay = PR_TRUE; break; + + case 'b': bindOnly = PR_TRUE; break; + + case 'h': Usage(progName); exit(0); break; + + case 'i': pidFile = optstate->value; break; + + case 'p': port = PORT_Atoi(optstate->value); break; + + case 't': + maxThreads = PORT_Atoi(optstate->value); + if ( maxThreads > MAX_THREADS ) maxThreads = MAX_THREADS; + if ( maxThreads < MIN_THREADS ) maxThreads = MIN_THREADS; + break; + + case 'v': verbose++; break; + + default: + case '?': + fprintf(stderr, "Unrecognized or bad option specified.\n"); + fprintf(stderr, "Run '%s -h' for usage information.\n", progName); + exit(4); + break; + } + } + PL_DestroyOptState(optstate); + if (status == PL_OPT_BAD) { + fprintf(stderr, "Unrecognized or bad option specified.\n"); + fprintf(stderr, "Run '%s -h' for usage information.\n", progName); + exit(5); + } + if (!optionsFound) { + Usage(progName); + exit(51); + } + + /* The -b (bindOnly) option is only used by the ssl.sh test + * script on Linux to determine whether a previous selfserv + * process has fully died and freed the port. (Bug 129701) + */ + if (bindOnly) { + listen_sock = getBoundListenSocket(port); + if (!listen_sock) { + exit(1); + } + if (listen_sock) { + PR_Close(listen_sock); + } + exit(0); + } + + if (port == 0) { + fprintf(stderr, "Required argument 'port' must be non-zero value\n"); + exit(7); + } + + if (pidFile) { + FILE *tmpfile=fopen(pidFile,"w+"); + + if (tmpfile) { + fprintf(tmpfile,"%d",getpid()); + fclose(tmpfile); + } + } + + tmp = getenv("TMP"); + if (!tmp) + tmp = getenv("TMPDIR"); + if (!tmp) + tmp = getenv("TEMP"); + /* we're an ordinary single process server. */ + listen_sock = getBoundListenSocket(port); + prStatus = PR_SetFDInheritable(listen_sock, PR_FALSE); + if (prStatus != PR_SUCCESS) + errExit("PR_SetFDInheritable"); + + lm = PR_NewLogModule("TestCase"); + +/* allocate the array of thread slots, and launch the worker threads. */ + rv = launch_threads(&jobLoop, 0, 0, 0, useLocalThreads); + + if (rv == SECSuccess) { + server_main(listen_sock, 0, 0, 0, + 0); + } + + VLOG(("selfserv: server_thread: exiting")); + + if (failedToNegotiateName) { + fprintf(stderr, "selfserv: Failed properly negotiate server name\n"); + exit(1); + } + + PR_Cleanup(); + printf("selfserv: normal termination\n"); + return 0; +} + diff --git a/mozilla/security/nss/cmd/httpserv/manifest.mn b/mozilla/security/nss/cmd/httpserv/manifest.mn new file mode 100644 index 00000000000..79d7dc1dfd9 --- /dev/null +++ b/mozilla/security/nss/cmd/httpserv/manifest.mn @@ -0,0 +1,20 @@ +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +CORE_DEPTH = ../../.. + +DEFINES += -DNSPR20 + +# MODULE public and private header directories are implicitly REQUIRED. +MODULE = nss + +CSRCS = httpserv.c + +# The MODULE is always implicitly required. +# Listing it here in REQUIRES makes it appear twice in the cc command line. +REQUIRES = seccmd dbm + +PROGRAM = httpserv + diff --git a/mozilla/security/nss/cmd/manifest.mn b/mozilla/security/nss/cmd/manifest.mn index 97d96619d78..0799114edd2 100644 --- a/mozilla/security/nss/cmd/manifest.mn +++ b/mozilla/security/nss/cmd/manifest.mn @@ -22,6 +22,7 @@ DIRS = lib \ dbtest \ derdump \ digest \ + httpserv \ fipstest \ $(LOWHASHTEST_SRCDIR) \ makepqg \