/* * Copyright (C) 2004-2014 ZNC, see the NOTICE file for details. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include CZNCSock::CZNCSock(int timeout) : Csock(timeout) { #ifdef HAVE_LIBSSL DisableSSLCompression(); DisableSSLProtocols(CZNC::Get().GetDisabledSSLProtocols()); CString sCipher = CZNC::Get().GetSSLCiphers(); if (!sCipher.empty()) { SetCipher(sCipher); } #endif } CZNCSock::CZNCSock(const CString& sHost, u_short port, int timeout) : Csock(sHost, port, timeout) { #ifdef HAVE_LIBSSL DisableSSLCompression(); DisableSSLProtocols(CZNC::Get().GetDisabledSSLProtocols()); #endif } unsigned int CSockManager::GetAnonConnectionCount(const CString &sIP) const { const_iterator it; unsigned int ret = 0; for (it = begin(); it != end(); ++it) { Csock *pSock = *it; // Logged in CClients have "USR::" as their sockname if (pSock->GetType() == Csock::INBOUND && pSock->GetRemoteIP() == sIP && pSock->GetSockName().Left(5) != "USR::") { ret++; } } DEBUG("There are [" << ret << "] clients from [" << sIP << "]"); return ret; } int CZNCSock::ConvertAddress(const struct sockaddr_storage * pAddr, socklen_t iAddrLen, CS_STRING & sIP, u_short * piPort) const { int ret = Csock::ConvertAddress(pAddr, iAddrLen, sIP, piPort); if (ret == 0) sIP.TrimPrefix("::ffff:"); return ret; } #ifdef HAVE_LIBSSL int CZNCSock::VerifyPeerCertificate(int iPreVerify, X509_STORE_CTX * pStoreCTX) { if (iPreVerify == 0) { m_ssCertVerificationErrors.insert(X509_verify_cert_error_string(X509_STORE_CTX_get_error(pStoreCTX))); } return 1; } void CZNCSock::SSLHandShakeFinished() { if (GetType() != ETConn::OUTBOUND) { return; } X509* pCert = GetX509(); if (!pCert) { DEBUG(GetSockName() + ": No cert"); CallSockError(errnoBadSSLCert, "Anonymous SSL cert is not allowed"); Close(); return; } CString sHostVerifyError; if (!ZNC_SSLVerifyHost(m_HostToVerifySSL, pCert, sHostVerifyError)) { m_ssCertVerificationErrors.insert(sHostVerifyError); } X509_free(pCert); if (m_ssCertVerificationErrors.empty()) { DEBUG(GetSockName() + ": Good cert"); return; } CString sFP = GetSSLPeerFingerprint(); if (m_ssTrustedFingerprints.count(sFP) != 0) { DEBUG(GetSockName() + ": Cert explicitly trusted by user: " << sFP); return; } DEBUG(GetSockName() + ": Bad cert"); CString sErrorMsg = "Invalid SSL certificate: "; sErrorMsg += CString(", ").Join(begin(m_ssCertVerificationErrors), end(m_ssCertVerificationErrors)); CallSockError(errnoBadSSLCert, sErrorMsg); Close(); } #endif CString CZNCSock::GetSSLPeerFingerprint() const { #ifdef HAVE_LIBSSL // Csocket's version returns insecure SHA-1 // This one is SHA-256 const EVP_MD* evp = EVP_sha256(); X509* pCert = GetX509(); if (!pCert) { DEBUG(GetSockName() + ": GetSSLPeerFingerprint: Anonymous cert"); return ""; } unsigned char buf[256/8]; unsigned int _32 = 256/8; int iSuccess = X509_digest(pCert, evp, buf, &_32); X509_free(pCert); if (!iSuccess) { DEBUG(GetSockName() + ": GetSSLPeerFingerprint: Couldn't find digest"); return ""; } CString sResult; sResult.reserve(3*256/8); for (unsigned char c : buf) { char b[3]; snprintf(b, 3, "%02x", c); sResult += b; sResult += ":"; } sResult.TrimRight(":"); return sResult; #else return ""; #endif } #ifdef HAVE_PTHREAD class CSockManager::CTDNSMonitorFD : public CSMonitorFD { public: CTDNSMonitorFD() { Add(CThreadPool::Get().getReadFD(), ECT_Read); } virtual bool FDsThatTriggered(const std::map& miiReadyFds) { if (miiReadyFds.find(CThreadPool::Get().getReadFD())->second) { CThreadPool::Get().handlePipeReadable(); } return true; } }; #endif #ifdef HAVE_THREADED_DNS void CSockManager::CDNSJob::runThread() { int iCount = 0; while (true) { addrinfo hints; memset(&hints, 0, sizeof(hints)); hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; hints.ai_protocol = IPPROTO_TCP; hints.ai_flags = AI_ADDRCONFIG; iRes = getaddrinfo(sHostname.c_str(), NULL, &hints, &aiResult); if (EAGAIN != iRes) { break; } iCount++; if (iCount > 5) { iRes = ETIMEDOUT; break; } sleep(5); // wait 5 seconds before next try } } void CSockManager::CDNSJob::runMain() { if (0 != this->iRes) { DEBUG("Error in threaded DNS: " << gai_strerror(this->iRes)); if (this->aiResult) { DEBUG("And aiResult is not NULL..."); } this->aiResult = NULL; // just for case. Maybe to call freeaddrinfo()? } pManager->SetTDNSThreadFinished(this->task, this->bBind, this->aiResult); } void CSockManager::StartTDNSThread(TDNSTask* task, bool bBind) { CString sHostname = bBind ? task->sBindhost : task->sHostname; CDNSJob* arg = new CDNSJob; arg->sHostname = sHostname; arg->task = task; arg->bBind = bBind; arg->iRes = 0; arg->aiResult = NULL; arg->pManager = this; CThreadPool::Get().addJob(arg); } void CSockManager::SetTDNSThreadFinished(TDNSTask* task, bool bBind, addrinfo* aiResult) { if (bBind) { task->aiBind = aiResult; task->bDoneBind = true; } else { task->aiTarget = aiResult; task->bDoneTarget = true; } // Now that something is done, check if everything we needed is done if (!task->bDoneBind || !task->bDoneTarget) { return; } // All needed DNS is done, now collect the results addrinfo* aiTarget = NULL; addrinfo* aiBind = NULL; try { addrinfo* aiTarget4 = task->aiTarget; addrinfo* aiBind4 = task->aiBind; while (aiTarget4 && aiTarget4->ai_family != AF_INET) aiTarget4 = aiTarget4->ai_next; while (aiBind4 && aiBind4->ai_family != AF_INET) aiBind4 = aiBind4->ai_next; addrinfo* aiTarget6 = NULL; addrinfo* aiBind6 = NULL; #ifdef HAVE_IPV6 aiTarget6 = task->aiTarget; aiBind6 = task->aiBind; while (aiTarget6 && aiTarget6->ai_family != AF_INET6) aiTarget6 = aiTarget6->ai_next; while (aiBind6 && aiBind6->ai_family != AF_INET6) aiBind6 = aiBind6->ai_next; #endif if (!aiTarget4 && !aiTarget6) { throw "Can't resolve server hostname"; } else if (task->sBindhost.empty()) { #ifdef HAVE_IPV6 aiTarget = task->aiTarget; #else aiTarget = aiTarget4; #endif } else if (!aiBind4 && !aiBind6) { throw "Can't resolve bind hostname. Try /znc clearbindhost and /znc clearuserbindhost"; } else if (aiBind6 && aiTarget6) { aiTarget = aiTarget6; aiBind = aiBind6; } else if (aiBind4 && aiTarget4) { aiTarget = aiTarget4; aiBind = aiBind4; } else { throw "Address family of bindhost doesn't match address family of server"; } CString sBindhost; CString sTargetHost; if (!task->sBindhost.empty()) { char s[INET6_ADDRSTRLEN] = {}; getnameinfo(aiBind->ai_addr, aiBind->ai_addrlen, s, sizeof(s), NULL, 0, NI_NUMERICHOST); sBindhost = s; } char s[INET6_ADDRSTRLEN] = {}; getnameinfo(aiTarget->ai_addr, aiTarget->ai_addrlen, s, sizeof(s), NULL, 0, NI_NUMERICHOST); sTargetHost = s; DEBUG("TDNS: " << task->sSockName << ", connecting to [" << sTargetHost << "] using bindhost [" << sBindhost << "]"); FinishConnect(sTargetHost, task->iPort, task->sSockName, task->iTimeout, task->bSSL, sBindhost, task->pcSock); } catch (const char* s) { DEBUG(task->sSockName << ", dns resolving error: " << s); task->pcSock->SetSockName(task->sSockName); task->pcSock->SockError(-1, s); delete task->pcSock; } if (task->aiTarget) freeaddrinfo(task->aiTarget); if (task->aiBind) freeaddrinfo(task->aiBind); delete task; } #endif /* HAVE_THREADED_DNS */ CSockManager::CSockManager() { #ifdef HAVE_PTHREAD MonitorFD(new CTDNSMonitorFD()); #endif } CSockManager::~CSockManager() { } void CSockManager::Connect(const CString& sHostname, u_short iPort, const CString& sSockName, int iTimeout, bool bSSL, const CString& sBindHost, CZNCSock *pcSock) { if (pcSock) { pcSock->SetHostToVerifySSL(sHostname); } #ifdef HAVE_THREADED_DNS DEBUG("TDNS: initiating resolving of [" << sHostname << "] and bindhost [" << sBindHost << "]"); TDNSTask* task = new TDNSTask; task->sHostname = sHostname; task->iPort = iPort; task->sSockName = sSockName; task->iTimeout = iTimeout; task->bSSL = bSSL; task->sBindhost = sBindHost; task->pcSock = pcSock; task->aiTarget = NULL; task->aiBind = NULL; task->bDoneTarget = false; if (sBindHost.empty()) { task->bDoneBind = true; } else { task->bDoneBind = false; StartTDNSThread(task, true); } StartTDNSThread(task, false); #else /* HAVE_THREADED_DNS */ // Just let Csocket handle DNS itself FinishConnect(sHostname, iPort, sSockName, iTimeout, bSSL, sBindHost, pcSock); #endif } void CSockManager::FinishConnect(const CString& sHostname, u_short iPort, const CString& sSockName, int iTimeout, bool bSSL, const CString& sBindHost, CZNCSock *pcSock) { CSConnection C(sHostname, iPort, iTimeout); C.SetSockName(sSockName); C.SetIsSSL(bSSL); C.SetBindHost(sBindHost); #ifdef HAVE_LIBSSL CString sCipher = CZNC::Get().GetSSLCiphers(); if (!sCipher.empty()) { C.SetCipher(sCipher); } #endif TSocketManager::Connect(C, pcSock); } /////////////////// CSocket /////////////////// CSocket::CSocket(CModule* pModule) : CZNCSock() { m_pModule = pModule; if (m_pModule) m_pModule->AddSocket(this); EnableReadLine(); SetMaxBufferThreshold(10240); } CSocket::CSocket(CModule* pModule, const CString& sHostname, unsigned short uPort, int iTimeout) : CZNCSock(sHostname, uPort, iTimeout) { m_pModule = pModule; if (m_pModule) m_pModule->AddSocket(this); EnableReadLine(); SetMaxBufferThreshold(10240); } CSocket::~CSocket() { CUser *pUser = NULL; // CWebSock could cause us to have a NULL pointer here if (m_pModule) { pUser = m_pModule->GetUser(); m_pModule->UnlinkSocket(this); } if (pUser && m_pModule && (m_pModule->GetType() != CModInfo::GlobalModule)) { pUser->AddBytesWritten(GetBytesWritten()); pUser->AddBytesRead(GetBytesRead()); } else { CZNC::Get().AddBytesWritten(GetBytesWritten()); CZNC::Get().AddBytesRead(GetBytesRead()); } } void CSocket::ReachedMaxBuffer() { DEBUG(GetSockName() << " == ReachedMaxBuffer()"); if (m_pModule) m_pModule->PutModule("Some socket reached its max buffer limit and was closed!"); Close(); } void CSocket::SockError(int iErrno, const CString& sDescription) { DEBUG(GetSockName() << " == SockError(" << sDescription << ", " << strerror(iErrno) << ")"); if (iErrno == EMFILE) { // We have too many open fds, this can cause a busy loop. Close(); } } bool CSocket::ConnectionFrom(const CString& sHost, unsigned short uPort) { return CZNC::Get().AllowConnectionFrom(sHost); } bool CSocket::Connect(const CString& sHostname, unsigned short uPort, bool bSSL, unsigned int uTimeout) { if (!m_pModule) { DEBUG("ERROR: CSocket::Connect called on instance without m_pModule handle!"); return false; } CUser* pUser = m_pModule->GetUser(); CString sSockName = "MOD::C::" + m_pModule->GetModName(); CString sBindHost; if (pUser) { sSockName += "::" + pUser->GetUserName(); sBindHost = pUser->GetBindHost(); CIRCNetwork* pNetwork = m_pModule->GetNetwork(); if (pNetwork) { sSockName += "::" + pNetwork->GetName(); sBindHost = pNetwork->GetBindHost(); } } // Don't overwrite the socket name if one is already set if (!GetSockName().empty()) { sSockName = GetSockName(); } m_pModule->GetManager()->Connect(sHostname, uPort, sSockName, uTimeout, bSSL, sBindHost, this); return true; } bool CSocket::Listen(unsigned short uPort, bool bSSL, unsigned int uTimeout) { if (!m_pModule) { DEBUG("ERROR: CSocket::Listen called on instance without m_pModule handle!"); return false; } CUser* pUser = m_pModule->GetUser(); CString sSockName = "MOD::L::" + m_pModule->GetModName(); if (pUser) { sSockName += "::" + pUser->GetUserName(); } // Don't overwrite the socket name if one is already set if (!GetSockName().empty()) { sSockName = GetSockName(); } return m_pModule->GetManager()->ListenAll(uPort, sSockName, bSSL, SOMAXCONN, this); } CModule* CSocket::GetModule() const { return m_pModule; } /////////////////// !CSocket ///////////////////