mirror of
https://github.com/znc/znc.git
synced 2026-03-28 17:42:41 +01:00
334 lines
11 KiB
C++
334 lines
11 KiB
C++
/*
|
|
* Copyright (C) 2004-2022 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.
|
|
*/
|
|
|
|
#ifndef ZNC_SOCKET_H
|
|
#define ZNC_SOCKET_H
|
|
|
|
#include <znc/zncconfig.h>
|
|
#include <znc/Csocket.h>
|
|
#include <znc/Threads.h>
|
|
#include <znc/Translation.h>
|
|
|
|
class CModule;
|
|
|
|
class CZNCSock : public Csock, protected CCoreTranslationMixin {
|
|
public:
|
|
CZNCSock(int timeout = 60);
|
|
CZNCSock(const CString& sHost, u_short port, int timeout = 60);
|
|
~CZNCSock() {}
|
|
|
|
int ConvertAddress(const struct sockaddr_storage* pAddr, socklen_t iAddrLen,
|
|
CString& sIP, u_short* piPort) const override;
|
|
#ifdef HAVE_LIBSSL
|
|
int VerifyPeerCertificate(int iPreVerify,
|
|
X509_STORE_CTX* pStoreCTX) override;
|
|
void SSLHandShakeFinished() override;
|
|
bool CheckSSLCert(X509* pCert);
|
|
virtual void SSLCertError(X509* pCert) {}
|
|
bool SNIConfigureClient(CString& sHostname) override;
|
|
CString GetSSLPeerFingerprint(X509* pCert = nullptr) const;
|
|
#else
|
|
CString GetSSLPeerFingerprint() const { return ""; }
|
|
#endif
|
|
void SetHostToVerifySSL(const CString& sHost) {
|
|
m_sHostToVerifySSL = sHost;
|
|
}
|
|
void SetSSLTrustedPeerFingerprints(const SCString& ssFPs) {
|
|
m_ssTrustedFingerprints = ssFPs;
|
|
}
|
|
|
|
void SetTrustAllCerts(bool bTrustAll) { m_bTrustAllCerts = bTrustAll; }
|
|
bool GetTrustAllCerts() const { return m_bTrustAllCerts; }
|
|
|
|
void SetTrustPKI(bool bTrustPKI) { m_bTrustPKI = bTrustPKI; }
|
|
bool GetTrustPKI() const { return m_bTrustPKI; }
|
|
|
|
void SetEncoding(const CString&);
|
|
|
|
virtual CString GetRemoteIP() const { return Csock::GetRemoteIP(); }
|
|
|
|
protected:
|
|
// All existing errno codes seem to be in range 1-300
|
|
enum {
|
|
errnoBadSSLCert = 12569,
|
|
};
|
|
|
|
private:
|
|
CString m_sHostToVerifySSL;
|
|
SCString m_ssTrustedFingerprints;
|
|
SCString m_ssCertVerificationErrors;
|
|
bool m_bTrustAllCerts = false;
|
|
bool m_bTrustPKI = true;
|
|
};
|
|
|
|
enum EAddrType { ADDR_IPV4ONLY, ADDR_IPV6ONLY, ADDR_ALL };
|
|
|
|
class CSockManager : public TSocketManager<CZNCSock>,
|
|
private CCoreTranslationMixin {
|
|
public:
|
|
CSockManager();
|
|
virtual ~CSockManager();
|
|
|
|
bool ListenHost(u_short iPort, const CString& sSockName,
|
|
const CString& sBindHost, bool bSSL = false,
|
|
int iMaxConns = SOMAXCONN, CZNCSock* pcSock = nullptr,
|
|
u_int iTimeout = 0, EAddrType eAddr = ADDR_ALL) {
|
|
CSListener L(iPort, sBindHost);
|
|
|
|
L.SetSockName(sSockName);
|
|
L.SetIsSSL(bSSL);
|
|
L.SetTimeout(iTimeout);
|
|
L.SetMaxConns(iMaxConns);
|
|
|
|
#ifdef HAVE_IPV6
|
|
switch (eAddr) {
|
|
case ADDR_IPV4ONLY:
|
|
L.SetAFRequire(CSSockAddr::RAF_INET);
|
|
break;
|
|
case ADDR_IPV6ONLY:
|
|
L.SetAFRequire(CSSockAddr::RAF_INET6);
|
|
break;
|
|
case ADDR_ALL:
|
|
L.SetAFRequire(CSSockAddr::RAF_ANY);
|
|
break;
|
|
}
|
|
#endif
|
|
|
|
return Listen(L, pcSock);
|
|
}
|
|
|
|
bool ListenAll(u_short iPort, const CString& sSockName, bool bSSL = false,
|
|
int iMaxConns = SOMAXCONN, CZNCSock* pcSock = nullptr,
|
|
u_int iTimeout = 0, EAddrType eAddr = ADDR_ALL) {
|
|
return ListenHost(iPort, sSockName, "", bSSL, iMaxConns, pcSock,
|
|
iTimeout, eAddr);
|
|
}
|
|
|
|
u_short ListenRand(const CString& sSockName, const CString& sBindHost,
|
|
bool bSSL = false, int iMaxConns = SOMAXCONN,
|
|
CZNCSock* pcSock = nullptr, u_int iTimeout = 0,
|
|
EAddrType eAddr = ADDR_ALL) {
|
|
unsigned short uPort = 0;
|
|
CSListener L(0, sBindHost);
|
|
|
|
L.SetSockName(sSockName);
|
|
L.SetIsSSL(bSSL);
|
|
L.SetTimeout(iTimeout);
|
|
L.SetMaxConns(iMaxConns);
|
|
|
|
#ifdef HAVE_IPV6
|
|
switch (eAddr) {
|
|
case ADDR_IPV4ONLY:
|
|
L.SetAFRequire(CSSockAddr::RAF_INET);
|
|
break;
|
|
case ADDR_IPV6ONLY:
|
|
L.SetAFRequire(CSSockAddr::RAF_INET6);
|
|
break;
|
|
case ADDR_ALL:
|
|
L.SetAFRequire(CSSockAddr::RAF_ANY);
|
|
break;
|
|
}
|
|
#endif
|
|
|
|
Listen(L, pcSock, &uPort);
|
|
|
|
return uPort;
|
|
}
|
|
|
|
u_short ListenAllRand(const CString& sSockName, bool bSSL = false,
|
|
int iMaxConns = SOMAXCONN, CZNCSock* pcSock = nullptr,
|
|
u_int iTimeout = 0, EAddrType eAddr = ADDR_ALL) {
|
|
return (ListenRand(sSockName, "", bSSL, iMaxConns, pcSock, iTimeout,
|
|
eAddr));
|
|
}
|
|
|
|
void Connect(const CString& sHostname, u_short iPort,
|
|
const CString& sSockName, int iTimeout = 60, bool bSSL = false,
|
|
const CString& sBindHost = "", CZNCSock* pcSock = nullptr);
|
|
|
|
unsigned int GetAnonConnectionCount(const CString& sIP) const;
|
|
void DelSockByAddr(Csock* pcSock) override;
|
|
|
|
private:
|
|
void FinishConnect(const CString& sHostname, u_short iPort,
|
|
const CString& sSockName, int iTimeout, bool bSSL,
|
|
const CString& sBindHost, CZNCSock* pcSock);
|
|
|
|
std::map<Csock*, bool /* deleted */> m_InFlightDnsSockets;
|
|
|
|
#ifdef HAVE_PTHREAD
|
|
class CThreadMonitorFD;
|
|
friend class CThreadMonitorFD;
|
|
#endif
|
|
#ifdef HAVE_THREADED_DNS
|
|
struct TDNSTask {
|
|
TDNSTask()
|
|
: sHostname(""),
|
|
iPort(0),
|
|
sSockName(""),
|
|
iTimeout(0),
|
|
bSSL(false),
|
|
sBindhost(""),
|
|
pcSock(nullptr),
|
|
bDoneTarget(false),
|
|
bDoneBind(false),
|
|
aiTarget(nullptr),
|
|
aiBind(nullptr) {}
|
|
|
|
TDNSTask(const TDNSTask&) = delete;
|
|
TDNSTask& operator=(const TDNSTask&) = delete;
|
|
|
|
CString sHostname;
|
|
u_short iPort;
|
|
CString sSockName;
|
|
int iTimeout;
|
|
bool bSSL;
|
|
CString sBindhost;
|
|
CZNCSock* pcSock;
|
|
|
|
bool bDoneTarget;
|
|
bool bDoneBind;
|
|
addrinfo* aiTarget;
|
|
addrinfo* aiBind;
|
|
};
|
|
class CDNSJob : public CJob {
|
|
public:
|
|
CDNSJob()
|
|
: sHostname(""),
|
|
task(nullptr),
|
|
pManager(nullptr),
|
|
bBind(false),
|
|
iRes(0),
|
|
aiResult(nullptr) {}
|
|
|
|
CDNSJob(const CDNSJob&) = delete;
|
|
CDNSJob& operator=(const CDNSJob&) = delete;
|
|
|
|
CString sHostname;
|
|
TDNSTask* task;
|
|
CSockManager* pManager;
|
|
bool bBind;
|
|
|
|
int iRes;
|
|
addrinfo* aiResult;
|
|
|
|
void runThread() override;
|
|
void runMain() override;
|
|
};
|
|
void StartTDNSThread(TDNSTask* task, bool bBind);
|
|
void SetTDNSThreadFinished(TDNSTask* task, bool bBind, addrinfo* aiResult);
|
|
static void* TDNSThread(void* argument);
|
|
#endif
|
|
protected:
|
|
};
|
|
|
|
/**
|
|
* @class CSocket
|
|
* @brief Base Csock implementation to be used by modules
|
|
*
|
|
* By all means, this class should be used as a base for sockets originating from modules. It handles removing instances of itself
|
|
* from the module as it unloads, and simplifies use in general.
|
|
* - EnableReadLine is default to true in this class
|
|
* - MaxBuffer for readline is set to 10240, in the event this is reached the socket is closed (@see ReachedMaxBuffer)
|
|
*/
|
|
class CSocket : public CZNCSock {
|
|
public:
|
|
/**
|
|
* @brief ctor
|
|
* @param pModule the module this sock instance is associated to
|
|
*/
|
|
CSocket(CModule* pModule);
|
|
/**
|
|
* @brief ctor
|
|
* @param pModule the module this sock instance is associated to
|
|
* @param sHostname the hostname being connected to
|
|
* @param uPort the port being connected to
|
|
* @param iTimeout the timeout period for this specific sock
|
|
*/
|
|
CSocket(CModule* pModule, const CString& sHostname, unsigned short uPort,
|
|
int iTimeout = 60);
|
|
virtual ~CSocket();
|
|
|
|
CSocket(const CSocket&) = delete;
|
|
CSocket& operator=(const CSocket&) = delete;
|
|
|
|
using Csock::Connect;
|
|
using Csock::Listen;
|
|
|
|
//! This defaults to closing the socket, feel free to override
|
|
void ReachedMaxBuffer() override;
|
|
void SockError(int iErrno, const CString& sDescription) override;
|
|
|
|
//! This limits the global connections from this IP to defeat DoS attacks, feel free to override. The ACL used is provided by the main interface @see CZNC::AllowConnectionFrom
|
|
bool ConnectionFrom(const CString& sHost, unsigned short uPort) override;
|
|
|
|
//! Ease of use Connect, assigns to the manager and is subsequently tracked
|
|
bool Connect(const CString& sHostname, unsigned short uPort,
|
|
bool bSSL = false, unsigned int uTimeout = 60);
|
|
//! Ease of use Listen, assigned to the manager and is subsequently tracked
|
|
bool Listen(unsigned short uPort, bool bSSL, unsigned int uTimeout = 0);
|
|
|
|
// Getters
|
|
CModule* GetModule() const;
|
|
// !Getters
|
|
|
|
#ifndef SWIG
|
|
// Translation. As opposed to CCoreTranslationMixin, this one uses module.mo
|
|
CString t_s(const CString& sEnglish, const CString& sContext = "") const;
|
|
CInlineFormatMessage t_f(const CString& sEnglish,
|
|
const CString& sContext = "") const;
|
|
CInlineFormatMessage t_p(const CString& sEnglish, const CString& sEnglishes,
|
|
int iNum, const CString& sContext) const;
|
|
CDelayedTranslation t_d(const CString& sEnglish,
|
|
const CString& sContext = "") const;
|
|
#endif
|
|
|
|
private:
|
|
protected:
|
|
CModule*
|
|
m_pModule; //!< pointer to the module that this sock instance belongs to
|
|
};
|
|
|
|
/**
|
|
* @class CIRCSocket
|
|
* @brief Base IRC socket for client<->ZNC, and ZNC<->server
|
|
*/
|
|
class CIRCSocket : public CZNCSock {
|
|
public:
|
|
#ifdef HAVE_ICU
|
|
/**
|
|
* @brief Allow IRC control characters to appear even if protocol encoding explicitly disallows them.
|
|
*
|
|
* E.g. ISO-2022-JP disallows 0x0F, which in IRC means "reset format",
|
|
* so by default it gets replaced with U+FFFD ("replacement character").
|
|
* https://code.google.com/p/chromium/issues/detail?id=277062#c3
|
|
*
|
|
* In case if protocol encoding uses these code points for something else, the encoding takes preference,
|
|
* and they are not IRC control characters anymore.
|
|
*/
|
|
void IcuExtToUCallback(UConverterToUnicodeArgs* toArgs,
|
|
const char* codeUnits, int32_t length,
|
|
UConverterCallbackReason reason,
|
|
UErrorCode* err) override;
|
|
void IcuExtFromUCallback(UConverterFromUnicodeArgs* fromArgs,
|
|
const UChar* codeUnits, int32_t length,
|
|
UChar32 codePoint, UConverterCallbackReason reason,
|
|
UErrorCode* err) override;
|
|
#endif
|
|
};
|
|
|
|
#endif /* ZNC_SOCKET_H */
|