PUBLIC int mprGetRandomBytes(char *buf, ssize length, bool block) { HCRYPTPROV prov; int rc; rc = 0; if (!CryptAcquireContext(&prov, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT | 0x40)) { return mprGetError(); } if (!CryptGenRandom(prov, (wsize) length, buf)) { rc = mprGetError(); } CryptReleaseContext(prov, 0); return rc; }
/* * Outgoing data service routine. May be called multiple times. */ static void sendOutgoingService(MaQueue *q) { MaConn *conn; MaResponse *resp; int written; int errCode; conn = q->conn; resp = conn->response; if (conn->sock == 0) { return; } /* * Loop doing non-blocking I/O until blocked or all the packets received are written. */ while (1) { /* * Rebuild the iovector only when the past vector has been completely written. Simplifies the logic quite a bit. */ written = 0; if (q->ioIndex == 0 && buildSendVec(q) <= 0) { break; } /* * Write the vector and file data. Exclude the file entry in the io vector. */ written = (int) mprSendFileToSocket(conn->sock, resp->file, q->ioPos, q->ioCount, q->iovec, q->ioIndex, NULL, 0); mprLog(q, 5, "Send connector written %d", written); if (written < 0) { errCode = mprGetError(); if (errCode == EAGAIN || errCode == EWOULDBLOCK) { maRequestWriteBlocked(conn); break; } if (errCode == EPIPE || errCode == ECONNRESET) { maDisconnectConn(conn); } else { mprLog(conn, 7, "mprSendFileToSocket failed, errCode %d", errCode); maDisconnectConn(conn); } freeSentPackets(q, MAXINT); break; } else if (written == 0) { /* Socket is full. Wait for an I/O event */ maRequestWriteBlocked(conn); break; } else if (written > 0) { resp->bytesWritten += written; freeSentPackets(q, written); adjustSendVec(q, written); } } if (q->ioCount == 0 && q->flags & MA_QUEUE_EOF) { maCompleteRequest(conn); } }
int mprGetRandomBytes(MprCtx ctx, char *buf, int length, int block) { HCRYPTPROV prov; int rc; rc = 0; if (!CryptAcquireContext(&prov, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT | 0x40)) { return mprGetError(); } if (!CryptGenRandom(prov, length, buf)) { rc = mprGetError(); } CryptReleaseContext(prov, 0); return rc; }
static void browserToCgiService(HttpQueue *q) { HttpConn *conn; HttpPacket *packet; Cgi *cgi; MprCmd *cmd; MprBuf *buf; ssize rc, len; int err; if ((cgi = q->queueData) == 0) { return; } assert(q == cgi->writeq); cmd = cgi->cmd; assert(cmd); conn = cgi->conn; for (packet = httpGetPacket(q); packet; packet = httpGetPacket(q)) { if ((buf = packet->content) == 0) { /* End packet */ continue; } len = mprGetBufLength(buf); rc = mprWriteCmd(cmd, MPR_CMD_STDIN, mprGetBufStart(buf), len); if (rc < 0) { err = mprGetError(); if (err == EINTR) { continue; } else if (err == EAGAIN || err == EWOULDBLOCK) { httpPutBackPacket(q, packet); break; } mprLog(2, "CGI: write to gateway failed for %d bytes, rc %d, errno %d", len, rc, mprGetOsError()); mprCloseCmdFd(cmd, MPR_CMD_STDIN); httpDiscardQueueData(q, 1); httpError(conn, HTTP_CODE_BAD_GATEWAY, "Cannot write body data to CGI gateway"); break; } mprTrace(6, "CGI: browserToCgiService %d/%d, qmax %d", rc, len, q->max); mprAdjustBufStart(buf, rc); if (mprGetBufLength(buf) > 0) { httpPutBackPacket(q, packet); break; } } if (q->count > 0) { /* Wait for writable event so cgiCallback can recall this routine */ mprEnableCmdEvents(cmd, MPR_CMD_STDIN); } else if (conn->rx->eof) { mprCloseCmdFd(cmd, MPR_CMD_STDIN); } else { mprDisableCmdEvents(cmd, MPR_CMD_STDIN); } }
/* Default callback routine for the mprRunCmd routines. Uses may supply their own callback instead of this routine. The callback is run whenever there is I/O to read/write to the CGI gateway. */ static void defaultCmdCallback(MprCmd *cmd, int channel, void *data) { MprBuf *buf; ssize len, space; int errCode; /* Note: stdin, stdout and stderr are named from the client's perspective */ buf = 0; switch (channel) { case MPR_CMD_STDIN: return; case MPR_CMD_STDOUT: buf = cmd->stdoutBuf; break; case MPR_CMD_STDERR: buf = cmd->stderrBuf; break; default: /* Child death notification */ return; } /* Read and aggregate the result into a single string */ space = mprGetBufSpace(buf); if (space < (ME_BUFSIZE / 4)) { if (mprGrowBuf(buf, ME_BUFSIZE) < 0) { mprCloseCmdFd(cmd, channel); return; } space = mprGetBufSpace(buf); } len = mprReadCmd(cmd, channel, mprGetBufEnd(buf), space); errCode = mprGetError(); if (len <= 0) { if (len == 0 || (len < 0 && !(errCode == EAGAIN || errCode == EWOULDBLOCK))) { mprCloseCmdFd(cmd, channel); return; } } else { mprAdjustBufEnd(buf, len); } mprAddNullToBuf(buf); mprEnableCmdEvents(cmd, channel); }
static void writeToCGI(MaQueue *q) { MaConn *conn; MaPacket *packet; MprCmd *cmd; MprBuf *buf; int len, rc, err; cmd = (MprCmd*) q->pair->queueData; mprAssert(cmd); conn = q->conn; for (packet = maGet(q); packet && !conn->requestFailed; packet = maGet(q)) { buf = packet->content; len = mprGetBufLength(buf); mprAssert(len > 0); rc = mprWriteCmdPipe(cmd, MPR_CMD_STDIN, mprGetBufStart(buf), len); mprLog(q, 5, "CGI: write %d bytes to gateway. Rc rc %d, errno %d", len, rc, mprGetOsError()); if (rc < 0) { err = mprGetError(); if (err == EINTR) { continue; } else if (err == EAGAIN || err == EWOULDBLOCK) { break; } mprLog(q, 2, "CGI: write to gateway failed for %d bytes, rc %d, errno %d", len, rc, mprGetOsError()); mprCloseCmdFd(cmd, MPR_CMD_STDIN); maFailRequest(conn, MPR_HTTP_CODE_BAD_GATEWAY, "Can't write body data to CGI gateway"); break; } else { mprLog(q, 5, "CGI: write to gateway %d bytes asked to write %d", rc, len); mprAdjustBufStart(buf, rc); if (mprGetBufLength(buf) > 0) { maPutBack(q, packet); } else { maFreePacket(q, packet); } } } }
static void readFromCgi(Cgi *cgi, int channel) { HttpConn *conn; HttpPacket *packet; HttpTx *tx; HttpQueue *q, *writeq; MprCmd *cmd; ssize nbytes; int err; cmd = cgi->cmd; conn = cgi->conn; tx = conn->tx; q = cgi->readq; writeq = conn->writeq; assert(conn->sock); assert(conn->state > HTTP_STATE_BEGIN); if (tx->finalized) { mprCloseCmdFd(cmd, channel); } while (mprGetCmdFd(cmd, channel) >= 0 && !tx->finalized && writeq->count < writeq->max) { if ((packet = cgi->headers) != 0) { if (mprGetBufSpace(packet->content) < ME_MAX_BUFFER && mprGrowBuf(packet->content, ME_MAX_BUFFER) < 0) { break; } } else if ((packet = httpCreateDataPacket(ME_MAX_BUFFER)) == 0) { break; } nbytes = mprReadCmd(cmd, channel, mprGetBufEnd(packet->content), ME_MAX_BUFFER); if (nbytes < 0) { err = mprGetError(); if (err == EINTR) { continue; } else if (err == EAGAIN || err == EWOULDBLOCK) { break; } mprCloseCmdFd(cmd, channel); break; } else if (nbytes == 0) { mprCloseCmdFd(cmd, channel); break; } else { traceData(cmd, mprGetBufEnd(packet->content), nbytes); mprAdjustBufEnd(packet->content, nbytes); } if (channel == MPR_CMD_STDERR) { mprLog("error cgi", 0, "CGI failed uri=\"%s\", details: %s", conn->rx->uri, mprGetBufStart(packet->content)); httpSetStatus(conn, HTTP_CODE_SERVICE_UNAVAILABLE); cgi->seenHeader = 1; } if (!cgi->seenHeader) { if (!parseCgiHeaders(cgi, packet)) { cgi->headers = packet; return; } cgi->headers = 0; cgi->seenHeader = 1; } if (!tx->finalizedOutput && httpGetPacketLength(packet) > 0) { /* Put the data to the CGI readq, then cgiToBrowserService will take care of it */ httpPutPacket(q, packet); } } }
static void netOutgoingService(HttpQueue *q) { HttpConn *conn; HttpTx *tx; ssize written; int errCode; conn = q->conn; tx = conn->tx; conn->lastActivity = conn->http->now; mprAssert(conn->sock); if (!conn->sock || conn->connectorComplete) { return; } if (tx->flags & HTTP_TX_NO_BODY) { httpDiscardQueueData(q, 1); } if ((tx->bytesWritten + q->count) > conn->limits->transmissionBodySize) { httpError(conn, HTTP_CODE_REQUEST_TOO_LARGE | ((tx->bytesWritten) ? HTTP_ABORT : 0), "Http transmission aborted. Exceeded transmission max body of %,Ld bytes", conn->limits->transmissionBodySize); if (tx->bytesWritten) { httpConnectorComplete(conn); return; } } if (tx->flags & HTTP_TX_SENDFILE) { /* Relay via the send connector */ if (tx->file == 0) { if (tx->flags & HTTP_TX_HEADERS_CREATED) { tx->flags &= ~HTTP_TX_SENDFILE; } else { tx->connector = conn->http->sendConnector; httpSendOpen(q); } } if (tx->file) { httpSendOutgoingService(q); return; } } while (q->first || q->ioIndex) { if (q->ioIndex == 0 && buildNetVec(q) <= 0) { break; } /* Issue a single I/O request to write all the blocks in the I/O vector */ mprAssert(q->ioIndex > 0); written = mprWriteSocketVector(conn->sock, q->iovec, q->ioIndex); LOG(5, "Net connector wrote %d, written so far %Ld, q->count %d/%d", written, tx->bytesWritten, q->count, q->max); if (written < 0) { errCode = mprGetError(q); if (errCode == EAGAIN || errCode == EWOULDBLOCK) { /* Socket full, wait for an I/O event */ httpSocketBlocked(conn); break; } if (errCode != EPIPE && errCode != ECONNRESET) { LOG(5, "netOutgoingService write failed, error %d", errCode); } httpError(conn, HTTP_ABORT | HTTP_CODE_COMMS_ERROR, "Write error %d", errCode); httpConnectorComplete(conn); break; } else if (written == 0) { /* Socket full, wait for an I/O event */ httpSocketBlocked(conn); break; } else if (written > 0) { tx->bytesWritten += written; freeNetPackets(q, written); adjustNetVec(q, written); } } if (q->ioCount == 0) { if ((q->flags & HTTP_QUEUE_EOF)) { httpConnectorComplete(conn); } else { httpNotifyWritable(conn); } } }
int APIENTRY WinMain(HINSTANCE inst, HINSTANCE junk, char *command, int junk2) { char **argv; cchar *documents, *home, *logs, *port, *ssl, *argp, *path, *contents, *revised; cchar *user, *group, *cache, *modules, *bak; int argc, err, nextArg; static void logHandler(int flags, int level, cchar *msg); if (mprCreate(0, NULL, MPR_USER_EVENTS_THREAD) == NULL) { exit(1); } if ((argc = mprMakeArgv(command, &argv, MPR_ARGV_ARGS_ONLY)) < 0) { return FALSE; } mprSetLogHandler(logHandler); #else int main(int argc, char **argv) { cchar *documents, *home, *logs, *port, *ssl, *argp, *path, *contents, *revised; cchar *user, *group, *cache, *modules, *bak; int err, nextArg; if (mprCreate(argc, argv, MPR_USER_EVENTS_THREAD) == NULL) { exit(1); } #endif documents = home = port = ssl = logs = user = group = cache = modules = 0; for (err = 0, nextArg = 1; nextArg < argc; nextArg++) { argp = argv[nextArg]; if (*argp != '-') { break; } if (smatch(argp, "--documents") && nextArg < argc) { documents = argv[++nextArg]; } else if (smatch(argp, "--home") && nextArg < argc) { home = argv[++nextArg]; } else if (smatch(argp, "--logs") && nextArg < argc) { logs = argv[++nextArg]; } else if (smatch(argp, "--port") && nextArg < argc) { port = argv[++nextArg]; } else if (smatch(argp, "--ssl") && nextArg < argc) { ssl = argv[++nextArg]; } else if (smatch(argp, "--user") && nextArg < argc) { user = argv[++nextArg]; } else if (smatch(argp, "--group") && nextArg < argc) { group = argv[++nextArg]; } else if (smatch(argp, "--cache") && nextArg < argc) { cache = argv[++nextArg]; } else if (smatch(argp, "--modules") && nextArg < argc) { modules = argv[++nextArg]; } else { err++; } } if (nextArg != (argc - 1)) { err++; } if (err) { #if BIT_WIN_LIKE mprUserError("Bad command line:"); #else mprUserError("Bad command line:\n" " Usage: pathConfig [options]\n" " Switches:\n" " --cache dir # Cache dir" " --documents dir # Static documents directory" " --group groupname # Group name" " --home dir # Server home directory" " --logs dir # Log directory" " --modules dir # moduels dir" " --port number # HTTP port number" " --user username # User name"); #endif return 1; } path = argv[nextArg++]; if ((contents = mprReadPathContents(path, NULL)) == 0) { mprUserError("Cannot read %s", path); return 1; } if (port) { contents = replace(contents, "Listen 80", "Listen %s", port); } if (ssl) { contents = replace(contents, "443", ssl); } if (documents) { contents = replace(contents, "DocumentRoot", "DocumentRoot \"%s\"", documents); } if (home) { contents = replace(contents, "ServerRoot", "ServerRoot \"%s\"", home); } if (logs) { contents = replace(contents, "ErrorLog", "ErrorLog \"%s\"", mprJoinPath(logs, "error.log")); contents = replace(contents, "AccessLog", "AccessLog \"%s\"", mprJoinPath(logs, "access.log")); } if (user) { contents = replace(contents, "UserAccount", "UserAccount %s", user); } if (group) { contents = replace(contents, "GroupAccount", "GroupAccount %s", group); } if (cache) { contents = replace(contents, "EspDir cache", "EspDir cache \"%s\"", cache); } if (modules) { contents = replace(contents, "LoadModulePath", "LoadModulePath \"%s\"", modules); } revised = mprGetTempPath(mprGetPathDir(path)); if (mprWritePathContents(revised, contents, -1, 0644) < 0) { mprUserError("Cannot write %s", revised); } bak = sfmt("%s.bak", path); mprDeletePath(bak); if (rename(path, bak) < 0) { mprUserError("Cannot save %s to %s: 0x%x", path, bak, mprGetError()); } mprDeletePath(path); if (rename(revised, path) < 0) { mprUserError("Cannot rename %s to %s: 0x%x", revised, path, mprGetError()); rename(bak, path); } return 0; }
static void cgiEvent(MaQueue *q, MprCmd *cmd, int channel) { MaConn *conn; MaResponse *resp; MprBuf *buf; int space, nbytes, err; mprLog(cmd, 6, "CGI callback channel %d", channel); buf = 0; conn = q->conn; resp = conn->response; mprAssert(resp); cmd->lastActivity = mprGetTime(cmd); switch (channel) { case MPR_CMD_STDIN: writeToCGI(q->pair); return; case MPR_CMD_STDOUT: buf = cmd->stdoutBuf; break; case MPR_CMD_STDERR: buf = cmd->stderrBuf; break; } mprAssert(buf); mprResetBufIfEmpty(buf); /* Come here for CGI stdout, stderr events. ie. reading data from the CGI program. */ while (mprGetCmdFd(cmd, channel) >= 0) { /* Read as much data from the CGI as possible */ do { if ((space = mprGetBufSpace(buf)) == 0) { mprGrowBuf(buf, MA_BUFSIZE); if ((space = mprGetBufSpace(buf)) == 0) { break; } } nbytes = mprReadCmdPipe(cmd, channel, mprGetBufEnd(buf), space); mprLog(q, 5, "CGI: read from gateway %d on channel %d. errno %d", nbytes, channel, nbytes >= 0 ? 0 : mprGetOsError()); if (nbytes < 0) { err = mprGetError(); if (err == EINTR) { continue; } else if (err == EAGAIN || err == EWOULDBLOCK) { break; } mprLog(cmd, 5, "CGI read error %d for %", mprGetError(), (channel == MPR_CMD_STDOUT) ? "stdout" : "stderr"); mprCloseCmdFd(cmd, channel); } else if (nbytes == 0) { /* This may reap the terminated child and thus clear cmd->process if both stderr and stdout are closed. */ mprLog(cmd, 5, "CGI EOF for %s", (channel == MPR_CMD_STDOUT) ? "stdout" : "stderr"); mprCloseCmdFd(cmd, channel); break; } else { mprLog(cmd, 5, "CGI read %d bytes from %s", nbytes, (channel == MPR_CMD_STDOUT) ? "stdout" : "stderr"); mprAdjustBufEnd(buf, nbytes); traceData(cmd, mprGetBufStart(buf), nbytes); } } while ((space = mprGetBufSpace(buf)) > 0); if (mprGetBufLength(buf) == 0) { return; } if (channel == MPR_CMD_STDERR) { /* If we have an error message, send that to the client */ if (mprGetBufLength(buf) > 0) { mprAddNullToBuf(buf); mprLog(conn, 4, mprGetBufStart(buf)); if (writeToClient(q, cmd, buf, channel) < 0) { return; } maSetResponseCode(conn, MPR_HTTP_CODE_SERVICE_UNAVAILABLE); cmd->userFlags |= MA_CGI_SEEN_HEADER; cmd->status = 0; } } else { if (!(cmd->userFlags & MA_CGI_SEEN_HEADER) && !parseHeader(conn, cmd)) { return; } if (cmd->userFlags & MA_CGI_SEEN_HEADER) { if (writeToClient(q, cmd, buf, channel) < 0) { return; } } } } }
static void readFromCgi(Cgi *cgi, int channel) { HttpConn *conn; HttpPacket *packet; HttpTx *tx; HttpQueue *q, *writeq; MprCmd *cmd; ssize nbytes; int err; cmd = cgi->cmd; conn = cgi->conn; tx = conn->tx; q = cgi->readq; writeq = conn->writeq; assert(conn->sock); assert(conn->state > HTTP_STATE_BEGIN); if (tx->finalized) { mprCloseCmdFd(cmd, channel); } while (mprGetCmdFd(cmd, channel) >= 0 && !tx->finalized && writeq->count < writeq->max) { if ((packet = cgi->headers) != 0) { if (mprGetBufSpace(packet->content) < ME_MAX_BUFFER && mprGrowBuf(packet->content, ME_MAX_BUFFER) < 0) { break; } } else if ((packet = httpCreateDataPacket(ME_MAX_BUFFER)) == 0) { break; } nbytes = mprReadCmd(cmd, channel, mprGetBufEnd(packet->content), ME_MAX_BUFFER); if (nbytes < 0) { err = mprGetError(); if (err == EINTR) { continue; } else if (err == EAGAIN || err == EWOULDBLOCK) { break; } mprTrace(6, "CGI: Gateway read error %d for %s", err, (channel == MPR_CMD_STDOUT) ? "stdout" : "stderr"); mprCloseCmdFd(cmd, channel); break; } else if (nbytes == 0) { mprTrace(6, "CGI: Gateway EOF for %s, pid %d", (channel == MPR_CMD_STDOUT) ? "stdout" : "stderr", cmd->pid); mprCloseCmdFd(cmd, channel); break; } else { mprTrace(6, "CGI: Gateway read %d bytes from %s", nbytes, (channel == MPR_CMD_STDOUT) ? "stdout" : "stderr"); traceData(cmd, mprGetBufEnd(packet->content), nbytes); mprAdjustBufEnd(packet->content, nbytes); } if (channel == MPR_CMD_STDERR) { // FUTURE - should be an option to keep going despite stderr output mprError("CGI: Error for \"%s\"\n\n%s", conn->rx->uri, mprGetBufStart(packet->content)); httpSetStatus(conn, HTTP_CODE_SERVICE_UNAVAILABLE); cgi->seenHeader = 1; } if (!cgi->seenHeader) { if (!parseCgiHeaders(cgi, packet)) { cgi->headers = packet; return; } cgi->headers = 0; cgi->seenHeader = 1; } if (!tx->finalizedOutput && httpGetPacketLength(packet) > 0) { /* Put the data to the CGI readq, then cgiToBrowserService will take care of it */ httpPutPacket(q, packet); } } }
PUBLIC void httpSendOutgoingService(HttpQueue *q) { HttpConn *conn; HttpTx *tx; MprFile *file; MprOff written; int errCode; conn = q->conn; tx = conn->tx; conn->lastActivity = conn->http->now; if (tx->finalizedConnector) { return; } if (tx->flags & HTTP_TX_NO_BODY) { httpDiscardQueueData(q, 1); } if ((tx->bytesWritten + q->ioCount) > conn->limits->txBodySize && conn->limits->txBodySize < HTTP_UNLIMITED) { httpLimitError(conn, HTTP_ABORT | HTTP_CODE_REQUEST_TOO_LARGE | ((tx->bytesWritten) ? HTTP_ABORT : 0), "Http transmission aborted. Exceeded max body of %lld bytes", conn->limits->txBodySize); if (tx->bytesWritten) { httpFinalizeConnector(conn); return; } } tx->writeBlocked = 0; if (q->ioIndex == 0) { buildSendVec(q); } /* No need to loop around as send file tries to write as much of the file as possible. If not eof, will always have the socket blocked. */ file = q->ioFile ? tx->file : 0; written = mprSendFileToSocket(conn->sock, file, q->ioPos, q->ioCount, q->iovec, q->ioIndex, NULL, 0); if (written < 0) { errCode = mprGetError(); if (errCode == EAGAIN || errCode == EWOULDBLOCK) { /* Socket full, wait for an I/O event */ tx->writeBlocked = 1; } else { if (errCode != EPIPE && errCode != ECONNRESET && errCode != ECONNABORTED && errCode != ENOTCONN) { httpError(conn, HTTP_ABORT | HTTP_CODE_COMMS_ERROR, "sendConnector: error, errCode %d", errCode); } else { httpDisconnect(conn); } httpFinalizeConnector(conn); } httpTrace(conn, "connection.io.error", "error", "msg:'Connector write error',errno:%d", errCode); } else if (written > 0) { tx->bytesWritten += written; freeSendPackets(q, written); adjustSendVec(q, written); } if (q->first && q->first->flags & HTTP_PACKET_END) { httpFinalizeConnector(conn); httpGetPacket(q); } }
/* Initialize the send connector for a request */ PUBLIC int httpSendOpen(HttpQueue *q) { HttpConn *conn; HttpTx *tx; conn = q->conn; tx = conn->tx; if (tx->connector != conn->http->sendConnector) { httpAssignQueue(q, tx->connector, HTTP_QUEUE_TX); tx->connector->open(q); return 0; } if (!(tx->flags & HTTP_TX_NO_BODY)) { assert(tx->fileInfo.valid); if (tx->fileInfo.size > conn->limits->txBodySize && conn->limits->txBodySize < HTTP_UNLIMITED) { httpLimitError(conn, HTTP_ABORT | HTTP_CODE_REQUEST_TOO_LARGE, "Http transmission aborted. File size exceeds max body of %lld bytes", conn->limits->txBodySize); return MPR_ERR_CANT_OPEN; } tx->file = mprOpenFile(tx->filename, O_RDONLY | O_BINARY, 0); if (tx->file == 0) { httpError(conn, HTTP_CODE_NOT_FOUND, "Cannot open document: %s, err %d", tx->filename, mprGetError()); } } return 0; }