mirror of
https://github.com/znc/znc.git
synced 2026-03-28 17:42:41 +01:00
Make unix sockets work from modules
This commit is contained in:
@@ -159,6 +159,28 @@ class CSockManager : public TSocketManager<CZNCSock>,
|
||||
const CString& sSockName, int iTimeout = 60, bool bSSL = false,
|
||||
const CString& sBindHost = "", CZNCSock* pcSock = nullptr);
|
||||
|
||||
bool ListenUnix(const CString& sSockName, const CString& sPath,
|
||||
CZNCSock* pcSock = nullptr) {
|
||||
if (pcSock->ListenUnixInternal(sPath)) {
|
||||
AddSock(pcSock, sSockName);
|
||||
return true;
|
||||
}
|
||||
|
||||
delete pcSock;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ConnectUnix(const CString& sSockName, const CString& sPath,
|
||||
CZNCSock* pcSock = nullptr) {
|
||||
if (pcSock->ConnectUnixInternal(sPath)) {
|
||||
AddSock(pcSock, sSockName);
|
||||
return true;
|
||||
}
|
||||
|
||||
delete pcSock;
|
||||
return false;
|
||||
}
|
||||
|
||||
unsigned int GetAnonConnectionCount(const CString& sIP) const;
|
||||
void DelSockByAddr(Csock* pcSock) override;
|
||||
|
||||
@@ -280,6 +302,10 @@ class CSocket : public CZNCSock {
|
||||
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);
|
||||
bool ConnectUnix(const CString& sPath);
|
||||
bool ListenUnix(const CString& sPath);
|
||||
//! Helper for modperl and modpython, modules don't normally need to call this
|
||||
CString ConstructSockName(const CString& sPart) const;
|
||||
|
||||
// Getters
|
||||
CModule* GetModule() const;
|
||||
|
||||
@@ -778,7 +778,7 @@ sub Connect {
|
||||
$self->GetModule->GetManager->Connect(
|
||||
$host,
|
||||
$port,
|
||||
"perl-socket",
|
||||
$self->ConstructSockName("Perl-C"),
|
||||
$arg{timeout}//60,
|
||||
$arg{ssl}//0,
|
||||
$arg{bindhost}//'',
|
||||
@@ -786,11 +786,26 @@ sub Connect {
|
||||
);
|
||||
}
|
||||
|
||||
sub ConnectUnix {
|
||||
my $self = shift;
|
||||
my $path = shift;
|
||||
$self->GetModule->GetManager->ConnectUnix(
|
||||
$self->ConstructSockName("Perl-CU"),
|
||||
$path, $self->{_csock}
|
||||
);
|
||||
}
|
||||
|
||||
sub Listen {
|
||||
my $self = shift;
|
||||
my %arg = @_;
|
||||
my $addrtype = $ZNC::ADDR_ALL;
|
||||
if (defined $arg{addrtype}) {
|
||||
if ($arg{addrtype} =~ /^unix$/i) {
|
||||
return $self->GetModule->GetManager->ListenUnix(
|
||||
$self->ConstructSockName("Perl-LU"),
|
||||
$arg{path}, $self->{_csock},
|
||||
);
|
||||
}
|
||||
if ($arg{addrtype} =~ /^ipv4$/i) { $addrtype = $ZNC::ADDR_IPV4ONLY }
|
||||
elsif ($arg{addrtype} =~ /^ipv6$/i) { $addrtype = $ZNC::ADDR_IPV6ONLY }
|
||||
elsif ($arg{addrtype} =~ /^all$/i) { }
|
||||
@@ -799,7 +814,7 @@ sub Listen {
|
||||
if (defined $arg{port}) {
|
||||
return $arg{port} if $self->GetModule->GetManager->ListenHost(
|
||||
$arg{port},
|
||||
"perl-socket",
|
||||
$self->ConstructSockName("Perl-L"),
|
||||
$arg{bindhost}//'',
|
||||
$arg{ssl}//0,
|
||||
$arg{maxconns}//ZNC::_GetSOMAXCONN,
|
||||
@@ -810,7 +825,7 @@ sub Listen {
|
||||
return 0;
|
||||
}
|
||||
$self->GetModule->GetManager->ListenRand(
|
||||
"perl-socket",
|
||||
$self->ConstructSockName("Perl-L"),
|
||||
$arg{bindhost}//'',
|
||||
$arg{ssl}//0,
|
||||
$arg{maxconns}//ZNC::_GetSOMAXCONN,
|
||||
|
||||
@@ -47,7 +47,12 @@ class Socket:
|
||||
return AsPyModule(self._csock.GetModule()).GetNewPyObj()
|
||||
|
||||
def Listen(self, addrtype='all', port=None, bindhost='', ssl=False,
|
||||
maxconns=GetSOMAXCONN(), timeout=0):
|
||||
maxconns=GetSOMAXCONN(), timeout=0, path=''):
|
||||
if addrtype == 'unix':
|
||||
return self.GetModule().GetManager().ListenUnix(
|
||||
self.ConstructSockName("Py-LU"),
|
||||
path, self._csock)
|
||||
|
||||
try:
|
||||
addr = self.ADDR_MAP[addrtype.lower()]
|
||||
except KeyError:
|
||||
@@ -55,7 +60,7 @@ class Socket:
|
||||
"Specified addrtype [{0}] isn't supported".format(addrtype))
|
||||
|
||||
args = (
|
||||
"python socket for {0}".format(self.GetModule()),
|
||||
self.ConstructSockName("Py-L"),
|
||||
bindhost,
|
||||
ssl,
|
||||
maxconns,
|
||||
@@ -76,13 +81,19 @@ class Socket:
|
||||
return self.GetModule().GetManager().Connect(
|
||||
host,
|
||||
port,
|
||||
'python conn socket for {0}'.format(self.GetModule()),
|
||||
self.ConstructSockName("Py-C"),
|
||||
timeout,
|
||||
ssl,
|
||||
bindhost,
|
||||
self._csock
|
||||
)
|
||||
|
||||
def ConnectUnix(self, path):
|
||||
return self.GetModule().GetManager().ConnectUnix(
|
||||
self.ConstructSockName("Py-CU"),
|
||||
path, self._csock
|
||||
)
|
||||
|
||||
def Write(self, data):
|
||||
if (isinstance(data, str)):
|
||||
return self._csock.Write(data)
|
||||
|
||||
@@ -97,13 +97,8 @@ bool CUnixListener::Listen() {
|
||||
m_pListener = new CRealListener(*this);
|
||||
SetupSSL();
|
||||
|
||||
if (m_pListener->ListenUnix(m_sPath)) {
|
||||
CZNC::Get().GetManager().AddSock(m_pListener, "UNIX_LISTENER");
|
||||
return true;
|
||||
}
|
||||
|
||||
delete m_pListener;
|
||||
return false;
|
||||
return CZNC::Get().GetManager().ListenUnix("UNIX_LISTENER", m_sPath,
|
||||
m_pListener);
|
||||
}
|
||||
|
||||
CConfig CUnixListener::ToConfig() const {
|
||||
|
||||
@@ -539,24 +539,17 @@ bool CSocket::Connect(const CString& sHostname, unsigned short uPort, bool bSSL,
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
CString sSockName = ConstructSockName("C");
|
||||
m_pModule->GetManager()->Connect(sHostname, uPort, sSockName, uTimeout,
|
||||
bSSL, sBindHost, this);
|
||||
return true;
|
||||
@@ -570,21 +563,50 @@ bool CSocket::Listen(unsigned short uPort, bool bSSL, unsigned int uTimeout) {
|
||||
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();
|
||||
}
|
||||
|
||||
CString sSockName = ConstructSockName("L");
|
||||
return m_pModule->GetManager()->ListenAll(uPort, sSockName, bSSL, SOMAXCONN,
|
||||
this);
|
||||
}
|
||||
|
||||
bool CSocket::ListenUnix(const CString& sPath) {
|
||||
if (!m_pModule) {
|
||||
DEBUG(
|
||||
"ERROR: CSocket::Listen called on instance without m_pModule "
|
||||
"handle!");
|
||||
return false;
|
||||
}
|
||||
|
||||
CString sSockName = ConstructSockName("LU");
|
||||
return m_pModule->GetManager()->ListenUnix(sSockName, sPath, this);
|
||||
}
|
||||
|
||||
bool CSocket::ConnectUnix(const CString& sPath) {
|
||||
if (!m_pModule) {
|
||||
DEBUG(
|
||||
"ERROR: CSocket::Listen called on instance without m_pModule "
|
||||
"handle!");
|
||||
return false;
|
||||
}
|
||||
|
||||
CString sSockName = ConstructSockName("CU");
|
||||
return m_pModule->GetManager()->ConnectUnix(sSockName, sPath, this);
|
||||
}
|
||||
|
||||
CString CSocket::ConstructSockName(const CString& sPart) const {
|
||||
CString sSockName = GetSockName();
|
||||
if (!sSockName.empty()) return sSockName;
|
||||
|
||||
sSockName = "MOD::" + sPart + "::" + m_pModule->GetModName();
|
||||
|
||||
if (CUser* pUser = m_pModule->GetUser()) {
|
||||
sSockName += "::" + pUser->GetUsername();
|
||||
if (CIRCNetwork* pNetwork = m_pModule->GetNetwork()) {
|
||||
sSockName += "::" + pNetwork->GetName();
|
||||
}
|
||||
}
|
||||
return sSockName;
|
||||
}
|
||||
|
||||
CModule* CSocket::GetModule() const { return m_pModule; }
|
||||
/////////////////// !CSocket ///////////////////
|
||||
|
||||
|
||||
@@ -156,6 +156,97 @@ TEST_F(ZNCTest, ModperlSocket) {
|
||||
client.ReadUntil("received 4 bytes");
|
||||
}
|
||||
|
||||
TEST_F(ZNCTest, ModpythonUnixSocket) {
|
||||
#ifndef WANT_PYTHON
|
||||
GTEST_SKIP() << "Modpython is disabled";
|
||||
#endif
|
||||
auto znc = Run();
|
||||
znc->CanLeak();
|
||||
|
||||
InstallModule("socktest.py", R"(
|
||||
import znc
|
||||
|
||||
class acc(znc.Socket):
|
||||
def OnReadData(self, data):
|
||||
self.GetModule().PutModule('received {} bytes'.format(len(data)))
|
||||
self.Close()
|
||||
|
||||
class lis(znc.Socket):
|
||||
def OnAccepted(self, host, port):
|
||||
sock = self.GetModule().CreateSocket(acc)
|
||||
sock.DisableReadLine()
|
||||
return sock
|
||||
|
||||
class socktest(znc.Module):
|
||||
def OnLoad(self, args, ret):
|
||||
listen = self.CreateSocket(lis)
|
||||
return listen.Listen(addrtype='unix', path=self.GetSavePath() + "/sock")
|
||||
|
||||
def OnModCommand(self, cmd):
|
||||
sock = self.CreateSocket()
|
||||
sock.ConnectUnix(self.GetSavePath() + "/sock")
|
||||
sock.WriteBytes(b'blah')
|
||||
)");
|
||||
|
||||
auto ircd = ConnectIRCd();
|
||||
auto client = LoginClient();
|
||||
client.Write("znc loadmod modpython");
|
||||
client.Write("znc loadmod socktest");
|
||||
client.Write("PRIVMSG *socktest :foo");
|
||||
client.ReadUntil("received 4 bytes");
|
||||
}
|
||||
|
||||
TEST_F(ZNCTest, ModperlUnixSocket) {
|
||||
#ifndef WANT_PERL
|
||||
GTEST_SKIP() << "Modperl is disabled";
|
||||
#endif
|
||||
auto znc = Run();
|
||||
znc->CanLeak();
|
||||
|
||||
InstallModule("socktest.pm", R"(
|
||||
package socktest::acc;
|
||||
use base 'ZNC::Socket';
|
||||
sub OnReadData {
|
||||
my ($self, $data, $len) = @_;
|
||||
$self->GetModule->PutModule("received $len bytes");
|
||||
$self->Close;
|
||||
}
|
||||
|
||||
package socktest::lis;
|
||||
use base 'ZNC::Socket';
|
||||
sub OnAccepted {
|
||||
my $self = shift;
|
||||
return $self->GetModule->CreateSocket('socktest::acc');
|
||||
}
|
||||
|
||||
package socktest::conn;
|
||||
use base 'ZNC::Socket';
|
||||
|
||||
package socktest;
|
||||
use base 'ZNC::Module';
|
||||
sub OnLoad {
|
||||
my $self = shift;
|
||||
my $listen = $self->CreateSocket('socktest::lis');
|
||||
$listen->Listen(addrtype=>'unix', path=>$self->GetSavePath . "/sock");
|
||||
}
|
||||
sub OnModCommand {
|
||||
my ($self, $cmd) = @_;
|
||||
my $sock = $self->CreateSocket('socktest::conn');
|
||||
$sock->ConnectUnix($self->GetSavePath . "/sock");
|
||||
$sock->Write('blah');
|
||||
}
|
||||
|
||||
1;
|
||||
)");
|
||||
|
||||
auto ircd = ConnectIRCd();
|
||||
auto client = LoginClient();
|
||||
client.Write("znc loadmod modperl");
|
||||
client.Write("znc loadmod socktest");
|
||||
client.Write("PRIVMSG *socktest :foo");
|
||||
client.ReadUntil("received 4 bytes");
|
||||
}
|
||||
|
||||
TEST_F(ZNCTest, ModpythonVCString) {
|
||||
#ifndef WANT_PYTHON
|
||||
GTEST_SKIP() << "Modpython is disabled";
|
||||
|
||||
Reference in New Issue
Block a user