Assorted fixes for reconfiguration leaks. === modified file 'src/acl/Gadgets.cc' --- src/acl/Gadgets.cc 2014-02-08 13:36:42 +0000 +++ src/acl/Gadgets.cc 2014-04-24 20:12:30 +0000 @@ -245,78 +245,92 @@ aclParseAclList(ConfigParser &, Acl::Tre Acl::Tree *tree = new Acl::Tree; tree->add(rule); tree->context(ctxTree.content(), config_input_line); assert(treep); assert(!*treep); *treep = tree; } void aclRegister(ACL *acl) { if (!acl->registered) { if (!RegisteredAcls) RegisteredAcls = new AclSet; RegisteredAcls->insert(acl); acl->registered = true; } } +/// remove registered acl from the centralized deletion set +static +void +aclDeregister(ACL *acl) +{ + if (acl->registered) { + if (RegisteredAcls) + RegisteredAcls->erase(acl); + acl->registered = false; + } +} + /*********************/ /* Destroy functions */ /*********************/ -/// helper for RegisteredAcls cleanup -static void -aclDeleteOne(ACL *acl) -{ - delete acl; -} - /// called to delete ALL Acls. void aclDestroyAcls(ACL ** head) { *head = NULL; // Config.aclList if (AclSet *acls = RegisteredAcls) { debugs(28, 8, "deleting all " << acls->size() << " ACLs"); - std::for_each(acls->begin(), acls->end(), &aclDeleteOne); - acls->clear(); + while (!acls->empty()) { + ACL *acl = *acls->begin(); + // We use centralized deletion (this function) so ~ACL should not + // delete other ACLs, but we still deregister first to prevent any + // accesses to the being-deleted ACL via RegisteredAcls. + assert(acl->registered); // make sure we are making progress + aclDeregister(acl); + delete acl; + } } } void aclDestroyAclList(ACLList **list) { debugs(28, 8, "aclDestroyAclList: invoked"); assert(list); - cbdataFree(*list); + delete *list; + *list = NULL; } void aclDestroyAccessList(acl_access ** list) { assert(list); if (*list) debugs(28, 3, "destroying: " << *list << ' ' << (*list)->name); - cbdataFree(*list); + delete *list; + *list = NULL; } /* maex@space.net (06.09.1996) * destroy an AclDenyInfoList */ void aclDestroyDenyInfoList(AclDenyInfoList ** list) { AclDenyInfoList *a = NULL; AclDenyInfoList *a_next = NULL; AclNameList *l = NULL; AclNameList *l_next = NULL; debugs(28, 8, "aclDestroyDenyInfoList: invoked"); for (a = *list; a; a = a_next) { for (l = a->acl_list; l; l = l_next) { l_next = l->next; safe_free(l); } === modified file 'src/acl/InnerNode.cc' --- src/acl/InnerNode.cc 2014-04-12 07:10:39 +0000 +++ src/acl/InnerNode.cc 2014-04-24 20:14:09 +0000 @@ -1,48 +1,50 @@ #include "squid.h" #include "acl/Acl.h" #include "acl/BoolOps.h" #include "acl/Checklist.h" +#include "acl/Gadgets.h" #include "acl/InnerNode.h" #include "cache_cf.h" #include "ConfigParser.h" #include "Debug.h" #include "globals.h" #include void Acl::InnerNode::prepareForUse() { std::for_each(nodes.begin(), nodes.end(), std::mem_fun(&ACL::prepareForUse)); } bool Acl::InnerNode::empty() const { return nodes.empty(); } void Acl::InnerNode::add(ACL *node) { assert(node != NULL); nodes.push_back(node); + aclRegister(node); } // one call parses one "acl name acltype name1 name2 ..." line // kids use this method to handle [multiple] parse() calls correctly void Acl::InnerNode::lineParse() { // XXX: not precise, may change when looping or parsing multiple lines if (!cfgline) cfgline = xstrdup(config_input_line); // expect a list of ACL names, each possibly preceeded by '!' for negation while (const char *t = ConfigParser::strtokFile()) { const bool negated = (*t == '!'); if (negated) ++t; debugs(28, 3, "looking for ACL " << t); ACL *a = ACL::FindByName(t); === modified file 'src/adaptation/AccessRule.cc' --- src/adaptation/AccessRule.cc 2013-05-13 22:48:23 +0000 +++ src/adaptation/AccessRule.cc 2014-04-24 20:12:30 +0000 @@ -1,37 +1,38 @@ #include "squid.h" +#include "acl/Tree.h" #include "acl/Gadgets.h" #include "adaptation/AccessRule.h" #include "adaptation/Service.h" #include "adaptation/ServiceGroups.h" #include "ConfigParser.h" #include "Debug.h" int Adaptation::AccessRule::LastId = 0; Adaptation::AccessRule::AccessRule(const String &aGroupId): id(++LastId), groupId(aGroupId), acl(NULL) { } Adaptation::AccessRule::~AccessRule() { - // XXX: leaking acls here? + delete acl; } void Adaptation::AccessRule::parse(ConfigParser &parser) { aclParseAccessLine("adaptation_access", parser, &acl); } void Adaptation::AccessRule::finalize() { if (!group()) { // no explicit group debugs(93,7, HERE << "no service group: " << groupId); // try to add a one-service group if (FindService(groupId) != NULL) { ServiceGroupPointer g = new SingleService(groupId); g->finalize(); // explicit groups were finalized before rules AllGroups().push_back(g); } } === modified file 'src/anyp/PortCfg.cc' --- src/anyp/PortCfg.cc 2014-04-16 21:58:08 +0000 +++ src/anyp/PortCfg.cc 2014-04-24 20:19:11 +0000 @@ -57,44 +57,46 @@ AnyP::PortCfg::PortCfg() : sslContextFlags(0), sslOptions(0) #endif { memset(&tcp_keepalive, 0, sizeof(tcp_keepalive)); } AnyP::PortCfg::~PortCfg() { if (Comm::IsConnOpen(listenConn)) { listenConn->close(); listenConn = NULL; } safe_free(name); safe_free(defaultsite); #if USE_OPENSSL safe_free(cert); safe_free(key); - safe_free(options); safe_free(cipher); + safe_free(options); + safe_free(clientca); safe_free(cafile); safe_free(capath); + safe_free(crlfile); safe_free(dhfile); safe_free(sslflags); safe_free(sslContextSessionId); #endif } AnyP::PortCfg * AnyP::PortCfg::clone() const { AnyP::PortCfg *b = new AnyP::PortCfg(); b->s = s; if (name) b->name = xstrdup(name); if (defaultsite) b->defaultsite = xstrdup(defaultsite); b->transport = transport; b->flags = flags; b->allow_direct = allow_direct; b->vhost = vhost; === modified file 'src/cache_cf.cc' --- src/cache_cf.cc 2014-04-12 17:32:34 +0000 +++ src/cache_cf.cc 2014-04-24 20:23:43 +0000 @@ -874,44 +874,49 @@ configDoConfigure(void) * Andres Kroonmaa : * Some getpwnam() implementations (Solaris?) require * an available FD < 256 for opening a FILE* to the * passwd file. * DW: * This should be safe at startup, but might still fail * during reconfigure. */ fatalf("getpwnam failed to find userid for effective user '%s'", Config.effectiveUser); return; } Config2.effectiveUserID = pwd->pw_uid; Config2.effectiveGroupID = pwd->pw_gid; #if HAVE_PUTENV if (pwd->pw_dir && *pwd->pw_dir) { + // putenv() leaks by design; avoid leaks when nothing changes + static String lastDir; + if (!lastDir.size() || lastDir != pwd->pw_dir) { + lastDir = pwd->pw_dir; int len; char *env_str = (char *)xcalloc((len = strlen(pwd->pw_dir) + 6), 1); snprintf(env_str, len, "HOME=%s", pwd->pw_dir); putenv(env_str); + } } #endif } } else { Config2.effectiveUserID = geteuid(); Config2.effectiveGroupID = getegid(); } if (NULL != Config.effectiveGroup) { struct group *grp = getgrnam(Config.effectiveGroup); if (NULL == grp) { fatalf("getgrnam failed to find groupid for effective group '%s'", Config.effectiveGroup); return; } @@ -3761,43 +3766,43 @@ parse_port_option(AnyP::PortCfg * s, cha s->generateHostCertificates = true; } else if (strcmp(token, "generate-host-certificates=off") == 0) { s->generateHostCertificates = false; } else if (strncmp(token, "dynamic_cert_mem_cache_size=", 28) == 0) { parseBytesOptionValue(&s->dynamicCertMemCacheSize, B_BYTES_STR, token + 28); #endif } else { debugs(3, DBG_CRITICAL, "FATAL: Unknown http(s)_port option '" << token << "'."); self_destruct(); } } void add_http_port(char *portspec) { AnyP::PortCfg *s = new AnyP::PortCfg(); s->setTransport("HTTP"); parsePortSpecification(s, portspec); // we may need to merge better if the above returns a list with clones assert(s->next == NULL); - s->next = cbdataReference(Config.Sockaddr.http); - cbdataReferenceDone(Config.Sockaddr.http); - Config.Sockaddr.http = cbdataReference(s); + // no cbdata locking here: Config owns the ports (deleted in free_PortCfg) + s->next = Config.Sockaddr.http; + Config.Sockaddr.http = s; } static void parsePortCfg(AnyP::PortCfg ** head, const char *optionName) { const char *protocol = NULL; if (strcmp(optionName, "http_port") == 0 || strcmp(optionName, "ascii_port") == 0) protocol = "http"; else if (strcmp(optionName, "https_port") == 0) protocol = "https"; if (!protocol) { self_destruct(); return; } char *token = ConfigParser::NextToken(); if (!token) { self_destruct(); @@ -3820,42 +3825,42 @@ parsePortCfg(AnyP::PortCfg ** head, cons if (s->flags.tunnelSslBumping && !hijacked) { debugs(3, DBG_CRITICAL, "FATAL: ssl-bump on https_port requires tproxy/intercept which is missing."); self_destruct(); } if (hijacked && !s->flags.tunnelSslBumping) { debugs(3, DBG_CRITICAL, "FATAL: tproxy/intercept on https_port requires ssl-bump which is missing."); self_destruct(); } } #endif if (Ip::EnableIpv6&IPV6_SPECIAL_SPLITSTACK && s->s.isAnyAddr()) { // clone the port options from *s to *(s->next) s->next = cbdataReference(s->clone()); s->next->s.setIPv4(); debugs(3, 3, AnyP::UriScheme(s->transport.protocol).c_str() << "_port: clone wildcard address for split-stack: " << s->s << " and " << s->next->s); } while (*head) head = &(*head)->next; - - *head = cbdataReference(s); + // no cbdata locking here: Config owns the ports (deleted in free_PortCfg) + *head = s; } static void dump_generic_port(StoreEntry * e, const char *n, const AnyP::PortCfg * s) { char buf[MAX_IPSTRLEN]; storeAppendPrintf(e, "%s %s", n, s->s.toUrl(buf,MAX_IPSTRLEN)); // MODES and specific sub-options. if (s->flags.natIntercept) storeAppendPrintf(e, " intercept"); else if (s->flags.tproxyIntercept) storeAppendPrintf(e, " tproxy"); else if (s->flags.accelSurrogate) { storeAppendPrintf(e, " accel"); @@ -3965,50 +3970,51 @@ dump_generic_port(StoreEntry * e, const #endif } static void dump_PortCfg(StoreEntry * e, const char *n, const AnyP::PortCfg * s) { while (s) { dump_generic_port(e, n, s); storeAppendPrintf(e, "\n"); s = s->next; } } static void free_PortCfg(AnyP::PortCfg ** head) { AnyP::PortCfg *s; while ((s = *head) != NULL) { *head = s->next; - cbdataReferenceDone(s); + delete s; } } void configFreeMemory(void) { free_all(); #if USE_OPENSSL SSL_CTX_free(Config.ssl_client.sslContext); + Config.ssl_client.sslContext = NULL; #endif } void requirePathnameExists(const char *name, const char *path) { struct stat sb; char pathbuf[BUFSIZ]; assert(path != NULL); if (Config.chroot_dir && (geteuid() == 0)) { snprintf(pathbuf, BUFSIZ, "%s/%s", Config.chroot_dir, path); path = pathbuf; } if (stat(path, &sb) < 0) { debugs(0, DBG_CRITICAL, (opt_parse_cfg_only?"FATAL ":"") << "ERROR: " << name << " " << path << ": " << xstrerror()); // keep going to find more issues if we are only checking the config file with "-k parse" if (opt_parse_cfg_only) === modified file 'src/cache_manager.cc' --- src/cache_manager.cc 2013-10-25 00:13:46 +0000 +++ src/cache_manager.cc 2014-04-24 20:12:30 +0000 @@ -65,41 +65,41 @@ class ClassActionCreator: public Mgr::Ac { public: typedef Mgr::Action::Pointer Handler(const Mgr::Command::Pointer &cmd); public: ClassActionCreator(Handler *aHandler): handler(aHandler) {} virtual Mgr::Action::Pointer create(const Mgr::Command::Pointer &cmd) const { return handler(cmd); } private: Handler *handler; }; /// Registers new profiles, ignoring attempts to register a duplicate void CacheManager::registerProfile(const Mgr::ActionProfile::Pointer &profile) { Must(profile != NULL); - if (std::find(menu_.begin(), menu_.end(), profile) == menu_.end()) { + if (!CacheManager::findAction(profile->name)) { menu_.push_back(profile); debugs(16, 3, HERE << "registered profile: " << *profile); } else { debugs(16, 2, HERE << "skipped duplicate profile: " << *profile); } } /** \ingroup CacheManagerAPI * Registers a C-style action, which is implemented as a pointer to a function * taking as argument a pointer to a StoreEntry and returning void. * Implemented via CacheManagerActionLegacy. */ void CacheManager::registerProfile(char const * action, char const * desc, OBJH * handler, int pw_req_flag, int atomic) { debugs(16, 3, HERE << "registering legacy " << action); const Mgr::ActionProfile::Pointer profile = new Mgr::ActionProfile(action, desc, pw_req_flag, atomic, new Mgr::FunActionCreator(handler)); registerProfile(profile); === modified file 'src/client_side.cc' --- src/client_side.cc 2014-03-31 06:57:27 +0000 +++ src/client_side.cc 2014-04-24 20:12:30 +0000 @@ -3704,43 +3704,48 @@ ConnStateData::sslCrtdHandleReplyWrapper ConnStateData * state_data = (ConnStateData *)(data); state_data->sslCrtdHandleReply(reply); } void ConnStateData::sslCrtdHandleReply(const HelperReply &reply) { if (reply.result == HelperReply::BrokenHelper) { debugs(33, 5, HERE << "Certificate for " << sslConnectHostOrIp << " cannot be generated. ssl_crtd response: " << reply); } else if (!reply.other().hasContent()) { debugs(1, DBG_IMPORTANT, HERE << "\"ssl_crtd\" helper returned reply."); } else { Ssl::CrtdMessage reply_message(Ssl::CrtdMessage::REPLY); if (reply_message.parse(reply.other().content(), reply.other().contentSize()) != Ssl::CrtdMessage::OK) { debugs(33, 5, HERE << "Reply from ssl_crtd for " << sslConnectHostOrIp << " is incorrect"); } else { if (reply.result != HelperReply::Okay) { debugs(33, 5, HERE << "Certificate for " << sslConnectHostOrIp << " cannot be generated. ssl_crtd response: " << reply_message.getBody()); } else { debugs(33, 5, HERE << "Certificate for " << sslConnectHostOrIp << " was successfully recieved from ssl_crtd"); - SSL_CTX *ctx = Ssl::generateSslContextUsingPkeyAndCertFromMemory(reply_message.getBody().c_str(), *port); - getSslContextDone(ctx, true); - return; + if (cbdataReferenceValid(port)) { + SSL_CTX *ctx = Ssl::generateSslContextUsingPkeyAndCertFromMemory(reply_message.getBody().c_str(), *port); + getSslContextDone(ctx, true); + return; + } else { + debugs(33, 3, "but our port is gone"); + // getSslContextDone() will cleanup + } } } } getSslContextDone(NULL); } void ConnStateData::buildSslCertGenerationParams(Ssl::CertificateProperties &certProperties) { certProperties.commonName = sslCommonName.size() > 0 ? sslCommonName.termedBuf() : sslConnectHostOrIp.termedBuf(); // fake certificate adaptation requires bump-server-first mode if (!sslServerBump) { assert(port->signingCert.get()); certProperties.signWithX509.resetAndLock(port->signingCert.get()); if (port->signPkey.get()) certProperties.signWithPkey.resetAndLock(port->signPkey.get()); certProperties.signAlgorithm = Ssl::algSignTrusted; return; } @@ -3806,41 +3811,41 @@ void ConnStateData::buildSslCertGenerati assert(port->untrustedSigningCert.get()); certProperties.signWithX509.resetAndLock(port->untrustedSigningCert.get()); certProperties.signWithPkey.resetAndLock(port->untrustedSignPkey.get()); } else { assert(port->signingCert.get()); certProperties.signWithX509.resetAndLock(port->signingCert.get()); if (port->signPkey.get()) certProperties.signWithPkey.resetAndLock(port->signPkey.get()); } signAlgorithm = certProperties.signAlgorithm; } void ConnStateData::getSslContextStart() { assert(areAllContextsForThisConnection()); freeAllContexts(); /* careful: freeAllContexts() above frees request, host, etc. */ - if (port->generateHostCertificates) { + if (cbdataReferenceValid(port) && port->generateHostCertificates) { Ssl::CertificateProperties certProperties; buildSslCertGenerationParams(certProperties); sslBumpCertKey = certProperties.dbKey().c_str(); assert(sslBumpCertKey.size() > 0 && sslBumpCertKey[0] != '\0'); debugs(33, 5, HERE << "Finding SSL certificate for " << sslBumpCertKey << " in cache"); Ssl::LocalContextStorage *ssl_ctx_cache = Ssl::TheGlobalContextStorage.getLocalStorage(port->s); SSL_CTX * dynCtx = NULL; Ssl::SSL_CTX_Pointer *cachedCtx = ssl_ctx_cache ? ssl_ctx_cache->get(sslBumpCertKey.termedBuf()) : NULL; if (cachedCtx && (dynCtx = cachedCtx->get())) { debugs(33, 5, HERE << "SSL certificate for " << sslBumpCertKey << " have found in cache"); if (Ssl::verifySslCertificate(dynCtx, certProperties)) { debugs(33, 5, HERE << "Cached SSL certificate for " << sslBumpCertKey << " is valid"); getSslContextDone(dynCtx); return; } else { debugs(33, 5, HERE << "Cached SSL certificate for " << sslBumpCertKey << " is out of date. Delete this certificate from cache"); if (ssl_ctx_cache) ssl_ctx_cache->del(sslBumpCertKey.termedBuf()); } @@ -3860,40 +3865,48 @@ ConnStateData::getSslContextStart() } catch (const std::exception &e) { debugs(33, DBG_IMPORTANT, "ERROR: Failed to compose ssl_crtd " << "request for " << certProperties.commonName << " certificate: " << e.what() << "; will now block to " << "generate that certificate."); // fall through to do blocking in-process generation. } #endif // USE_SSL_CRTD debugs(33, 5, HERE << "Generating SSL certificate for " << certProperties.commonName); dynCtx = Ssl::generateSslContext(certProperties, *port); getSslContextDone(dynCtx, true); return; } getSslContextDone(NULL); } void ConnStateData::getSslContextDone(SSL_CTX * sslContext, bool isNew) { + if (!cbdataReferenceValid(port)) { // reconfigure may delete our port + debugs(83, 2, "Closing SSL " << clientConnection->remote << " for lost port"); + if (sslContext && Comm::IsConnOpen(clientConnection)) + fd_table[clientConnection->fd].dynamicSslContext = sslContext; + clientConnection->close(); + return; + } + // Try to add generated ssl context to storage. if (port->generateHostCertificates && isNew) { if (signAlgorithm == Ssl::algSignTrusted) { // Add signing certificate to the certificates chain X509 *cert = port->signingCert.get(); if (SSL_CTX_add_extra_chain_cert(sslContext, cert)) { // increase the certificate lock CRYPTO_add(&(cert->references),1,CRYPTO_LOCK_X509); } else { const int ssl_error = ERR_get_error(); debugs(33, DBG_IMPORTANT, "WARNING: can not add signing certificate to SSL context chain: " << ERR_error_string(ssl_error, NULL)); } Ssl::addChainToSslContext(sslContext, port->certsToChain.get()); } //else it is self-signed or untrusted do not attrach any certificate Ssl::LocalContextStorage *ssl_ctx_cache = Ssl::TheGlobalContextStorage.getLocalStorage(port->s); assert(sslBumpCertKey.size() > 0 && sslBumpCertKey[0] != '\0'); if (sslContext) { === modified file 'src/comm/TcpAcceptor.cc' --- src/comm/TcpAcceptor.cc 2014-04-14 06:50:31 +0000 +++ src/comm/TcpAcceptor.cc 2014-04-24 20:12:30 +0000 @@ -105,40 +105,46 @@ Comm::TcpAcceptor::doneAll() const { // stop when FD is closed if (!IsConnOpen(conn)) { return AsyncJob::doneAll(); } // stop when handlers are gone if (theCallSub == NULL) { return AsyncJob::doneAll(); } // open FD with handlers...keep accepting. return false; } void Comm::TcpAcceptor::swanSong() { debugs(5,5, HERE); unsubscribe("swanSong"); + if (IsConnOpen(conn)) { + if (closer_ != NULL) + comm_remove_close_handler(conn->fd, closer_); + conn->close(); + } + conn = NULL; AcceptLimiter::Instance().removeDead(this); AsyncJob::swanSong(); } const char * Comm::TcpAcceptor::status() const { if (conn == NULL) return "[nil connection]"; static char ipbuf[MAX_IPSTRLEN] = {'\0'}; if (ipbuf[0] == '\0') conn->local.toHostStr(ipbuf, MAX_IPSTRLEN); static MemBuf buf; buf.reset(); buf.Printf(" FD %d, %s",conn->fd, ipbuf); const char *jobStatus = AsyncJob::status(); @@ -165,42 +171,57 @@ Comm::TcpAcceptor::setListen() } if (Config.accept_filter && strcmp(Config.accept_filter, "none") != 0) { #ifdef SO_ACCEPTFILTER struct accept_filter_arg afa; bzero(&afa, sizeof(afa)); debugs(5, DBG_IMPORTANT, "Installing accept filter '" << Config.accept_filter << "' on " << conn); xstrncpy(afa.af_name, Config.accept_filter, sizeof(afa.af_name)); if (setsockopt(conn->fd, SOL_SOCKET, SO_ACCEPTFILTER, &afa, sizeof(afa)) < 0) debugs(5, DBG_CRITICAL, "WARNING: SO_ACCEPTFILTER '" << Config.accept_filter << "': '" << xstrerror()); #elif defined(TCP_DEFER_ACCEPT) int seconds = 30; if (strncmp(Config.accept_filter, "data=", 5) == 0) seconds = atoi(Config.accept_filter + 5); if (setsockopt(conn->fd, IPPROTO_TCP, TCP_DEFER_ACCEPT, &seconds, sizeof(seconds)) < 0) debugs(5, DBG_CRITICAL, "WARNING: TCP_DEFER_ACCEPT '" << Config.accept_filter << "': '" << xstrerror()); #else debugs(5, DBG_CRITICAL, "WARNING: accept_filter not supported on your OS"); #endif } + + typedef CommCbMemFunT Dialer; + closer_ = JobCallback(5, 4, Dialer, this, Comm::TcpAcceptor::handleClosure); + comm_add_close_handler(conn->fd, closer_); } +/// called when listening descriptor is closed by an external force +/// such as clientHttpConnectionsClose() +void +Comm::TcpAcceptor::handleClosure(const CommCloseCbParams &io) +{ + closer_ = NULL; + conn = NULL; + Must(done()); +} + + /** * This private callback is called whenever a filedescriptor is ready * to dupe itself and fob off an accept()ed connection * * It will either do that accept operation. Or if there are not enough FD * available to do the clone safely will push the listening FD into a list * of deferred operations. The list gets kicked and the dupe/accept() actually * done later when enough sockets become available. */ void Comm::TcpAcceptor::doAccept(int fd, void *data) { try { debugs(5, 2, HERE << "New connection on FD " << fd); Must(isOpen(fd)); TcpAcceptor *afd = static_cast(data); if (!okToAccept()) { AcceptLimiter::Instance().defer(afd); === modified file 'src/comm/TcpAcceptor.h' --- src/comm/TcpAcceptor.h 2013-10-25 00:13:46 +0000 +++ src/comm/TcpAcceptor.h 2014-04-24 20:12:30 +0000 @@ -1,29 +1,31 @@ #ifndef SQUID_COMM_TCPACCEPTOR_H #define SQUID_COMM_TCPACCEPTOR_H #include "base/AsyncJob.h" #include "base/CbcPointer.h" #include "base/Subscription.h" #include "comm/forward.h" #include "comm_err_t.h" +class CommCloseCbParams; + namespace Comm { class AcceptLimiter; /** * Listens on a Comm::Connection for new incoming connections and * emits an active Comm::Connection descriptor for the new client. * * Handles all event limiting required to quash inbound connection * floods within the global FD limits of available Squid_MaxFD and * client_ip_max_connections. * * Fills the emitted connection with all connection details able to * be looked up. Currently these are the local/remote IP:port details * and the listening socket transparent-mode flag. */ class TcpAcceptor : public AsyncJob { public: @@ -56,37 +58,41 @@ public: */ void acceptNext(); /// Call the subscribed callback handler with details about a new connection. void notify(const comm_err_t flag, const Comm::ConnectionPointer &details) const; /// errno code of the last accept() or listen() action if one occurred. int errcode; protected: friend class AcceptLimiter; int32_t isLimited; ///< whether this socket is delayed and on the AcceptLimiter queue. private: Subscription::Pointer theCallSub; ///< used to generate AsyncCalls handling our events. /// conn being listened on for new connections /// Reserved for read-only use. ConnectionPointer conn; + AsyncCall::Pointer closer_; ///< listen socket closure handler + /// Method to test if there are enough file descriptors to open a new client connection /// if not the accept() will be postponed static bool okToAccept(); /// Method callback for whenever an FD is ready to accept a client connection. static void doAccept(int fd, void *data); void acceptOne(); comm_err_t oldAccept(Comm::ConnectionPointer &details); void setListen(); + void handleClosure(const CommCloseCbParams &io); + CBDATA_CLASS2(TcpAcceptor); }; } // namespace Comm #endif /* SQUID_COMM_TCPACCEPTOR_H */ === modified file 'src/ssl/context_storage.cc' --- src/ssl/context_storage.cc 2014-03-30 12:00:34 +0000 +++ src/ssl/context_storage.cc 2014-04-24 20:35:45 +0000 @@ -73,36 +73,37 @@ Ssl::LocalContextStorage *Ssl::GlobalCon return NULL; else return i->second; } void Ssl::GlobalContextStorage::reconfigureStart() { configureStorage.clear(); reconfiguring = true; } void Ssl::GlobalContextStorage::reconfigureFinish() { if (reconfiguring) { reconfiguring = false; // remove or change old local storages. for (std::map::iterator i = storage.begin(); i != storage.end(); ++i) { std::map::iterator conf_i = configureStorage.find(i->first); if (conf_i == configureStorage.end() || conf_i->second <= 0) { + delete i->second; storage.erase(i); } else { i->second->setMemLimit(conf_i->second); } } // add new local storages. for (std::map::iterator conf_i = configureStorage.begin(); conf_i != configureStorage.end(); ++conf_i ) { if (storage.find(conf_i->first) == storage.end() && conf_i->second > 0) { storage.insert(std::pair(conf_i->first, new LocalContextStorage(-1, conf_i->second))); } } } } Ssl::GlobalContextStorage Ssl::TheGlobalContextStorage; === modified file 'src/ssl/support.cc' --- src/ssl/support.cc 2014-03-30 12:00:34 +0000 +++ src/ssl/support.cc 2014-04-24 20:37:38 +0000 @@ -694,69 +694,69 @@ static void ssl_free_CertChain(void *, void *ptr, CRYPTO_EX_DATA *, int, long, void *) { STACK_OF(X509) *certsChain = static_cast (ptr); sk_X509_pop_free(certsChain,X509_free); } // "free" function for X509 certificates static void ssl_free_X509(void *, void *ptr, CRYPTO_EX_DATA *, int, long, void *) { X509 *cert = static_cast (ptr); X509_free(cert); } /// \ingroup ServerProtocolSSLInternal static void ssl_initialize(void) { - static int ssl_initialized = 0; + static bool initialized = false; + if (initialized) + return; + initialized = true; - if (!ssl_initialized) { - ssl_initialized = 1; SSL_load_error_strings(); SSLeay_add_ssl_algorithms(); #if HAVE_OPENSSL_ENGINE_H if (Config.SSL.ssl_engine) { ENGINE *e; if (!(e = ENGINE_by_id(Config.SSL.ssl_engine))) { fatalf("Unable to find SSL engine '%s'\n", Config.SSL.ssl_engine); } if (!ENGINE_set_default(e, ENGINE_METHOD_ALL)) { int ssl_error = ERR_get_error(); fatalf("Failed to initialise SSL engine: %s\n", ERR_error_string(ssl_error, NULL)); } } #else if (Config.SSL.ssl_engine) { fatalf("Your OpenSSL has no SSL engine support\n"); } #endif - } ssl_ex_index_server = SSL_get_ex_new_index(0, (void *) "server", NULL, NULL, NULL); ssl_ctx_ex_index_dont_verify_domain = SSL_CTX_get_ex_new_index(0, (void *) "dont_verify_domain", NULL, NULL, NULL); ssl_ex_index_cert_error_check = SSL_get_ex_new_index(0, (void *) "cert_error_check", NULL, &ssl_dupAclChecklist, &ssl_freeAclChecklist); ssl_ex_index_ssl_error_detail = SSL_get_ex_new_index(0, (void *) "ssl_error_detail", NULL, NULL, &ssl_free_ErrorDetail); ssl_ex_index_ssl_peeked_cert = SSL_get_ex_new_index(0, (void *) "ssl_peeked_cert", NULL, NULL, &ssl_free_X509); ssl_ex_index_ssl_errors = SSL_get_ex_new_index(0, (void *) "ssl_errors", NULL, NULL, &ssl_free_SslErrors); ssl_ex_index_ssl_cert_chain = SSL_get_ex_new_index(0, (void *) "ssl_cert_chain", NULL, NULL, &ssl_free_CertChain); ssl_ex_index_ssl_validation_counter = SSL_get_ex_new_index(0, (void *) "ssl_validation_counter", NULL, NULL, &ssl_free_int); } /// \ingroup ServerProtocolSSLInternal static int ssl_load_crl(SSL_CTX *sslContext, const char *CRLfile) { X509_STORE *st = SSL_CTX_get_cert_store(sslContext); X509_CRL *crl; BIO *in = BIO_new_file(CRLfile, "r"); int count = 0;