static bool get_first_addr(const char* hostname, const char* servname, const struct addrinfo* hints, struct sockaddr* addr, size_t addr_len, ls_err* err) { int status; struct addrinfo* res, * p, * found; bool ret = false; if ( ( status = getaddrinfo(hostname, servname, hints, &res) ) != 0 ) { if (err != NULL) { /* HACK. GAI errors on linux are negative, positive on OSX */ err->code = ls_err_gai(status); err->message = gai_strerror(status); err->function = __func__; err->file = __FILE__; err->line = __LINE__; } return false; } /* Take the first v6 address, otherwise take the first v4 address */ found = NULL; for (p = res; p != NULL; p = p->ai_next) { if (p->ai_family == AF_INET6) { found = p; break; } else if ( !found && (p->ai_family == AF_INET) ) { found = p; } } if (found) { if (addr_len < found->ai_addrlen) { LS_ERROR(err, LS_ERR_OVERFLOW); } else { memcpy(addr, found->ai_addr, found->ai_addrlen); ret = true; } } else { LS_ERROR(err, LS_ERR_NOT_FOUND); } freeaddrinfo(res); return ret; }
Server::Server(std::shared_ptr<Logger> logger) : _logger(logger), _listenSock(-1), _epollFd(-1), _eventFd(-1), _maxKeepAliveDrops(0), _lameConnectionTimeoutSeconds(DefaultLameConnectionTimeoutSeconds), _nextDeadConnectionCheck(0), _threadId(0), _terminate(false), _expectedTerminate(false) { _epollFd = epoll_create(10); if (_epollFd == -1) { LS_ERROR(_logger, "Unable to create epoll: " << getLastError()); return; } _eventFd = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC); if (_eventFd == -1) { LS_ERROR(_logger, "Unable to create event FD: " << getLastError()); return; } epoll_event eventWake = { EPOLLIN, { &_eventFd } }; if (epoll_ctl(_epollFd, EPOLL_CTL_ADD, _eventFd, &eventWake) == -1) { LS_ERROR(_logger, "Unable to add wake socket to epoll: " << getLastError()); return; } }
void Server::handleAccept() { sockaddr_in address; socklen_t addrLen = sizeof(address); int fd = ::accept(_listenSock, reinterpret_cast<sockaddr*>(&address), &addrLen); if (fd == -1) { LS_ERROR(_logger, "Unable to accept: " << getLastError()); return; } if (!configureSocket(fd)) { ::close(fd); return; } LS_INFO(_logger, formatAddress(address) << " : Accepted on descriptor " << fd); Connection* newConnection = new Connection(_logger, *this, fd, address); epoll_event event = { EPOLLIN, { newConnection } }; if (epoll_ctl(_epollFd, EPOLL_CTL_ADD, fd, &event) == -1) { LS_ERROR(_logger, "Unable to add socket to epoll: " << getLastError()); delete newConnection; ::close(fd); return; } _connections.insert(std::make_pair(newConnection, time(nullptr))); }
void LshttpdMain::gracefulRestart() { LS_DBG_L("Graceful Restart... "); close(m_fdAdmin); broadcastSig(SIGTERM, 1); s_iRunning = 0; m_pidFile.closePidFile(); m_pServer->passListeners(); int pid = fork(); if (!pid) { char achCmd[1024]; int fd = StdErrLogger::getInstance().getStdErr(); if (fd != 2) close(fd); int len = getFullPath("bin/litespeed", achCmd, 1024); achCmd[len - 10] = 0; chdir(achCmd); achCmd[len - 10] = '/'; if (execl(achCmd, "litespeed", NULL)) LS_ERROR("Failed to start new instance of LiteSpeed Web server!"); exit(0); } if (pid == -1) LS_ERROR("Failed to restart the server!"); }
XmlNode *ConfigCtx::parseFile(const char *configFilePath, const char *rootTag) { char achError[4096]; XmlTreeBuilder tb; XmlNode *pRoot = tb.parse(configFilePath, achError, 4095); if (pRoot == NULL) { LS_ERROR(this, "%s", achError); return NULL; } // basic validation if (strcmp(pRoot->getName(), rootTag) != 0) { LS_ERROR(this, "%s: root tag expected: <%s>, real root tag : <%s>!\n", configFilePath, rootTag, pRoot->getName()); delete pRoot; return NULL; } #ifdef TEST_OUTPUT_PLAIN_CONF char sPlainFile[512] = {0}; strcpy(sPlainFile, configFilePath); strcat(sPlainFile, ".txt"); // plainconf::testOutputConfigFile( pRoot, sPlainFile ); #endif return pRoot; }
bool Server::configureSocket(int fd) const { if (!makeNonBlocking(fd)) { return false; } const int yesPlease = 1; if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yesPlease, sizeof(yesPlease)) == -1) { LS_ERROR(_logger, "Unable to set reuse socket option: " << getLastError()); return false; } if (_maxKeepAliveDrops > 0) { if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &yesPlease, sizeof(yesPlease)) == -1) { LS_ERROR(_logger, "Unable to enable keepalive: " << getLastError()); return false; } const int oneSecond = 1; if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPIDLE, &oneSecond, sizeof(oneSecond)) == -1) { LS_ERROR(_logger, "Unable to set idle probe: " << getLastError()); return false; } if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPINTVL, &oneSecond, sizeof(oneSecond)) == -1) { LS_ERROR(_logger, "Unable to set idle interval: " << getLastError()); return false; } if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPCNT, &_maxKeepAliveDrops, sizeof(_maxKeepAliveDrops)) == -1) { LS_ERROR(_logger, "Unable to set keep alive count: " << getLastError()); return false; } } return true; }
static int copyFile(const char *pSrc, const char *pDest) { int fd = open(pSrc, O_RDONLY); if (fd == -1) { LS_ERROR("Can not open Awstats model configuration file[%s]: %s", pSrc, strerror(errno)); return LS_FAIL; } int fdDest = open(pDest, O_RDWR | O_CREAT | O_TRUNC, 0644); if (fdDest == -1) { LS_ERROR("Can not create file [%s], %s", pDest, strerror(errno)); close(fd); return LS_FAIL; } int ret = 0; int len; char achBuf[8192]; while ((len = read(fd, achBuf, 8192)) > 0) { if (write(fdDest, achBuf, len) != len) { LS_ERROR("Can not write to file [%s], disk full?", pDest); unlink(pDest); ret = -1; break; } } close(fd); close(fdDest); return ret; }
int renameLogFile(const char *pLogPath, const char *pSrcSuffix, const char *pDestSuffix) { struct stat st; char achDest[1024]; char achSrc[1024]; ls_snprintf(achDest, sizeof(achDest), "%s%s", pLogPath, pDestSuffix); if (ls_fio_stat(achDest, &st) == 0) { LS_ERROR("File already exists: [%s], Awstats updating" " might be in progress.", achDest); return LS_FAIL; } ls_snprintf(achSrc, sizeof(achSrc), "%s%s", pLogPath, pSrcSuffix); if (ls_fio_stat(achSrc, &st) == -1) { LS_ERROR("Log file does not exist: [%s], cannot update" " Awstats statistics", achSrc); return LS_FAIL; } if (st.st_size == 0) return LS_FAIL; if (rename(achSrc, achDest) == -1) { LS_ERROR("Cannot rename file from [%s] to [%s]: %s", achSrc, achDest, strerror(errno)); return LS_FAIL; } return 0; }
int ConfigCtx::getRootPath(const char *&pRoot, const char *&pFile) { int offset = 1; if (*pFile == '$') { if (strncasecmp(pFile + 1, VH_ROOT, 7) == 0) { if (s_aVhRoot[0]) { pRoot = s_aVhRoot; pFile += 8; } else { LS_ERROR(this, "Virtual host root path is not available for %s.", pFile); return LS_FAIL; } } else if (strncasecmp(pFile + 1, DOC_ROOT, 8) == 0) { if (s_aDocRoot[0]) { pRoot = getDocRoot(); pFile += 9; } else { LS_ERROR(this, "Document root path is not available for %s.", pFile); return LS_FAIL; } } else if (strncasecmp(pFile + 1, SERVER_ROOT, 11) == 0) { pRoot = MainServerConfig::getInstance().getServerRoot(); pFile += 12; } } else { offset = 0; if ((*pFile != '/') && (s_aDocRoot[0])) pRoot = getDocRoot(); } if ((offset) && (*pFile == '/')) ++pFile; return 0; }
const char *ConfigCtx::getTag(const XmlNode *pNode, const char *pName, int bKeyName) { if (pNode == NULL) { LS_ERROR(this, "pNode is NULL while calling getTag( name: %s )" , pName); return NULL; } const char *pRet = pNode->getChildValue(pName, bKeyName); if (!pRet) LS_ERROR(this, MISSING_TAG_IN, pName, pNode->getName()); return pRet; }
/*** * We try to make log available even if errorlog is not setup. * So, at the beginning, we put the logs to StringList plainconf::errorLogList by call logToMem() * once flushErrorLog() is called, it means errorlog is setup, and this function will save all * the buffered logs to errorlog file. * Then if logToMem still be called, it should not access the stringlist anymore, * but just access the errorlog directly. */ void plainconf::logToMem(char errorLevel, const char *format, ...) { char buf[512]; sprintf(buf, "%c[PlainConf] ", errorLevel); int len = strlen(buf); if (gModuleList.size() > 0) { XmlNode *pCurNode = (XmlNode *)gModuleList.back(); sprintf(buf + len, "[%s:%s] ", pCurNode->getName(), ((pCurNode->getValue() == NULL) ? "" : pCurNode->getValue())); } len = strlen(buf); va_list ap; va_start(ap, format); int ret = vsnprintf(buf + len, 512 - len, format, ap); va_end(ap); if (!bErrorLogSetup) errorLogList.add(buf, ret + len); else { if (errorLevel == LOG_LEVEL_ERR) LS_ERROR(buf + 1); else LS_INFO(buf + 1); } }
int SslContext::setMultiKeyCertFile(const char *pKeyFile, int iKeyType, const char *pCertFile, int iCertType, int chained) { int i, iCertLen, iKeyLen, iLoaded = 0; char achCert[max_path_len], achKey[max_path_len]; const char *apExt[max_certs] = {"", ".rsa", ".dsa", ".ecc"}; char *pCertCur, *pKeyCur; iCertLen = snprintf(achCert, max_path_len, "%s", pCertFile); pCertCur = achCert + iCertLen; iKeyLen = snprintf(achKey, max_path_len, "%s", pKeyFile); pKeyCur = achKey + iKeyLen; for (i = 0; i < max_certs; ++i) { snprintf(pCertCur, max_path_len - iCertLen, "%s", apExt[i]); snprintf(pKeyCur, max_path_len - iKeyLen, "%s", apExt[i]); if ((access(achCert, F_OK) == 0) && (access(achKey, F_OK) == 0)) { if (setKeyCertificateFile(achKey, iKeyType, achCert, iCertType, chained) == false) { LS_ERROR("Failed to load key file %s and cert file %s", achKey, achCert); return false; } iLoaded = 1; } } return (iLoaded == 1); }
LS_API bool tube_data(tube *t, uint8_t *data, size_t len, ls_err *err) { // max size for CBOR preamble 19 bytes: // 1(map|27) 8(length) 1(key:0) 1(bstr|27) 8(length) //uint8_t preamble[19]; cn_cbor *map; cn_cbor *cdata; bool ret = false; cn_cbor_context ctx; assert(t); if (len == 0) { return tube_send(t, SPUD_DATA, false, false, NULL, 0, 0, err); } if (!_map_create(&ctx, &map, err)) { return false; } // TODO: the whole point of the iov system is so that we don't have to copy // the data here. Which we just did. Please fix. if (!(cdata = cn_cbor_data_create(data, len, &ctx, NULL)) || !cn_cbor_mapput_int(map, 0, cdata, &ctx, NULL)) { LS_ERROR(err, LS_ERR_NO_MEMORY); goto cleanup; } ret = tube_send_cbor(t, SPUD_DATA, false, false, map, err); cleanup: ls_pool_destroy((ls_pool*)ctx.context); return ret; }
static bool get_rand_buf(void* buf, size_t sz, ls_err* err) { #ifdef HAVE_ARC4RANDOM UNUSED_PARAM(err); arc4random_buf(buf, sz); return true; #elif defined(HAVE__DEV_URANDOM) /* TODO: think about pre-reading entropy to avoid blocking I/O here. */ /* *certainly* don't open/close the file every time. */ /* Also, read these: */ /* * http://insanecoding.blogspot.com/2014/05/a-good-idea-with-bad-usage-devurandom.html * */ /* http://www.2uo.de/myths-about-urandom/ */ /* --- Yes. Move to openSSL or someting? */ FILE* rfile = fopen("/dev/urandom", "r"); size_t nread; if (!rfile) { LS_ERROR(err, -errno); return false; } nread = fread(buf, 1, sz, rfile); fclose(rfile); /* Only true if size is 1. (did not understand man page) */ /* If this is untrue, something horrible has happened, and we should just */ /* stop. */ return (nread == sz); #else #error New random source needed #endif }
int ConfigCtx::getValidPath(char *dest, const char *path, const char *desc) { if (getAbsolutePath(dest, path) != 0) { LS_ERROR(INVAL_PATH, desc, path); return LS_FAIL; } if (access(dest, F_OK) != 0) { LS_ERROR(INACCESSIBLE_PATH, desc, dest); return LS_FAIL; } return 0; }
void LocalWorkerConfig::configExtAppUserGroup(const XmlNode *pNode, int iType) { const char *pUser = pNode->getChildValue("extUser"); const char *pGroup = pNode->getChildValue("extGroup"); gid_t gid = -1; struct passwd *pw = Daemonize::configUserGroup(pUser, pGroup, gid); if (pw) { if ((int) gid == -1) gid = pw->pw_gid; if ((iType != EA_LOGGER) && ((pw->pw_uid < ServerProcessConfig::getInstance().getUidMin()) || (gid < ServerProcessConfig::getInstance().getGidMin()))) { LS_NOTICE(ConfigCtx::getCurConfigCtx(), "ExtApp suExec access denied," " UID or GID of VHost document root is smaller " "than minimum UID, GID configured. "); } else setUGid(pw->pw_uid, gid); } LS_ERROR(ConfigCtx::getCurConfigCtx(), "Invalid User Name(%s) or Group " "Name(%s)!", pUser, pGroup); }
int LshttpdMain::processAdminBuffer(char *p, char *pEnd) { while ((pEnd > p) && isspace(pEnd[-1])) --pEnd; if (pEnd - p < 14) return LS_FAIL; if (strncasecmp(pEnd - 14, "end of actions", 14) != 0) { LS_ERROR("[ADMIN] failed to read command, command buf len=%d", (int)(pEnd - p)); return LS_FAIL; } pEnd -= 14; int apply; char *pLineEnd; while (p < pEnd) { pLineEnd = (char *)memchr(p, '\n', pEnd - p); if (pLineEnd == NULL) pLineEnd = pEnd; char *pTemp = pLineEnd; while ((pLineEnd > p) && isspace(pLineEnd[-1])) --pLineEnd; *pLineEnd = 0; if (processAdminCmd(p, pLineEnd, apply)) break; p = pTemp + 1; } m_pBuilder->releaseConfigXmlTree(); if (s_iRunning > 0) m_pServer->generateStatusReport(); if (apply) applyChanges(); return 0; }
int HttpMime::processOneLine(const char *pFilePath, char *pLine, int lineNo) { pLine = StringTool::strTrim(pLine); if (strlen(pLine) == 0) return 0; char *pType = pLine ; char *pDesc ; const char *reason; while (1) { if ((pDesc = strchr(pLine, '=')) == NULL) { reason = "missing '='"; break; } *pDesc = '\0'; ++ pDesc; if (!addUpdateMIME(pType, pDesc, reason, 0)) break; else return 0; } LS_ERROR("[MIME] File %s line %d: (%s) - \"%s\"", pFilePath, lineNo, reason, pLine); return LS_FAIL; }
int HttpMime::loadMime(const char *pPropertyPath) { FILE *fpMime = fopen(pPropertyPath, "r"); if (fpMime == NULL) { LS_ERROR("[MIME] Cannot load property file: %s", pPropertyPath); return errno; } char pBuf[TEMP_BUF_LEN]; int lineNo = 0; m_pSuffixMap->release_objects(); while (! feof(fpMime)) { lineNo ++ ; if (fgets(pBuf, TEMP_BUF_LEN, fpMime)) { char *p = strchr(pBuf, '#'); if (p) *p = 0; processOneLine(pPropertyPath, pBuf, lineNo); } } fclose(fpMime); return 0; }
int SslOcspStapling::config(const XmlNode *pNode, SSL_CTX *pSSL, const char *pCAFile, char *pachCert) { setCertFile(pachCert); if (pCAFile) setCAFile(pCAFile); setRespMaxAge(ConfigCtx::getCurConfigCtx()->getLongValue(pNode, "ocspRespMaxAge", 60, 360000, 3600)); const char *pResponder = pNode->getChildValue("ocspResponder"); if (pResponder) setOcspResponder(pResponder); if (init(pSSL) == -1) { LS_ERROR(ConfigCtx::getCurConfigCtx(), "OCSP Stapling can't be enabled [%s].", getStaplingErrMsg()); return LS_FAIL; } return 0; }
//return 0 means OK, -1 means ERROR int CgidWorker::spawnCgid(int fd, char *pData, const char *secret) { int pid; const char *pChroot = ""; if (ServerProcessConfig::getInstance().getChroot() != NULL) pChroot = ServerProcessConfig::getInstance().getChroot()->c_str(); snprintf(pData, 255, "uds:/%s%s", pChroot, getConfig().getServerAddrUnixSock()); pid = fork(); //in child if (pid == 0) { CloseUnusedFd(fd); int ret = lscgid_main(fd, argv0, secret, pData); exit(ret); } else if (pid > 0) { LS_NOTICE("[PID: %d]: forked cgid: %d", getpid(), pid); m_pid = pid; return pid; } else { LS_ERROR("[PID: %d]: fork error", getpid()); return LS_FAIL; } }
void Server::checkAndDispatchEpoll(int epollMillis) { constexpr int maxEvents = 256; epoll_event events[maxEvents]; std::list<Connection*> toBeDeleted; int numEvents = epoll_wait(_epollFd, events, maxEvents, epollMillis); if (numEvents == -1) { if (errno != EINTR) { LS_ERROR(_logger, "Error from epoll_wait: " << getLastError()); } return; } if (numEvents == maxEvents) { static time_t lastWarnTime = 0; time_t now = time(nullptr); if (now - lastWarnTime >= 60) { LS_WARNING(_logger, "Full event queue; may start starving connections. " "Will warn at most once a minute"); lastWarnTime = now; } } for (int i = 0; i < numEvents; ++i) { if (events[i].data.ptr == this) { if (events[i].events & ~EPOLLIN) { LS_SEVERE(_logger, "Got unexpected event on listening socket (" << EventBits(events[i].events) << ") - terminating"); _terminate = true; break; } handleAccept(); } else if (events[i].data.ptr == &_eventFd) { if (events[i].events & ~EPOLLIN) { LS_SEVERE(_logger, "Got unexpected event on management pipe (" << EventBits(events[i].events) << ") - terminating"); _terminate = true; break; } handlePipe(); } else { auto connection = reinterpret_cast<Connection*>(events[i].data.ptr); if (handleConnectionEvents(connection, events[i].events) == Close) { toBeDeleted.push_back(connection); } } } // The connections are all deleted at the end so we've processed any other subject's // closes etc before we call onDisconnect(). for (auto it = toBeDeleted.begin(); it != toBeDeleted.end(); ++it) { auto connection = *it; if (_connections.find(connection) == _connections.end()) { LS_SEVERE(_logger, "Attempt to delete connection we didn't know about: " << (void*)connection << formatAddress(connection->getRemoteAddress())); _terminate = true; break; } LS_DEBUG(_logger, "Deleting connection: " << formatAddress(connection->getRemoteAddress())); delete connection; } }
void CallbackQueue::logState(const char *s, CallbackLinkedObj *p) { if (p) LS_DBG_M("[CallbackQueue:%s] Obj=%p Session= %p Param=%p\n", s, p, p->m_pSession, p->m_pParam); else LS_ERROR("[CallbackQueue:%s] Obj=NULL\n", s); }
bool Server::makeNonBlocking(int fd) const { int yesPlease = 1; if (ioctl(fd, FIONBIO, &yesPlease) != 0) { LS_ERROR(_logger, "Unable to make FD non-blocking: " << getLastError()); return false; } return true; }
void Server::terminate() { _expectedTerminate = true; _terminate = true; uint64_t one = 1; if (_eventFd != -1 && ::write(_eventFd, &one, sizeof(one)) == -1) { LS_ERROR(_logger, "Unable to post a wake event: " << getLastError()); } }
void Server::remove(Connection* connection) { checkThread(); epoll_event event = { 0, { connection } }; if (epoll_ctl(_epollFd, EPOLL_CTL_DEL, connection->getFd(), &event) == -1) { LS_ERROR(_logger, "Unable to remove from epoll: " << getLastError()); } _connections.erase(connection); }
bool Server::unsubscribeFromWriteEvents(Connection* connection) { epoll_event event = { EPOLLIN, { connection } }; if (epoll_ctl(_epollFd, EPOLL_CTL_MOD, connection->getFd(), &event) == -1) { LS_ERROR(_logger, "Unable to unsubscribe from write events: " << getLastError()); return false; } return true; }
void EvtcbQue::logState(const char *s, evtcbnode_s *p) { if (p) LS_DBG_M("[EvtcbQue:%s] Obj=%p Session= %p Param=%p\n", s, p, p->m_pSession, p->m_pParam); else LS_ERROR("[EvtcbQue:%s] Obj=NULL\n", s); }
int ConfigCtx::checkAccess(char *pReal) { if (HttpServerConfig::getInstance().getDeniedDir()->isDenied(pReal)) { LS_ERROR(this, "Path is in the access denied list:%s", pReal); return LS_FAIL; } return 0; }
LS_API bool ls_pktinfo_get_addr(ls_pktinfo *p, struct sockaddr *addr, socklen_t *addr_len, ls_err *err) { assert(p); assert(addr); assert(addr_len); switch (p->kind) { case HAS_INFO_4: if (*addr_len < sizeof(struct sockaddr_in)) { LS_ERROR(err, LS_ERR_OVERFLOW); return false; } { struct sockaddr_in *sa = (struct sockaddr_in*)addr; sa->sin_family = AF_INET; sa->sin_port = htons(-1); sa->sin_addr = p->info.i4.ipi_addr; *addr_len = sizeof(struct sockaddr_in); } break; case HAS_INFO_6: if (*addr_len < sizeof(struct sockaddr_in6)) { LS_ERROR(err, LS_ERR_OVERFLOW); return false; } { struct sockaddr_in6 *sa = (struct sockaddr_in6*)addr; sa->sin6_family = AF_INET6; sa->sin6_port = htons(-1); sa->sin6_addr = p->info.i6.ipi6_addr; *addr_len = sizeof(struct sockaddr_in6); } break; default: LS_ERROR(err, LS_ERR_INVALID_ARG); return false; } return true; }