Merge pull request #1945 from DarthGandalf/unix

Unix sockets
This commit is contained in:
Alexey Sokolov
2025-05-02 00:47:26 +01:00
committed by GitHub
35 changed files with 1240 additions and 524 deletions
+2 -2
View File
@@ -32,7 +32,7 @@ sudo make install
/usr/local/bin/znc --version
# TODO: use DEVEL_COVER_OPTIONS for https://metacpan.org/pod/Devel::Cover
env LLVM_PROFILE_FILE="$PWD/inttest.profraw" ZNC_MODPERL_COVERAGE_OPTS="-db,$PWD/cover_db" PYTHONWARNINGS=error make VERBOSE=1 inttest
env LLVM_PROFILE_FILE="$PWD/inttest.profraw.%p" ZNC_MODPERL_COVERAGE_OPTS="-db,$PWD/cover_db" PYTHONWARNINGS=error make VERBOSE=1 inttest
ls -lRa
~/perl5/bin/cover --no-gcov --report=clover
@@ -47,7 +47,7 @@ case "${CC:-gcc}" in
export PATH=$PATH:/Library/Developer/CommandLineTools/usr/bin
fi
llvm-profdata merge unittest.profraw -o unittest.profdata
llvm-profdata merge inttest.profraw -o inttest.profdata
llvm-profdata merge inttest.profraw* -o inttest.profdata
llvm-cov show -show-line-counts-or-regions -instr-profile=unittest.profdata test/unittest_bin > unittest-cmake-coverage.txt
llvm-cov show -show-line-counts-or-regions -instr-profile=inttest.profdata /usr/local/bin/znc > inttest-znc-coverage.txt
find /usr/local/lib/znc -name '*.so' -or -name '*.bundle' | while read f; do llvm-cov show -show-line-counts-or-regions -instr-profile=inttest.profdata $f > inttest-$(basename $f)-coverage.txt; done
+3
View File
@@ -10,3 +10,6 @@
[submodule "third_party/cctz"]
path = third_party/cctz
url = https://github.com/google/cctz
[submodule "third_party/gtest-parallel"]
path = third_party/gtest-parallel
url = https://github.com/google/gtest-parallel
+2
View File
@@ -17,6 +17,8 @@ ZNC includes code from Selectize (http://brianreavis.github.io/selectize.js/), l
ZNC includes modified code from CMakeFindFrameworks.cmake by Kitware, Inc., licensed under BSD License.
ZNC includes modified code from TestLargeFiles.cmake, licensed under Boost Software License, Version 1.0.
ZNC includes code from cctz (https://github.com/google/cctz), licensed under the Apache License 2.0.
ZNC includes code from gtest-parallel (https://github.com/google/gtest-parallel), licensed under the Apache License 2.0.
ZNC integration test includes modified code from Qt, licensed under LGPL.
ZNC is developed by these people:
+2
View File
@@ -114,6 +114,8 @@ class CIRCNetwork : private CCoreTranslationMixin {
CServer* FindServer(const CString& sName) const;
bool DelServer(const CString& sName, unsigned short uPort,
const CString& sPass);
bool DelServer(const CServer& Server);
bool AddServer(CServer Server);
bool AddServer(const CString& sName);
bool AddServer(const CString& sName, unsigned short uPort,
const CString& sPass = "", bool bSSL = false);
+58 -14
View File
@@ -22,33 +22,26 @@
// Forward Declarations
class CRealListener;
class CConfig;
// !Forward Declarations
class CListener {
public:
typedef enum { ACCEPT_IRC, ACCEPT_HTTP, ACCEPT_ALL } EAcceptType;
CListener(unsigned short uPort, const CString& sBindHost,
const CString& sURIPrefix, bool bSSL, EAddrType eAddr,
EAcceptType eAccept)
CListener(const CString& sURIPrefix, bool bSSL, EAcceptType eAccept)
: m_bSSL(bSSL),
m_eAddr(eAddr),
m_uPort(uPort),
m_sBindHost(sBindHost),
m_sURIPrefix(sURIPrefix),
m_pListener(nullptr),
m_eAcceptType(eAccept) {}
~CListener();
virtual ~CListener();
CListener(const CListener&) = delete;
CListener& operator=(const CListener&) = delete;
// Getters
bool IsSSL() const { return m_bSSL; }
EAddrType GetAddrType() const { return m_eAddr; }
unsigned short GetPort() const { return m_uPort; }
const CString& GetBindHost() const { return m_sBindHost; }
CRealListener* GetRealListener() const { return m_pListener; }
const CString& GetURIPrefix() const { return m_sURIPrefix; }
EAcceptType GetAcceptType() const { return m_eAcceptType; }
@@ -58,20 +51,71 @@ class CListener {
// except this one, so don't add other setters!
void SetAcceptType(EAcceptType eType) { m_eAcceptType = eType; }
bool Listen();
virtual bool Listen() = 0;
void ResetRealListener();
virtual CConfig ToConfig() const;
private:
protected:
void SetupSSL() const;
bool m_bSSL;
EAddrType m_eAddr;
unsigned short m_uPort;
CString m_sBindHost;
CString m_sURIPrefix;
CRealListener* m_pListener;
EAcceptType m_eAcceptType;
};
class CTCPListener : public CListener {
public:
CTCPListener(unsigned short uPort, const CString& sBindHost,
const CString& sURIPrefix, bool bSSL, EAddrType eAddr,
EAcceptType eAccept)
: CListener(sURIPrefix, bSSL, eAccept),
m_eAddr(eAddr),
m_uPort(uPort),
m_sBindHost(sBindHost) {}
~CTCPListener();
CTCPListener(const CTCPListener&) = delete;
CTCPListener& operator=(const CTCPListener&) = delete;
// Getters
EAddrType GetAddrType() const { return m_eAddr; }
unsigned short GetPort() const { return m_uPort; }
const CString& GetBindHost() const { return m_sBindHost; }
// !Getters
bool Listen() override;
CConfig ToConfig() const override;
protected:
EAddrType m_eAddr;
unsigned short m_uPort;
CString m_sBindHost;
};
class CUnixListener : public CListener {
public:
CUnixListener(const CString& sPath, const CString& sURIPrefix, bool bSSL,
EAcceptType eAccept)
: CListener(sURIPrefix, bSSL, eAccept),
m_sPath(sPath) {}
~CUnixListener();
CUnixListener(const CUnixListener&) = delete;
CUnixListener& operator=(const CUnixListener&) = delete;
// Getters
const CString& GetPath() const { return m_sPath; }
// !Getters
bool Listen() override;
CConfig ToConfig() const override;
protected:
CString m_sPath;
};
class CRealListener : public CZNCSock {
public:
CRealListener(CListener& listener) : CZNCSock(), m_Listener(listener) {}
+10 -1
View File
@@ -23,13 +23,21 @@
class CServer {
public:
CServer(const CString& sName, unsigned short uPort = 6667,
const CString& sPass = "", bool bSSL = false);
const CString& sPass = "", bool bSSL = false,
bool bUnixSocket = false);
~CServer();
// TODO: use C++20's =default and <=>
bool operator==(const CServer&) const;
bool operator<(const CServer&) const;
static CServer Parse(CString sLine);
const CString& GetName() const;
unsigned short GetPort() const;
const CString& GetPass() const;
bool IsSSL() const;
bool IsUnixSocket() const;
CString GetString(bool bIncludePassword = true) const;
static bool IsValidHostName(const CString& sHostName);
@@ -39,6 +47,7 @@ class CServer {
unsigned short m_uPort;
CString m_sPass;
bool m_bSSL;
bool m_bUnixSocket;
};
#endif // !ZNC_SERVER_H
+26
View File
@@ -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;
+15 -2
View File
@@ -202,11 +202,22 @@ class CZNC : private CCoreTranslationMixin {
// Listener yummy
CListener* FindListener(u_short uPort, const CString& BindHost,
EAddrType eAddr);
CListener* FindUnixListener(const CString& sPath);
bool AddListener(CListener*);
bool AddTCPListener(unsigned short uPort, const CString& sBindHost,
const CString& sURIPrefix, bool bSSL, EAddrType eAddr,
CListener::EAcceptType eAccept, CString& sError);
bool AddUnixListener(const CString& sPath, const CString& sURIPrefix, bool bSSL,
CListener::EAcceptType eAccept, CString& sError);
bool DelListener(CListener*);
// For backwards-compatibility TODO: Remove
/// @deprecated use AddTCPListener
bool AddListener(unsigned short uPort, const CString& sBindHost,
const CString& sURIPrefix, bool bSSL, EAddrType eAddr,
CListener::EAcceptType eAccept, CString& sError);
bool DelListener(CListener*);
CListener::EAcceptType eAccept, CString& sError) {
return AddTCPListener(uPort, sBindHost, sURIPrefix, bSSL, eAddr, eAccept, sError);
}
// Message of the Day
void SetMotd(const CString& sMessage) {
@@ -267,6 +278,8 @@ class CZNC : private CCoreTranslationMixin {
CString MakeConfigHeader();
bool AddListener(const CString& sLine, CString& sError);
bool AddListener(CConfig* pConfig, CString& sError);
bool CheckSslAndPemFile(bool bSSL, CString& sError);
bool FinishAddingListener(CListener* pListener, CString& sError);
protected:
time_t m_TimeStarted;
+2
View File
@@ -45,6 +45,8 @@ mkdir -p --mode=0755 $TMPDIR/$ZNCDIR/third_party/Csocket
cp -p third_party/Csocket/Csocket.cc third_party/Csocket/Csocket.h $TMPDIR/$ZNCDIR/third_party/Csocket/
mkdir -p --mode=0755 $TMPDIR/$ZNCDIR/third_party/cctz
cp -Rp third_party/cctz/src third_party/cctz/include third_party/cctz/LICENSE.txt $TMPDIR/$ZNCDIR/third_party/cctz/
mkdir -p --mode=0755 $TMPDIR/$ZNCDIR/third_party/gtest-parallel
cp -p third_party/gtest-parallel/LICENSE third_party/gtest-parallel/gtest-parallel third_party/gtest-parallel/gtest_parallel.py $TMPDIR/$ZNCDIR/third_party/gtest-parallel/
(
cd $TMPDIR2
cmake $TMPDIR/$ZNCDIR -DWANT_PERL=yes -DWANT_PYTHON=yes
+13 -4
View File
@@ -21,6 +21,7 @@
#include <znc/IRCNetwork.h>
#include <znc/Chan.h>
#include <znc/IRCSock.h>
#include <znc/Server.h>
using std::map;
using std::vector;
@@ -1249,6 +1250,10 @@ class CAdminMod : public CModule {
PutModule(
t_s("Usage: AddServer <username> <network> <server> [[+]port] "
"[password]"));
if (GetUser()->IsAdmin()) {
PutModule(t_s("Or: AddServer <username> <network> unix:[ssl:]/path/to/socket"));
}
PutModule(t_s("+ means SSL"));
return;
}
@@ -1265,7 +1270,13 @@ class CAdminMod : public CModule {
return;
}
if (pNetwork->AddServer(sServer))
CServer Server = CServer::Parse(sServer);
if (Server.IsUnixSocket() && !GetUser()->IsAdmin()) {
PutModule(t_s("Access denied!"));
return;
}
if (pNetwork->AddServer(std::move(Server)))
PutModule(t_f("Added IRC Server {1} to network {2} for user {3}.")(
sServer, pNetwork->GetName(), pUser->GetUsername()));
else
@@ -1278,8 +1289,6 @@ class CAdminMod : public CModule {
CString sUsername = sLine.Token(1);
CString sNetwork = sLine.Token(2);
CString sServer = sLine.Token(3, true);
unsigned short uPort = sLine.Token(4).ToUShort();
CString sPass = sLine.Token(5);
if (sServer.empty()) {
PutModule(
@@ -1301,7 +1310,7 @@ class CAdminMod : public CModule {
return;
}
if (pNetwork->DelServer(sServer, uPort, sPass))
if (pNetwork->DelServer(CServer::Parse(sServer)))
PutModule(
t_f("Deleted IRC Server {1} from network {2} for user {3}.")(
sServer, pNetwork->GetName(), pUser->GetUsername()));
+42 -14
View File
@@ -106,9 +106,11 @@ function serverlist_init($) {
var pass = $(".servers_row_pass", $(this)).val();
if (host.length == 0) return;
text += host;
text += " ";
if (ssl) text += "+";
text += port;
if (!host.startsWith("unix:")) {
text += " ";
if (ssl) text += "+";
text += port;
}
text += " ";
text += pass;
text += "\n";
@@ -122,14 +124,15 @@ function serverlist_init($) {
serialize();
}
if (NetworkEdit) {
var disable = host.startsWith("unix:") && !EditUnixSockets;
row.append(
$("<td/>").append($("<input/>").attr({"type":"text"})
$("<td/>").append($("<input/>").attr({"type":"text","disabled":disable})
.addClass("servers_row_host").val(host)),
$("<td/>").append($("<input/>").attr({"type":"number"})
$("<td/>").append($("<input/>").attr({"type":"number","disabled":disable})
.addClass("servers_row_port").val(port)),
$("<td/>").append($("<input/>").attr({"type":"checkbox"})
$("<td/>").append($("<input/>").attr({"type":"checkbox","disabled":disable})
.addClass("servers_row_ssl").prop("checked", ssl)),
$("<td/>").append($("<input/>").attr({"type":"text"})
$("<td/>").append($("<input/>").attr({"type":"text","disabled":disable})
.addClass("servers_row_pass").val(pass)),
$("<td/>").append($("<input/>").attr({"type":"button"})
.val("X").click(delete_row))
@@ -147,6 +150,25 @@ function serverlist_init($) {
);
}
$("input", row).change(serialize);
$("input.servers_row_host", row).change(function (ev) {
var host = ev.target.value;
if (host.startsWith("unix:")) {
$("input.servers_row_ssl", row)[0].checked = host.startsWith("unix:ssl:");
}
});
$("input.servers_row_ssl", row).change(function (ev) {
var host = $("input.servers_row_host", row).val();
if (host.startsWith("unix:")) {
if (ev.target.checked != host.startsWith("unix:ssl:")) {
if (host.startsWith("unix:ssl:")) {
host = host.substr(9);
} else {
host = host.substr(5);
}
$("input.servers_row_host", row).val("unix:" + (ev.target.checked ? "ssl:" : "") + host);
}
}
});
$("#servers_tbody").append(row);
}
@@ -157,14 +179,20 @@ function serverlist_init($) {
if (line.length == 0) return;
line = line.split(" ");
var host = line[0];
var port = line[1] || "6667";
var pass = line[2] || "";
var ssl;
if (port.match(/^\+/)) {
ssl = true;
port = port.substr(1);
var unix = host.startsWith("unix:");
var port = "0";
var pass = line[unix ? 1 : 2] || "";
var ssl = false;
if (unix) {
if (host.startsWith("unix:ssl:")) {
ssl = true;
}
} else {
ssl = false;
port = line[1] || "6667";
if (port.match(/^\+/)) {
ssl = true;
port = port.substr(1);
}
}
add_row(host, port, ssl, pass);
});
@@ -5,6 +5,7 @@
<script>
var NetworkEdit = <? VAR NetworkEdit ?>;
var EditUnixSockets = <? VAR EditUnixSockets ?>;
</script>
<div class="section">
+27 -5
View File
@@ -12,9 +12,9 @@
<tr>
<th><? FORMAT "Port" ?></th>
<th><? FORMAT "BindHost" ?></th>
<th><? FORMAT "SSL" ?></th>
<th><? FORMAT "IPv4" ?></th>
<th><? FORMAT "IPv6" ?></th>
<th><? FORMAT "SSL" ?></th>
<th><? FORMAT "IRC" ?></th>
<th><? FORMAT "HTTP" ?></th>
<th><? FORMAT "URIPrefix" ?></th>
@@ -24,17 +24,21 @@
<? LOOP ListenLoop ?>
<tr class="<? IF __EVEN__ ?>evenrow<? ELSE ?>oddrow<? ENDIF ?>">
<? IF Type == "TCP" ?>
<td><? VAR Port ?></td>
<td><? VAR BindHost DEFAULT=** ?></td>
<td class="listener_show_ssl">
<div class="checkbox"><input type="checkbox" disabled="disabled" <? IF IsSSL ?>checked="checked"<? ENDIF ?>/></div>
</td>
<td>
<div class="checkbox"><input type="checkbox" disabled="disabled" <? IF IsIPV4 ?>checked="checked"<? ENDIF ?>/></div>
</td>
<td>
<div class="checkbox"><input type="checkbox" disabled="disabled" <? IF IsIPV6 ?>checked="checked"<? ENDIF ?>/></div>
</td>
<? ELSE ?>
<td colspan="4">unix:<? VAR Path ?></td>
<? ENDIF ?>
<td class="listener_show_ssl">
<div class="checkbox"><input type="checkbox" disabled="disabled" <? IF IsSSL ?>checked="checked"<? ENDIF ?>/></div>
</td>
<td>
<div class="checkbox"><input type="checkbox" disabled="disabled" <? IF IsIRC ?>checked="checked"<? ENDIF ?>/></div>
</td>
@@ -46,10 +50,15 @@
<? IF SuggestDeletion ?>
<form action="<? VAR URIPrefix TOP ?><? VAR ModPath TOP ?>del_listener" method="post">
<? INC _csrf_check.tmpl ?>
<input name="type" type="hidden" value="<? VAR Type ?>"/>
<? IF Type == "TCP" ?>
<input name="host" type="hidden" value="<? VAR BindHost ?>"/>
<input name="port" type="hidden" value="<? VAR Port ?>"/>
<input name="ipv4" type="hidden" value="<? VAR IsIPV4 ?>"/>
<input name="ipv6" type="hidden" value="<? VAR IsIPV6 ?>"/>
<? ELSE ?>
<input name="path" type="hidden" value="<? VAR Path ?>"/>
<? ENDIF ?>
<input type="submit" value="<? FORMAT "Del" ?>"/>
</form>
<? ELSE ?>
@@ -61,11 +70,24 @@
<tr>
<form action="<? VAR URIPrefix TOP ?><? VAR ModPath TOP ?>add_listener" method="post">
<? INC _csrf_check.tmpl ?>
<input name="type" type="hidden" value="TCP"/>
<td><input name="port" type="number" min="1" max="65535" class="number third"/></td>
<td><input name="host" type="text" value="*" class="sixth"/></td>
<td><div class="checkbox"><input name="ssl" type="checkbox"/></div></td>
<td><div class="checkbox"><input name="ipv4" type="checkbox" checked="checked"/></div></td>
<td><div class="checkbox"><input name="ipv6" type="checkbox" checked="checked"/></div></td>
<td><div class="checkbox"><input name="ssl" type="checkbox"/></div></td>
<td><div class="checkbox"><input name="irc" type="checkbox" checked="checked"/></div></td>
<td><div class="checkbox"><input name="web" type="checkbox" checked="checked"/></div></td>
<td><input name="uriprefix" type="text" value="/" class="sixth"/></td>
<td><input type="submit" value="<? FORMAT "Add" ?>"/></td>
</form>
</tr>
<tr>
<form action="<? VAR URIPrefix TOP ?><? VAR ModPath TOP ?>add_listener" method="post">
<? INC _csrf_check.tmpl ?>
<input name="type" type="hidden" value="Unix"/>
<td colspan="4">unix:<input name="path" type="text" value="/" class="third"/></td>
<td><div class="checkbox"><input name="ssl" type="checkbox"/></div></td>
<td><div class="checkbox"><input name="irc" type="checkbox" checked="checked"/></div></td>
<td><div class="checkbox"><input name="web" type="checkbox" checked="checked"/></div></td>
<td><input name="uriprefix" type="text" value="/" class="sixth"/></td>
+18 -3
View File
@@ -779,7 +779,7 @@ sub Connect {
$self->GetModule->GetManager->Connect(
$host,
$port,
"perl-socket",
$self->ConstructSockName("Perl-C"),
$arg{timeout}//60,
$arg{ssl}//0,
$arg{bindhost}//'',
@@ -787,11 +787,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) { }
@@ -800,7 +815,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,
@@ -811,7 +826,7 @@ sub Listen {
return 0;
}
$self->GetModule->GetManager->ListenRand(
"perl-socket",
$self->ConstructSockName("Perl-L"),
$arg{bindhost}//'',
$arg{ssl}//0,
$arg{maxconns}//ZNC::_GetSOMAXCONN,
+14 -3
View File
@@ -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)
+118 -148
View File
@@ -99,82 +99,6 @@ class CWebAdminMod : public CModule {
~CWebAdminMod() override {}
bool OnLoad(const CString& sArgStr, CString& sMessage) override {
if (sArgStr.empty() || CModInfo::GlobalModule != GetType()) return true;
// We don't accept any arguments, but for backwards
// compatibility we have to do some magic here.
sMessage = "Arguments converted to new syntax";
bool bSSL = false;
bool bIPv6 = false;
bool bShareIRCPorts = true;
unsigned short uPort = 8080;
CString sArgs(sArgStr);
CString sPort;
CString sListenHost;
CString sURIPrefix;
while (sArgs.Left(1) == "-") {
CString sOpt = sArgs.Token(0);
sArgs = sArgs.Token(1, true);
if (sOpt.Equals("-IPV6")) {
bIPv6 = true;
} else if (sOpt.Equals("-IPV4")) {
bIPv6 = false;
} else if (sOpt.Equals("-noircport")) {
bShareIRCPorts = false;
} else {
// Uhm... Unknown option? Let's just ignore all
// arguments, older versions would have returned
// an error and denied loading
return true;
}
}
// No arguments left: Only port sharing
if (sArgs.empty() && bShareIRCPorts) return true;
if (sArgs.find(" ") != CString::npos) {
sListenHost = sArgs.Token(0);
sPort = sArgs.Token(1, true);
} else {
sPort = sArgs;
}
if (sPort.Left(1) == "+") {
sPort.TrimLeft("+");
bSSL = true;
}
if (!sPort.empty()) {
uPort = sPort.ToUShort();
}
if (!bShareIRCPorts) {
// Make all existing listeners IRC-only
const vector<CListener*>& vListeners = CZNC::Get().GetListeners();
for (CListener* pListener : vListeners) {
pListener->SetAcceptType(CListener::ACCEPT_IRC);
}
}
// Now turn that into a listener instance
CListener* pListener = new CListener(
uPort, sListenHost, sURIPrefix, bSSL,
(!bIPv6 ? ADDR_IPV4ONLY : ADDR_ALL), CListener::ACCEPT_HTTP);
if (!pListener->Listen()) {
sMessage = "Failed to add backwards-compatible listener";
return false;
}
CZNC::Get().AddListener(pListener);
SetArgs("");
return true;
}
CUser* GetNewUser(CWebSock& WebSock, CUser* pUser) {
std::shared_ptr<CWebSession> spSession = WebSock.GetSession();
CString sUsername = WebSock.GetParam("newuser");
@@ -1048,6 +972,7 @@ class CWebAdminMod : public CModule {
Tmpl["NetworkEdit"] =
spSession->IsAdmin() || !spSession->GetUser()->DenySetNetwork()
? "true" : "false";
Tmpl["EditUnixSockets"] = spSession->IsAdmin() ? "true" : "false";
Tmpl["FloodProtection"] =
CString(CIRCSock::IsFloodProtected(pNetwork->GetFloodRate()));
@@ -1223,9 +1148,22 @@ class CWebAdminMod : public CModule {
VCString vsArgs;
if (spSession->IsAdmin() || !spSession->GetUser()->DenySetNetwork()) {
std::set<CServer> vAllowedUnixServers;
for (const CServer* pServer : pNetwork->GetServers()) {
if (pServer->IsUnixSocket()) {
vAllowedUnixServers.insert(*pServer);
}
}
pNetwork->DelServers();
WebSock.GetRawParam("servers").Split("\n", vsArgs);
for (const CString& sServer : vsArgs) {
CServer Server = CServer::Parse(sServer);
if (Server.IsUnixSocket() && !spSession->IsAdmin() &&
vAllowedUnixServers.count(Server) == 0) {
// For non-admins, allow unix sockets only if they had these
// exact servers before.
continue;
}
pNetwork->AddServer(sServer.Trim_n());
}
}
@@ -1480,9 +1418,11 @@ class CWebAdminMod : public CModule {
l["IRCNick"] = pNetwork->GetIRCNick().GetNick();
CServer* pServer = pNetwork->GetCurrentServer();
if (pServer) {
l["Server"] = pServer->GetName() + ":" +
(pServer->IsSSL() ? "+" : "") +
CString(pServer->GetPort());
l["Server"] = pServer->IsUnixSocket()
? "unix:" + pServer->GetName()
: pServer->GetName() + ":" +
(pServer->IsSSL() ? "+" : "") +
CString(pServer->GetPort());
}
}
@@ -1920,33 +1860,10 @@ class CWebAdminMod : public CModule {
}
bool AddListener(CWebSock& WebSock, CTemplate& Tmpl) {
unsigned short uPort = WebSock.GetParam("port").ToUShort();
CString sHost = WebSock.GetParam("host");
CString sURIPrefix = WebSock.GetParam("uriprefix");
if (sHost == "*") sHost = "";
bool bSSL = WebSock.GetParam("ssl").ToBool();
bool bIPv4 = WebSock.GetParam("ipv4").ToBool();
bool bIPv6 = WebSock.GetParam("ipv6").ToBool();
bool bIRC = WebSock.GetParam("irc").ToBool();
bool bWeb = WebSock.GetParam("web").ToBool();
EAddrType eAddr = ADDR_ALL;
if (bIPv4) {
if (bIPv6) {
eAddr = ADDR_ALL;
} else {
eAddr = ADDR_IPV4ONLY;
}
} else {
if (bIPv6) {
eAddr = ADDR_IPV6ONLY;
} else {
WebSock.GetSession()->AddError(
t_s("Choose either IPv4 or IPv6 or both."));
return SettingsPage(WebSock, Tmpl);
}
}
CListener::EAcceptType eAccept;
if (bIRC) {
if (bWeb) {
@@ -1965,8 +1882,39 @@ class CWebAdminMod : public CModule {
}
CString sMessage;
if (CZNC::Get().AddListener(uPort, sHost, sURIPrefix, bSSL, eAddr,
eAccept, sMessage)) {
bool bResult;
if (WebSock.GetParam("type") == "TCP") {
unsigned short uPort = WebSock.GetParam("port").ToUShort();
CString sHost = WebSock.GetParam("host");
if (sHost == "*") sHost = "";
bool bIPv4 = WebSock.GetParam("ipv4").ToBool();
bool bIPv6 = WebSock.GetParam("ipv6").ToBool();
EAddrType eAddr = ADDR_ALL;
if (bIPv4) {
if (bIPv6) {
eAddr = ADDR_ALL;
} else {
eAddr = ADDR_IPV4ONLY;
}
} else {
if (bIPv6) {
eAddr = ADDR_IPV6ONLY;
} else {
WebSock.GetSession()->AddError(
t_s("Choose either IPv4 or IPv6 or both."));
return SettingsPage(WebSock, Tmpl);
}
}
bResult = CZNC::Get().AddListener(uPort, sHost, sURIPrefix, bSSL,
eAddr, eAccept, sMessage);
} else {
CString sPath = WebSock.GetParam("path");
bResult = CZNC::Get().AddUnixListener(sPath, sURIPrefix, bSSL,
eAccept, sMessage);
}
if (bResult) {
if (!sMessage.empty()) {
WebSock.GetSession()->AddSuccess(sMessage);
}
@@ -1982,28 +1930,34 @@ class CWebAdminMod : public CModule {
}
bool DelListener(CWebSock& WebSock, CTemplate& Tmpl) {
unsigned short uPort = WebSock.GetParam("port").ToUShort();
CString sHost = WebSock.GetParam("host");
bool bIPv4 = WebSock.GetParam("ipv4").ToBool();
bool bIPv6 = WebSock.GetParam("ipv6").ToBool();
CListener* pListener;
EAddrType eAddr = ADDR_ALL;
if (bIPv4) {
if (bIPv6) {
eAddr = ADDR_ALL;
if (WebSock.GetParam("type") == "TCP") {
unsigned short uPort = WebSock.GetParam("port").ToUShort();
CString sHost = WebSock.GetParam("host");
bool bIPv4 = WebSock.GetParam("ipv4").ToBool();
bool bIPv6 = WebSock.GetParam("ipv6").ToBool();
EAddrType eAddr = ADDR_ALL;
if (bIPv4) {
if (bIPv6) {
eAddr = ADDR_ALL;
} else {
eAddr = ADDR_IPV4ONLY;
}
} else {
eAddr = ADDR_IPV4ONLY;
if (bIPv6) {
eAddr = ADDR_IPV6ONLY;
} else {
WebSock.GetSession()->AddError(t_s("Invalid request."));
return SettingsPage(WebSock, Tmpl);
}
}
pListener = CZNC::Get().FindListener(uPort, sHost, eAddr);
} else {
if (bIPv6) {
eAddr = ADDR_IPV6ONLY;
} else {
WebSock.GetSession()->AddError(t_s("Invalid request."));
return SettingsPage(WebSock, Tmpl);
}
pListener = CZNC::Get().FindUnixListener(WebSock.GetParam("path"));
}
CListener* pListener = CZNC::Get().FindListener(uPort, sHost, eAddr);
if (pListener) {
CZNC::Get().DelListener(pListener);
if (!CZNC::Get().WriteConfig()) {
@@ -2043,45 +1997,61 @@ class CWebAdminMod : public CModule {
for (const CListener* pListener : vpListeners) {
CTemplate& l = Tmpl.AddRow("ListenLoop");
l["Port"] = CString(pListener->GetPort());
l["BindHost"] = pListener->GetBindHost();
if (const CTCPListener* pTCPListener =
dynamic_cast<const CTCPListener*>(pListener)) {
l["Type"] = "TCP";
l["Port"] = CString(pTCPListener->GetPort());
l["BindHost"] = pTCPListener->GetBindHost();
// simple protection for user from shooting his own foot
// TODO check also for hosts/families
// such check is only here, user still can forge HTTP request to
// delete web port
l["SuggestDeletion"] =
CString(pTCPListener->GetPort() != WebSock.GetLocalPort());
#ifdef HAVE_IPV6
switch (pTCPListener->GetAddrType()) {
case ADDR_IPV4ONLY:
l["IsIPV4"] = "true";
break;
case ADDR_IPV6ONLY:
l["IsIPV6"] = "true";
break;
case ADDR_ALL:
l["IsIPV4"] = "true";
l["IsIPV6"] = "true";
break;
}
#else
l["IsIPV4"] = "true";
#endif
}
if (const CUnixListener* pUnixListener =
dynamic_cast<const CUnixListener*>(pListener)) {
l["Type"] = "Unix";
l["Path"] = pUnixListener->GetPath();
// We can't determine whether it's the same port, as it's
// always "localhost". Just assume the user knows what he's
// doing. Unix sockets are advanced topic anyway.
l["SuggestDeletion"] = "true";
}
l["IsHTTP"] = CString(pListener->GetAcceptType() !=
CListener::ACCEPT_IRC);
l["IsIRC"] = CString(pListener->GetAcceptType() !=
CListener::ACCEPT_HTTP);
l["URIPrefix"] = pListener->GetURIPrefix() + "/";
// simple protection for user from shooting his own foot
// TODO check also for hosts/families
// such check is only here, user still can forge HTTP request to
// delete web port
l["SuggestDeletion"] =
CString(pListener->GetPort() != WebSock.GetLocalPort());
CString sURIPrefix = pListener->GetURIPrefix();
if (!sURIPrefix.EndsWith("/")) {
sURIPrefix += "/";
}
l["URIPrefix"] = sURIPrefix;
#ifdef HAVE_LIBSSL
if (pListener->IsSSL()) {
l["IsSSL"] = "true";
}
#endif
#ifdef HAVE_IPV6
switch (pListener->GetAddrType()) {
case ADDR_IPV4ONLY:
l["IsIPV4"] = "true";
break;
case ADDR_IPV6ONLY:
l["IsIPV6"] = "true";
break;
case ADDR_ALL:
l["IsIPV4"] = "true";
l["IsIPV6"] = "true";
break;
}
#else
l["IsIPV4"] = "true";
#endif
}
vector<CString> vDirs;
+1 -1
View File
@@ -1341,7 +1341,7 @@ void CClient::RefuseSASLLogin(const CString& sReason) {
void CClient::AcceptSASLLogin(CUser& User) {
PutClient(":irc.znc.in 900 " + GetNick() + " " + GetNick() + "!" +
User.GetIdent() + "@" + GetHostName() + " " + User.GetUsername() +
User.GetIdent() + "@" + GetRemoteIP() + " " + User.GetUsername() +
" :You are now logged in as " + User.GetUsername());
PutClient(":irc.znc.in 903 " + GetNick() +
" :SASL authentication successful");
+118 -66
View File
@@ -817,7 +817,7 @@ void CClient::UserCommand(CString& sLine) {
return;
}
CString sServer = sLine.Token(1);
CString sServer = sLine.Token(1, true);
if (!m_pNetwork) {
PutStatus(t_s(
@@ -827,10 +827,20 @@ void CClient::UserCommand(CString& sLine) {
if (sServer.empty()) {
PutStatus(t_s("Usage: AddServer <host> [[+]port] [pass]"));
if (m_pUser->IsAdmin()) {
PutStatus(t_s("Or: AddServer unix:[ssl:]/path/to/socket"));
}
PutStatus(t_s("+ means SSL"));
return;
}
if (m_pNetwork->AddServer(sLine.Token(1, true))) {
CServer Server = CServer::Parse(sServer);
if (Server.IsUnixSocket() && !m_pUser->IsAdmin()) {
PutStatus(t_s("Access denied!"));
return;
}
if (m_pNetwork->AddServer(std::move(Server))) {
PutStatus(t_s("Server added"));
} else {
PutStatus(
@@ -849,11 +859,9 @@ void CClient::UserCommand(CString& sLine) {
return;
}
CString sServer = sLine.Token(1);
unsigned short uPort = sLine.Token(2).ToUShort();
CString sPass = sLine.Token(3);
CServer Server = CServer::Parse(sLine.Token(1, true));
if (sServer.empty()) {
if (Server.GetName().empty()) {
PutStatus(t_s("Usage: DelServer <host> [port] [pass]"));
return;
}
@@ -863,7 +871,9 @@ void CClient::UserCommand(CString& sLine) {
return;
}
if (m_pNetwork->DelServer(sServer, uPort, sPass)) {
// Unix sockets can be removed with "unix:" prefix and without, both
// work.
if (m_pNetwork->DelServer(Server)) {
PutStatus(t_s("Server removed"));
} else {
PutStatus(t_s("No such server"));
@@ -888,9 +898,12 @@ void CClient::UserCommand(CString& sLine) {
Table.AddRow();
Table.SetCell(
t_s("Host", "listservers"),
pServer->GetName() + (pServer == pCurServ ? "*" : ""));
Table.SetCell(t_s("Port", "listservers"),
CString(pServer->GetPort()));
(pServer->IsUnixSocket() ? pServer->GetString(false)
: pServer->GetName()) +
(pServer == pCurServ ? "*" : ""));
if (!pServer->IsUnixSocket())
Table.SetCell(t_s("Port", "listservers"),
CString(pServer->GetPort()));
Table.SetCell(
t_s("SSL", "listservers"),
(pServer->IsSSL()) ? t_s("SSL", "listservers|cell") : "");
@@ -1620,6 +1633,10 @@ void CClient::UserCommand(CString& sLine) {
}
}
namespace {
struct PortCommandUsage {};
}
void CClient::UserPortCommand(CString& sLine) {
const CString sCommand = sLine.Token(0);
@@ -1636,23 +1653,31 @@ void CClient::UserPortCommand(CString& sLine) {
for (const CListener* pListener : vpListeners) {
Table.AddRow();
Table.SetCell(t_s("Port", "listports"),
CString(pListener->GetPort()));
Table.SetCell(
t_s("BindHost", "listports"),
(pListener->GetBindHost().empty() ? CString("*")
: pListener->GetBindHost()));
if (const CTCPListener* pTCPListener =
dynamic_cast<const CTCPListener*>(pListener)) {
Table.SetCell(t_s("Port", "listports"),
CString(pTCPListener->GetPort()));
Table.SetCell(t_s("BindHost", "listports"),
(pTCPListener->GetBindHost().empty()
? CString("*")
: pTCPListener->GetBindHost()));
EAddrType eAddr = pTCPListener->GetAddrType();
Table.SetCell(
t_s("Protocol", "listports"),
eAddr == ADDR_ALL
? t_s("IPv4 and IPv6", "listports")
: (eAddr == ADDR_IPV4ONLY ? t_s("IPv4", "listports")
: t_s("IPv6", "listports")));
} else if (const CUnixListener* pUnixListener =
dynamic_cast<const CUnixListener*>(pListener)) {
Table.SetCell(t_s("Port", "listports"),
pUnixListener->GetPath());
}
Table.SetCell(t_s("SSL", "listports"),
pListener->IsSSL() ? t_s("yes", "listports|ssl")
: t_s("no", "listports|ssl"));
EAddrType eAddr = pListener->GetAddrType();
Table.SetCell(t_s("Protocol", "listports"),
eAddr == ADDR_ALL ? t_s("IPv4 and IPv6", "listports")
: (eAddr == ADDR_IPV4ONLY
? t_s("IPv4", "listports")
: t_s("IPv6", "listports")));
CListener::EAcceptType eAccept = pListener->GetAcceptType();
Table.SetCell(t_s("IRC", "listports"),
eAccept == CListener::ACCEPT_ALL ||
@@ -1672,68 +1697,92 @@ void CClient::UserPortCommand(CString& sLine) {
return;
}
auto ParseEAddr = [](const CString& sAddr) {
if (sAddr.Equals("IPV4")) {
return ADDR_IPV4ONLY;
} else if (sAddr.Equals("IPV6")) {
return ADDR_IPV6ONLY;
} else if (sAddr.Equals("ALL")) {
return ADDR_ALL;
} else {
throw PortCommandUsage{};
}
};
auto ParseEAccept = [](const CString& sAccept) {
if (sAccept.Equals("WEB")) {
return CListener::ACCEPT_HTTP;
} else if (sAccept.Equals("IRC")) {
return CListener::ACCEPT_IRC;
} else if (sAccept.Equals("ALL")) {
return CListener::ACCEPT_ALL;
} else {
throw PortCommandUsage{};
}
};
CString sPort = sLine.Token(1);
CString sAddr = sLine.Token(2);
EAddrType eAddr = ADDR_ALL;
if (sAddr.Equals("IPV4")) {
eAddr = ADDR_IPV4ONLY;
} else if (sAddr.Equals("IPV6")) {
eAddr = ADDR_IPV6ONLY;
} else if (sAddr.Equals("ALL")) {
eAddr = ADDR_ALL;
} else {
sAddr.clear();
}
unsigned short uPort = sPort.ToUShort();
if (sCommand.Equals("ADDPORT")) {
CListener::EAcceptType eAccept = CListener::ACCEPT_ALL;
CString sAccept = sLine.Token(3);
try {
if (sPort.empty()) {
throw PortCommandUsage{};
}
if (sAccept.Equals("WEB")) {
eAccept = CListener::ACCEPT_HTTP;
} else if (sAccept.Equals("IRC")) {
eAccept = CListener::ACCEPT_IRC;
} else if (sAccept.Equals("ALL")) {
eAccept = CListener::ACCEPT_ALL;
} else {
sAccept.clear();
}
std::unique_ptr<CListener> pListener;
if (sPort.TrimPrefix("unix:")) {
bool bSSL = sPort.TrimPrefix("+");
const CString& sPath = sPort;
CListener::EAcceptType eAccept = ParseEAccept(sLine.Token(2));
CString sURIPrefix = sLine.Token(3);
if (sPort.empty() || sAddr.empty() || sAccept.empty()) {
PutStatus(
t_s("Usage: AddPort <[+]port> <ipv4|ipv6|all> <web|irc|all> "
"[bindhost [uriprefix]]"));
} else {
bool bSSL = (sPort.StartsWith("+"));
const CString sBindHost = sLine.Token(4);
const CString sURIPrefix = sLine.Token(5);
pListener.reset(new CUnixListener(sPath, sURIPrefix, bSSL, eAccept));
} else {
bool bSSL = sPort.StartsWith("+");
EAddrType eAddr = ParseEAddr(sLine.Token(2));
CListener::EAcceptType eAccept = ParseEAccept(sLine.Token(3));
const CString sBindHost = sLine.Token(4);
const CString sURIPrefix = sLine.Token(5);
CListener* pListener = new CListener(uPort, sBindHost, sURIPrefix,
bSSL, eAddr, eAccept);
pListener.reset(new CTCPListener(uPort, sBindHost, sURIPrefix,
bSSL, eAddr, eAccept));
}
if (!pListener->Listen()) {
auto e = errno;
delete pListener;
PutStatus(t_f("Unable to bind: {1}")(CString(strerror(e))));
} else {
if (CZNC::Get().AddListener(pListener)) {
if (CZNC::Get().AddListener(pListener.release())) {
PutStatus(t_s("Port added"));
} else {
PutStatus(t_s("Couldn't add port"));
}
}
} catch (PortCommandUsage) {
PutStatus(
t_s("Usage: AddPort <[+]port> <ipv4|ipv6|all> <web|irc|all> "
"[bindhost [uriprefix]]"));
PutStatus(
t_s("Or: AddPort unix:[+]/path/to/socket <web|irc|all> "
"[uriprefix]"));
PutStatus(t_s("+ means SSL"));
}
} else if (sCommand.Equals("DELPORT")) {
if (sPort.empty() || sAddr.empty()) {
PutStatus(t_s("Usage: DelPort <port> <ipv4|ipv6|all> [bindhost]"));
} else {
const CString sBindHost = sLine.Token(3);
CListener* pListener =
CZNC::Get().FindListener(uPort, sBindHost, eAddr);
try {
if (sPort.empty()) {
throw PortCommandUsage{};
}
CListener* pListener;
if (sPort.TrimPrefix("unix:")) {
sPort.TrimPrefix("+");
pListener = CZNC::Get().FindUnixListener(sPort);
} else {
CString sAddr = sLine.Token(2);
CString sBindHost = sLine.Token(3);
pListener = CZNC::Get().FindListener(
uPort, sBindHost, ParseEAddr(sAddr));
}
if (pListener) {
CZNC::Get().DelListener(pListener);
@@ -1741,6 +1790,9 @@ void CClient::UserPortCommand(CString& sLine) {
} else {
PutStatus(t_s("Unable to find a matching port"));
}
} catch (PortCommandUsage) {
PutStatus(t_s("Usage: DelPort <port> <ipv4|ipv6|all> [bindhost]"));
PutStatus(t_s("Or: DelPort unix:/path/to/socket"));
}
}
}
+38 -43
View File
@@ -215,8 +215,7 @@ void CIRCNetwork::Clone(const CIRCNetwork& Network, bool bCloneName) {
DelServers();
for (CServer* pServer : vServers) {
AddServer(pServer->GetName(), pServer->GetPort(), pServer->GetPass(),
pServer->IsSSL());
AddServer(*pServer);
}
m_uServerIdx = 0;
@@ -1155,6 +1154,11 @@ bool CIRCNetwork::DelServer(const CString& sName, unsigned short uPort,
return false;
}
CServer Server(sName, uPort, sPass);
return DelServer(Server);
}
bool CIRCNetwork::DelServer(const CServer& Server) {
unsigned int a = 0;
bool bSawCurrentServer = false;
CServer* pCurServer = GetCurrentServer();
@@ -1165,11 +1169,16 @@ bool CIRCNetwork::DelServer(const CString& sName, unsigned short uPort,
if (pServer == pCurServer) bSawCurrentServer = true;
if (!pServer->GetName().Equals(sName)) continue;
// Unix sockets can be removed with "unix:" prefix and without, both
// work - that's not part of GetName()
if (!pServer->GetName().Equals(Server.GetName())) continue;
if (uPort != 0 && pServer->GetPort() != uPort) continue;
// But it makes no sense to remove TCP server via "unix:hostname.com"
if (!pServer->IsUnixSocket() && Server.IsUnixSocket()) continue;
if (!sPass.empty() && pServer->GetPass() != sPass) continue;
if (Server.GetPort() != 6667 && pServer->GetPort() != Server.GetPort()) continue;
if (!Server.GetPass().empty() && pServer->GetPass() != Server.GetPass()) continue;
m_vServers.erase(it);
@@ -1205,21 +1214,23 @@ bool CIRCNetwork::AddServer(const CString& sName) {
return false;
}
bool bSSL = false;
CString sLine = sName;
sLine.Trim();
return AddServer(CServer::Parse(sName));
}
CString sHost = sLine.Token(0);
CString sPort = sLine.Token(1);
bool CIRCNetwork::AddServer(CServer Server) {
if (Server.GetName().empty()) return false;
#ifndef HAVE_LIBSSL
if (Server.IsSSL()) return false;
#endif
if (sPort.TrimPrefix("+")) {
bSSL = true;
// Check if server is already added
for (CServer* pServer : m_vServers) {
if (*pServer == Server) return false;
}
unsigned short uPort = sPort.ToUShort();
CString sPass = sLine.Token(2, true);
return AddServer(sHost, uPort, sPass, bSSL);
m_vServers.push_back(new CServer(std::move(Server)));
CheckIRCConnect();
return true;
}
bool CIRCNetwork::AddServer(const CString& sName, unsigned short uPort,
@@ -1234,30 +1245,7 @@ bool CIRCNetwork::AddServer(const CString& sName, unsigned short uPort,
return false;
}
if (!uPort) {
uPort = 6667;
}
// Check if server is already added
for (CServer* pServer : m_vServers) {
if (!sName.Equals(pServer->GetName())) continue;
if (uPort != pServer->GetPort()) continue;
if (sPass != pServer->GetPass()) continue;
if (bSSL != pServer->IsSSL()) continue;
// Server is already added
return false;
}
CServer* pServer = new CServer(sName, uPort, sPass, bSSL);
m_vServers.push_back(pServer);
CheckIRCConnect();
return true;
return AddServer(CServer(sName, uPort, sPass, bSSL));
}
CServer* CIRCNetwork::GetNextServer(bool bAdvance) {
@@ -1374,9 +1362,16 @@ bool CIRCNetwork::Connect() {
}
CString sSockName = "IRC::" + m_pUser->GetUsername() + "::" + m_sName;
CZNC::Get().GetManager().Connect(pServer->GetName(), pServer->GetPort(),
sSockName, 120, bSSL, GetBindHost(),
pIRCSock);
if (pServer->IsUnixSocket()) {
pIRCSock->SetSSL(bSSL);
CZNC::Get().GetManager().ConnectUnix(sSockName, pServer->GetName(),
pIRCSock);
} else {
CZNC::Get().GetManager().Connect(pServer->GetName(), pServer->GetPort(),
sSockName, 120, bSSL, GetBindHost(),
pIRCSock);
}
return true;
}
+73 -12
View File
@@ -15,39 +15,100 @@
*/
#include <znc/Listener.h>
#include <znc/Config.h>
#include <znc/znc.h>
CListener::~CListener() {
if (m_pListener) CZNC::Get().GetManager().DelSockByAddr(m_pListener);
}
bool CListener::Listen() {
CConfig CListener::ToConfig() const {
CConfig listenerConfig;
listenerConfig.AddKeyValuePair("URIPrefix", GetURIPrefix() + "/");
listenerConfig.AddKeyValuePair("SSL", CString(IsSSL()));
listenerConfig.AddKeyValuePair(
"AllowIRC",
CString(GetAcceptType() != CListener::ACCEPT_HTTP));
listenerConfig.AddKeyValuePair(
"AllowWeb",
CString(GetAcceptType() != CListener::ACCEPT_IRC));
return listenerConfig;
}
void CListener::SetupSSL() const {
#ifdef HAVE_LIBSSL
if (IsSSL()) {
m_pListener->SetSSL(true);
m_pListener->SetPemLocation(CZNC::Get().GetPemLocation());
m_pListener->SetKeyLocation(CZNC::Get().GetKeyLocation());
m_pListener->SetDHParamLocation(CZNC::Get().GetDHParamLocation());
}
#endif
}
CTCPListener::~CTCPListener() {
}
bool CTCPListener::Listen() {
if (!m_uPort || m_pListener) {
errno = EINVAL;
return false;
}
m_pListener = new CRealListener(*this);
bool bSSL = false;
#ifdef HAVE_LIBSSL
if (IsSSL()) {
bSSL = true;
m_pListener->SetPemLocation(CZNC::Get().GetPemLocation());
m_pListener->SetKeyLocation(CZNC::Get().GetKeyLocation());
m_pListener->SetDHParamLocation(CZNC::Get().GetDHParamLocation());
}
#endif
SetupSSL();
// If e.g. getaddrinfo() fails, the following might not set errno.
// Make sure there is a consistent error message, not something random
// which might even be "Error: Success".
errno = EINVAL;
return CZNC::Get().GetManager().ListenHost(m_uPort, "_LISTENER",
m_sBindHost, bSSL, SOMAXCONN,
m_sBindHost, IsSSL(), SOMAXCONN,
m_pListener, 0, m_eAddr);
}
CConfig CTCPListener::ToConfig() const {
CConfig listenerConfig = CListener::ToConfig();
listenerConfig.AddKeyValuePair("Host", GetBindHost());
listenerConfig.AddKeyValuePair("Port", CString(GetPort()));
listenerConfig.AddKeyValuePair(
"IPv4", CString(GetAddrType() != ADDR_IPV6ONLY));
listenerConfig.AddKeyValuePair(
"IPv6", CString(GetAddrType() != ADDR_IPV4ONLY));
return listenerConfig;
}
CUnixListener::~CUnixListener() {
}
bool CUnixListener::Listen() {
if (m_pListener) {
errno = EINVAL;
return false;
}
m_pListener = new CRealListener(*this);
SetupSSL();
return CZNC::Get().GetManager().ListenUnix("UNIX_LISTENER", m_sPath,
m_pListener);
}
CConfig CUnixListener::ToConfig() const {
CConfig listenerConfig = CListener::ToConfig();
listenerConfig.AddKeyValuePair("Path", GetPath());
return listenerConfig;
}
void CListener::ResetRealListener() { m_pListener = nullptr; }
CRealListener::~CRealListener() { m_Listener.ResetRealListener(); }
+63 -5
View File
@@ -17,11 +17,12 @@
#include <znc/Server.h>
CServer::CServer(const CString& sName, unsigned short uPort,
const CString& sPass, bool bSSL)
const CString& sPass, bool bSSL, bool bUnixSocket)
: m_sName(sName),
m_uPort((uPort) ? uPort : (unsigned short)6667),
m_sPass(sPass),
m_bSSL(bSSL) {}
m_bSSL(bSSL),
m_bUnixSocket(bUnixSocket) {}
CServer::~CServer() {}
@@ -33,9 +34,66 @@ const CString& CServer::GetName() const { return m_sName; }
unsigned short CServer::GetPort() const { return m_uPort; }
const CString& CServer::GetPass() const { return m_sPass; }
bool CServer::IsSSL() const { return m_bSSL; }
bool CServer::IsUnixSocket() const { return m_bUnixSocket; }
CString CServer::GetString(bool bIncludePassword) const {
return m_sName + " " + CString(m_bSSL ? "+" : "") + CString(m_uPort) +
CString(bIncludePassword ? (m_sPass.empty() ? "" : " " + m_sPass)
: "");
CString sResult;
if (m_bUnixSocket) {
sResult = "unix:" + CString(m_bSSL ? "ssl:" : "") + m_sName;
} else {
sResult = m_sName + " " + CString(m_bSSL ? "+" : "") + CString(m_uPort);
}
sResult +=
CString(bIncludePassword ? (m_sPass.empty() ? "" : " " + m_sPass) : "");
return sResult;
}
CServer CServer::Parse(CString sLine) {
bool bSSL = false;
sLine.Trim();
if (sLine.TrimPrefix("unix:")) {
if (sLine.TrimPrefix("ssl:")) {
bSSL = true;
}
CString sPath = sLine.Token(0);
CString sPass = sLine.Token(1, true);
return CServer(sPath, 0, sPass, bSSL, true);
}
CString sHost = sLine.Token(0);
CString sPort = sLine.Token(1);
if (sPort.TrimPrefix("+")) {
bSSL = true;
}
unsigned short uPort = sPort.ToUShort();
CString sPass = sLine.Token(2, true);
return CServer(sHost, uPort, sPass, bSSL, false);
}
bool CServer::operator==(const CServer& o) const {
if (m_sName != o.m_sName) return false;
if (m_uPort != o.m_uPort) return false;
if (m_sPass != o.m_sPass) return false;
if (m_bSSL != o.m_bSSL) return false;
if (m_bUnixSocket != o.m_bUnixSocket) return false;
return true;
}
bool CServer::operator<(const CServer& o) const {
if (m_sName < o.m_sName) return true;
if (m_sName > o.m_sName) return false;
if (m_uPort < o.m_uPort) return true;
if (m_uPort > o.m_uPort) return false;
if (m_sPass < o.m_sPass) return true;
if (m_sPass > o.m_sPass) return false;
if (m_bSSL < o.m_bSSL) return true;
if (m_bSSL > o.m_bSSL) return false;
if (m_bUnixSocket < o.m_bUnixSocket) return true;
if (m_bUnixSocket > o.m_bUnixSocket) return false;
return false;
}
+41 -19
View File
@@ -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 ///////////////////
+160 -125
View File
@@ -486,29 +486,8 @@ bool CZNC::WriteConfig() {
unsigned int l = 0;
for (CListener* pListener : m_vpListeners) {
CConfig listenerConfig;
listenerConfig.AddKeyValuePair("Host", pListener->GetBindHost());
listenerConfig.AddKeyValuePair("URIPrefix",
pListener->GetURIPrefix() + "/");
listenerConfig.AddKeyValuePair("Port", CString(pListener->GetPort()));
listenerConfig.AddKeyValuePair(
"IPv4", CString(pListener->GetAddrType() != ADDR_IPV6ONLY));
listenerConfig.AddKeyValuePair(
"IPv6", CString(pListener->GetAddrType() != ADDR_IPV4ONLY));
listenerConfig.AddKeyValuePair("SSL", CString(pListener->IsSSL()));
listenerConfig.AddKeyValuePair(
"AllowIRC",
CString(pListener->GetAcceptType() != CListener::ACCEPT_HTTP));
listenerConfig.AddKeyValuePair(
"AllowWeb",
CString(pListener->GetAcceptType() != CListener::ACCEPT_IRC));
config.AddSubConfig("Listener", "listener" + CString(l++),
listenerConfig);
pListener->ToConfig());
}
config.AddKeyValuePair("ConnectDelay", CString(m_uiConnectDelay));
@@ -646,49 +625,57 @@ bool CZNC::WriteNewConfig(const CString& sConfigFile) {
unsigned int uListenPort = 0;
bool bSuccess;
do {
bSuccess = true;
while (true) {
if (!CUtils::GetNumInput("Listen on port", uListenPort, 1025,
65534)) {
continue;
}
if (uListenPort == 6667 || uListenPort == 6697) {
CUtils::PrintStatus(false,
"WARNING: Some web browsers reject ports "
"6667 and 6697. If you intend to");
CUtils::PrintStatus(false,
"use ZNC's web interface, you might want "
"to use another port.");
if (!CUtils::GetBoolInput("Proceed anyway?",
true)) {
// Unix sockets are not exposed in --makeconf by default, but it's possible
// to trigger this using env var. This is mostly useful for the integration
// test.
char* szListenUnixSocket = getenv("ZNC_LISTEN_UNIX_SOCKET");
if (!szListenUnixSocket) {
do {
bSuccess = true;
while (true) {
if (!CUtils::GetNumInput("Listen on port", uListenPort, 1025,
65534)) {
continue;
}
if (uListenPort == 6667 || uListenPort == 6697) {
CUtils::PrintStatus(
false,
"WARNING: Some web browsers reject ports "
"6667 and 6697. If you intend to");
CUtils::PrintStatus(
false,
"use ZNC's web interface, you might want "
"to use another port.");
if (!CUtils::GetBoolInput("Proceed anyway?", true)) {
continue;
}
}
break;
}
break;
}
#ifdef HAVE_LIBSSL
bListenSSL = CUtils::GetBoolInput("Listen using SSL", bListenSSL);
bListenSSL = CUtils::GetBoolInput("Listen using SSL", bListenSSL);
#endif
#ifdef HAVE_IPV6
b6 = CUtils::GetBoolInput("Listen using both IPv4 and IPv6", b6);
b6 = CUtils::GetBoolInput("Listen using both IPv4 and IPv6", b6);
#endif
// Don't ask for listen host, it may be configured later if needed.
// Don't ask for listen host, it may be configured later if needed.
CUtils::PrintAction("Verifying the listener");
CListener* pListener = new CListener(
(unsigned short int)uListenPort, sListenHost, sURIPrefix,
bListenSSL, b6 ? ADDR_ALL : ADDR_IPV4ONLY, CListener::ACCEPT_ALL);
if (!pListener->Listen()) {
CUtils::PrintStatus(false, FormatBindError());
bSuccess = false;
} else
CUtils::PrintStatus(true);
delete pListener;
} while (!bSuccess);
CUtils::PrintAction("Verifying the listener");
CListener* pListener = new CTCPListener(
(unsigned short int)uListenPort, sListenHost, sURIPrefix,
bListenSSL, b6 ? ADDR_ALL : ADDR_IPV4ONLY,
CListener::ACCEPT_ALL);
if (!pListener->Listen()) {
CUtils::PrintStatus(false, FormatBindError());
bSuccess = false;
} else
CUtils::PrintStatus(true);
delete pListener;
} while (!bSuccess);
}
#ifdef HAVE_LIBSSL
CString sPemFile = GetPemLocation();
@@ -700,9 +687,13 @@ bool CZNC::WriteNewConfig(const CString& sConfigFile) {
#endif
vsLines.push_back("<Listener l>");
vsLines.push_back("\tPort = " + CString(uListenPort));
vsLines.push_back("\tIPv4 = true");
vsLines.push_back("\tIPv6 = " + CString(b6));
if (szListenUnixSocket) {
vsLines.push_back("\tPath = " + CString(szListenUnixSocket));
} else {
vsLines.push_back("\tPort = " + CString(uListenPort));
vsLines.push_back("\tIPv4 = true");
vsLines.push_back("\tIPv6 = " + CString(b6));
}
vsLines.push_back("\tSSL = " + CString(bListenSSL));
if (!sListenHost.empty()) {
vsLines.push_back("\tHost = " + sListenHost);
@@ -809,12 +800,16 @@ bool CZNC::WriteNewConfig(const CString& sConfigFile) {
bSSL = CUtils::GetBoolInput("Server uses SSL?", bSSL);
#endif
while (!CUtils::GetNumInput("Server port", uServerPort, 1, 65535,
bSSL ? 6697 : 6667))
;
bSSL ? 6697 : 6667));
CUtils::GetInput("Server password (probably empty)", sPass);
vsLines.push_back("\t\tServer = " + sHost + ((bSSL) ? " +" : " ") +
CString(uServerPort) + " " + sPass);
if (sHost.StartsWith("unix:")) {
vsLines.push_back("\t\tServer = " + sHost + " " + sPass);
} else {
vsLines.push_back("\t\tServer = " + sHost +
((bSSL) ? " +" : " ") + CString(uServerPort) +
" " + sPass);
}
CString sChans;
if (CUtils::GetInput("Initial channels", sChans)) {
@@ -1586,9 +1581,21 @@ bool CZNC::AddUser(CUser* pUser, CString& sErrorRet, bool bStartup) {
CListener* CZNC::FindListener(u_short uPort, const CString& sBindHost,
EAddrType eAddr) {
for (CListener* pListener : m_vpListeners) {
if (pListener->GetPort() != uPort) continue;
if (pListener->GetBindHost() != sBindHost) continue;
if (pListener->GetAddrType() != eAddr) continue;
CTCPListener* pTCPListener = dynamic_cast<CTCPListener*>(pListener);
if (!pTCPListener) continue;
if (pTCPListener->GetPort() != uPort) continue;
if (pTCPListener->GetBindHost() != sBindHost) continue;
if (pTCPListener->GetAddrType() != eAddr) continue;
return pListener;
}
return nullptr;
}
CListener* CZNC::FindUnixListener(const CString& sPath) {
for (CListener* pListener : m_vpListeners) {
CUnixListener* pUnixListener = dynamic_cast<CUnixListener*>(pListener);
if (!pUnixListener) continue;
if (pUnixListener->GetPath() != sPath) continue;
return pListener;
}
return nullptr;
@@ -1639,8 +1646,37 @@ bool CZNC::AddListener(const CString& sLine, CString& sError) {
sError);
}
bool CZNC::AddListener(unsigned short uPort, const CString& sBindHost,
const CString& sURIPrefixRaw, bool bSSL, EAddrType eAddr,
bool CZNC::CheckSslAndPemFile(bool bSSL, CString& sError) {
#ifndef HAVE_LIBSSL
if (bSSL) {
sError = t_s("SSL is not enabled");
CUtils::PrintStatus(false, sError);
return false;
}
#else
CString sPemFile = GetPemLocation();
if (bSSL && !CFile::Exists(sPemFile)) {
sError = t_f("Unable to locate pem file: {1}")(sPemFile);
CUtils::PrintStatus(false, sError);
// If stdin is e.g. /dev/null and we call GetBoolInput(),
// we are stuck in an endless loop!
if (isatty(0) &&
CUtils::GetBoolInput("Would you like to create a new pem file?",
true)) {
sError.clear();
WritePemFile();
} else {
return false;
}
}
#endif
return true;
}
bool CZNC::AddTCPListener(unsigned short uPort, const CString& sBindHost,
const CString& sURIPrefix, bool bSSL, EAddrType eAddr,
CListener::EAcceptType eAccept, CString& sError) {
CString sHostComment;
@@ -1672,54 +1708,32 @@ bool CZNC::AddListener(unsigned short uPort, const CString& sBindHost,
}
#endif
#ifndef HAVE_LIBSSL
if (bSSL) {
sError = t_s("SSL is not enabled");
CUtils::PrintStatus(false, sError);
return false;
}
#else
CString sPemFile = GetPemLocation();
if (!CheckSslAndPemFile(bSSL, sError)) return false;
if (bSSL && !CFile::Exists(sPemFile)) {
sError = t_f("Unable to locate pem file: {1}")(sPemFile);
CUtils::PrintStatus(false, sError);
// If stdin is e.g. /dev/null and we call GetBoolInput(),
// we are stuck in an endless loop!
if (isatty(0) &&
CUtils::GetBoolInput("Would you like to create a new pem file?",
true)) {
sError.clear();
WritePemFile();
} else {
return false;
}
CUtils::PrintAction("Binding to port [+" + CString(uPort) + "]" +
sHostComment + sIPV6Comment);
}
#endif
if (!uPort) {
sError = t_s("Invalid port");
CUtils::PrintStatus(false, sError);
return false;
}
// URIPrefix must start with a slash and end without one.
CString sURIPrefix = CString(sURIPrefixRaw);
if (!sURIPrefix.empty()) {
if (!sURIPrefix.StartsWith("/")) {
sURIPrefix = "/" + sURIPrefix;
}
if (sURIPrefix.EndsWith("/")) {
sURIPrefix.TrimRight("/");
}
}
CListener* pListener =
new CTCPListener(uPort, sBindHost, sURIPrefix, bSSL, eAddr, eAccept);
return FinishAddingListener(pListener, sError);
}
bool CZNC::AddUnixListener(const CString& sPath, const CString& sURIPrefix,
bool bSSL, CListener::EAcceptType eAccept,
CString& sError) {
CUtils::PrintAction("Binding to path [" + sPath + "]" + (bSSL ? " with SSL" : ""));
if (!CheckSslAndPemFile(bSSL, sError)) return false;
CListener* pListener =
new CListener(uPort, sBindHost, sURIPrefix, bSSL, eAddr, eAccept);
new CUnixListener(sPath, sURIPrefix, bSSL, eAccept);
return FinishAddingListener(pListener, sError);
}
bool CZNC::FinishAddingListener(CListener* pListener, CString& sError) {
if (!pListener->Listen()) {
sError = FormatBindError();
CUtils::PrintStatus(false, sError);
@@ -1736,6 +1750,7 @@ bool CZNC::AddListener(unsigned short uPort, const CString& sBindHost,
bool CZNC::AddListener(CConfig* pConfig, CString& sError) {
CString sBindHost;
CString sURIPrefix;
CString sPath;
bool bSSL;
bool b4;
#ifdef HAVE_IPV6
@@ -1746,32 +1761,22 @@ bool CZNC::AddListener(CConfig* pConfig, CString& sError) {
bool bIRC;
bool bWeb;
unsigned short uPort;
bool bTcpListener = true;
if (!pConfig->FindUShortEntry("port", uPort)) {
sError = "No port given";
CUtils::PrintError(sError);
return false;
bTcpListener = false;
if (!pConfig->FindStringEntry("path", sPath)) {
sError = "No port and no path given";
CUtils::PrintError(sError);
return false;
}
}
pConfig->FindStringEntry("host", sBindHost);
pConfig->FindBoolEntry("ssl", bSSL, false);
pConfig->FindBoolEntry("ipv4", b4, true);
pConfig->FindBoolEntry("ipv6", b6, b6);
pConfig->FindBoolEntry("allowirc", bIRC, true);
pConfig->FindBoolEntry("allowweb", bWeb, true);
pConfig->FindStringEntry("uriprefix", sURIPrefix);
EAddrType eAddr;
if (b4 && b6) {
eAddr = ADDR_ALL;
} else if (b4 && !b6) {
eAddr = ADDR_IPV4ONLY;
} else if (!b4 && b6) {
eAddr = ADDR_IPV6ONLY;
} else {
sError = "No address family given";
CUtils::PrintError(sError);
return false;
}
CListener::EAcceptType eAccept;
if (bIRC && bWeb) {
eAccept = CListener::ACCEPT_ALL;
@@ -1785,8 +1790,38 @@ bool CZNC::AddListener(CConfig* pConfig, CString& sError) {
return false;
}
return AddListener(uPort, sBindHost, sURIPrefix, bSSL, eAddr, eAccept,
sError);
// URIPrefix must start with a slash and end without one.
if (!sURIPrefix.empty()) {
if (!sURIPrefix.StartsWith("/")) {
sURIPrefix = "/" + sURIPrefix;
}
if (sURIPrefix.EndsWith("/")) {
sURIPrefix.TrimRight("/");
}
}
if (bTcpListener) {
pConfig->FindStringEntry("host", sBindHost);
pConfig->FindBoolEntry("ipv4", b4, true);
pConfig->FindBoolEntry("ipv6", b6, b6);
EAddrType eAddr;
if (b4 && b6) {
eAddr = ADDR_ALL;
} else if (b4 && !b6) {
eAddr = ADDR_IPV4ONLY;
} else if (!b4 && b6) {
eAddr = ADDR_IPV6ONLY;
} else {
sError = "No address family given";
CUtils::PrintError(sError);
return false;
}
return AddTCPListener(uPort, sBindHost, sURIPrefix, bSSL, eAddr,
eAccept, sError);
}
return AddUnixListener(sPath, sURIPrefix, bSSL, eAccept, sError);
}
bool CZNC::AddListener(CListener* pListener) {
+1
View File
@@ -102,5 +102,6 @@ add_custom_target(inttest COMMAND
# znc-buildmod should use the correct compiler.
# https://bugs.gentoo.org/699258 is an example of how it can go wrong.
${CMAKE_COMMAND} -E env MAKEFLAGS= CXX=${CMAKE_CXX_COMPILER}
"${PROJECT_SOURCE_DIR}/third_party/gtest-parallel/gtest-parallel"
"${CMAKE_CURRENT_BINARY_DIR}/integration/inttest")
add_dependencies(inttest inttest_bin)
+7
View File
@@ -56,3 +56,10 @@ target_include_directories(inttest PUBLIC
"${GMOCK_ROOT}" "${GMOCK_ROOT}/include")
target_compile_definitions(inttest PRIVATE
"ZNC_BIN_DIR=\"${ZNC_BIN_DIR}\"")
if(CYGWIN)
# This workaround contains a sizeable modified copypaste of Qt's qlocalsocket_unix.cpp, which is LGPL, so has to be in a separate shared library.
add_library(inttest_cygwin SHARED framework/cygwin.cpp)
target_link_libraries(inttest_cygwin Qt${ZNC_QT_VER}::NetworkPrivate)
target_link_libraries(inttest inttest_cygwin)
endif()
+10 -1
View File
@@ -14,9 +14,12 @@
* limitations under the License.
*/
#include <gmock/gmock.h>
#include "base.h"
#include <gmock/gmock.h>
#include <QTcpServer>
using testing::AnyOf;
using testing::Eq;
@@ -58,4 +61,10 @@ Process::~Process() {
}
}
int PickPortNumber() {
QTcpServer tcp;
tcp.listen(QHostAddress::LocalHost);
return tcp.serverPort();
}
} // namespace znc_inttest
+42 -6
View File
@@ -20,11 +20,11 @@
#include <QCoreApplication>
#include <QDateTime>
#include <QLocalSocket>
#include <QProcess>
#include <QRegularExpression>
#include <QTcpSocket>
#include <memory>
namespace znc_inttest {
template <typename Device>
@@ -34,6 +34,7 @@ class IO {
: m_device(device), m_verbose(verbose) {}
virtual ~IO() {}
void ReadUntil(QByteArray pattern);
void ReadUntilRe(QString pattern);
/*
* Reads from Device until pattern is matched and returns this pattern
* up to and excluding the first newline. Pattern itself can contain a newline.
@@ -49,6 +50,7 @@ class IO {
// Need to flush QTcpSocket, and QIODevice doesn't have flush at all...
static void FlushIfCan(QIODevice*) {}
static void FlushIfCan(QTcpSocket* sock) { sock->flush(); }
static void FlushIfCan(QLocalSocket* sock) { sock->flush(); }
Device* m_device;
bool m_verbose;
@@ -60,7 +62,7 @@ IO<Device> WrapIO(Device* d) {
return IO<Device>(d);
}
using Socket = IO<QTcpSocket>;
using Socket = IO<QLocalSocket>;
class Process : public IO<QProcess> {
public:
@@ -114,7 +116,36 @@ void IO<Device>::ReadUntil(QByteArray pattern) {
}
const int timeout_ms =
QDateTime::currentDateTime().msecsTo(deadline);
ASSERT_GT(timeout_ms, 0) << "Wanted:" << pattern.toStdString();
ASSERT_GT(timeout_ms, 0) << "Wanted: " << pattern.toStdString();
ASSERT_TRUE(m_device->waitForReadyRead(timeout_ms))
<< "Wanted: " << pattern.toStdString();
QByteArray chunk = m_device->readAll();
if (m_verbose) {
std::cout << chunk.toStdString() << std::flush;
}
m_readed += chunk;
}
}
template <typename Device>
void IO<Device>::ReadUntilRe(QString pattern) {
QRegularExpression expr(pattern);
auto deadline = QDateTime::currentDateTime().addSecs(60);
while (true) {
QRegularExpressionMatch match =
expr.match(QString::fromUtf8(m_readed), 0,
QRegularExpression::PartialPreferCompleteMatch);
if (match.hasMatch()) {
m_readed.remove(0, match.capturedEnd());
return;
}
if (!match.hasPartialMatch()) {
m_readed.clear();
}
const int timeout_ms =
QDateTime::currentDateTime().msecsTo(deadline);
ASSERT_GT(timeout_ms, 0)
<< "Wanted: " << pattern.toStdString();
ASSERT_TRUE(m_device->waitForReadyRead(timeout_ms))
<< "Wanted: " << pattern.toStdString();
QByteArray chunk = m_device->readAll();
@@ -156,7 +187,7 @@ void IO<Device>::ReadUntilAndGet(QByteArray pattern, QByteArray& match) {
}
const int timeout_ms =
QDateTime::currentDateTime().msecsTo(deadline);
ASSERT_GT(timeout_ms, 0) << "Wanted:" << pattern.toStdString();
ASSERT_GT(timeout_ms, 0) << "Wanted: " << pattern.toStdString();
ASSERT_TRUE(m_device->waitForReadyRead(timeout_ms))
<< "Wanted: " << pattern.toStdString();
QByteArray chunk = m_device->readAll();
@@ -202,6 +233,9 @@ void IO<Device>::Write(QByteArray s, bool new_line) {
FlushIfCan(m_device);
}
inline void DisconnectFromServer(QTcpSocket* s) { s->disconnectFromHost(); }
inline void DisconnectFromServer(QLocalSocket* s) { s->disconnectFromServer(); }
template <typename Device>
void IO<Device>::Close() {
#ifdef __CYGWIN__
@@ -209,8 +243,10 @@ void IO<Device>::Close() {
// without this line
sleep(1);
#endif
m_device->disconnectFromHost();
DisconnectFromServer(m_device);
}
int PickPortNumber();
} // namespace znc_inttest
+107
View File
@@ -0,0 +1,107 @@
// This file is LGPL, as it's based on Qt's qlocalsocket_unix.cpp
// The original header follows:
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtNetwork module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 3 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL3 included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 3 requirements
** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 2.0 or (at your option) the GNU General
** Public license version 3 or any later version approved by the KDE Free
** Qt Foundation. The licenses are as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-2.0.html and
** https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include "cygwin.h"
#include <fcntl.h>
#include <unistd.h>
#include <sys/un.h>
#include <qglobal.h>
#undef QT_THREADSAFE_CLOEXEC
#include <QFile>
#include <QtNetwork/private/qlocalsocket_p.h>
#include <QtNetwork/private/qnet_unix_p.h>
namespace znc_inttest_cygwin {
// https://stackoverflow.com/questions/424104/can-i-access-private-members-from-outside-the-class-without-using-friends
template <typename Tag, typename Tag::type M>
struct Rob {
friend typename Tag::type get(Tag) { return M; }
};
struct A_member {
using type = QScopedPointer<QObjectData> QObject::*;
friend type get(A_member);
};
template struct Rob<A_member, &QObject::d_ptr>;
// This function is inspired by QLocalSocket::connectToServer() and QLocalSocketPrivate::_q_connectToSocket()
void CygwinWorkaroundLocalConnect(QLocalSocket& sock) {
QObjectData* o = (sock.*get(A_member())).data();
QLocalSocketPrivate* d = reinterpret_cast<QLocalSocketPrivate*>(o);
d->unixSocket.setSocketState(QAbstractSocket::ConnectingState);
d->state = QLocalSocket::ConnectingState;
sock.stateChanged(d->state);
if ((d->connectingSocket = qt_safe_socket(PF_UNIX, SOCK_STREAM, 0, 0)) ==
-1) {
qDebug() << "CygwinWorkaroundLocalConnect: qt_safe_socket errored";
return;
}
d->fullServerName = d->serverName;
const QByteArray encodedConnectingPathName =
QFile::encodeName(d->serverName);
struct sockaddr_un name;
name.sun_family = PF_UNIX;
::memcpy(name.sun_path, encodedConnectingPathName.constData(),
encodedConnectingPathName.size() + 1);
if (qt_safe_connect(d->connectingSocket, (struct sockaddr*)&name,
sizeof(name)) == -1) {
qDebug() << "CygwinWorkaroundLocalConnect: qt_safe_connect errored";
return;
}
::fcntl(d->connectingSocket, F_SETFL,
::fcntl(d->connectingSocket, F_GETFL) | O_NONBLOCK);
if (!d->unixSocket.setSocketDescriptor(d->connectingSocket,
QAbstractSocket::ConnectedState)) {
qDebug() << "CygwinWorkaroundLocalConnect: setSocketDescriptor errored";
return;
}
sock.QIODevice::open(QLocalSocket::ReadWrite | QLocalSocket::Unbuffered);
sock.connected();
d->connectingSocket = -1;
d->connectingName.clear();
}
} // namespace znc_inttest_cygwin
+11
View File
@@ -0,0 +1,11 @@
#pragma once
#include <QLocalSocket>
namespace znc_inttest_cygwin {
// Qt uses non-blocking sockets for unix sockets, but cygwin emulates them via
// AF_INET sockets, so connect() fails. This function connects the socket by
// reaching into private parts of QLocalSocket, and sets it non-blocking only
// after connect().
void CygwinWorkaroundLocalConnect(QLocalSocket& sock);
}
+19 -10
View File
@@ -15,7 +15,9 @@
*/
#include <QRegularExpression>
#include <QProcess>
#include "znctest.h"
#include "cygwin.h"
#ifndef ZNC_BIN_DIR
#define ZNC_BIN_DIR ""
@@ -24,13 +26,15 @@
namespace znc_inttest {
void WriteConfig(QString path) {
Process p(ZNC_BIN_DIR "/znc",
QStringList() << "--debug" << "--datadir" << path << "--makeconf",
[=](QProcess* p) {
auto env = p->processEnvironment();
env.insert("ZNC_LISTEN_UNIX_SOCKET",
path + "/inttest.znc");
p->setProcessEnvironment(env);
});
// clang-format off
Process p(ZNC_BIN_DIR "/znc", QStringList() << "--debug"
<< "--datadir" << path
<< "--makeconf");
p.ReadUntil("Listen on port"); p.Write("12345");
p.ReadUntil("Listen using SSL"); p.Write();
p.ReadUntil("IPv6"); p.Write();
p.ReadUntil("Username"); p.Write("user");
p.ReadUntil("password"); p.Write("hunter2", false);
p.ReadUntil("Confirm"); p.Write("hunter2", false);
@@ -41,7 +45,7 @@ void WriteConfig(QString path) {
p.ReadUntil("Bind host"); p.Write();
p.ReadUntil("Set up a network?"); p.Write();
p.ReadUntil("Name [libera]"); p.Write("test");
p.ReadUntil("Server host (host only)"); p.Write("127.0.0.1");
p.ReadUntil("Server host (host only)"); p.Write("unix:" + path.toUtf8() + "/inttest.ircd");
p.ReadUntil("Server uses SSL?"); p.Write();
p.ReadUntil("6667"); p.Write();
p.ReadUntil("password"); p.Write();
@@ -61,7 +65,7 @@ void WriteConfig(QString path) {
void ZNCTest::SetUp() {
WriteConfig(m_dir.path());
ASSERT_TRUE(m_server.listen(QHostAddress::LocalHost, 6667))
ASSERT_TRUE(m_server.listen(m_dir.path() + "/inttest.ircd"))
<< m_server.errorString().toStdString();
}
@@ -72,8 +76,13 @@ Socket ZNCTest::ConnectIRCd() {
Socket ZNCTest::ConnectClient() {
m_clients.emplace_back();
QTcpSocket& sock = m_clients.back();
sock.connectToHost("127.0.0.1", 12345);
QLocalSocket& sock = m_clients.back();
sock.setServerName(m_dir.path() + "/inttest.znc");
#ifdef __CYGWIN__
znc_inttest_cygwin::CygwinWorkaroundLocalConnect(sock);
#else
sock.connectToServer();
#endif
[&] {
ASSERT_TRUE(sock.waitForConnected())
<< sock.errorString().toStdString();
+6 -5
View File
@@ -16,17 +16,18 @@
#pragma once
#include "base.h"
#include <QLocalServer>
#include <QNetworkAccessManager>
#include <QNetworkRequest>
#include <QNetworkReply>
#include <QTcpServer>
#include <QNetworkRequest>
#include <QTemporaryDir>
#include <QTextStream>
#include <QTimer>
#include <QUrl>
#include <QUrlQuery>
#include "base.h"
namespace znc_inttest {
void WriteConfig(QString path);
@@ -52,8 +53,8 @@ class ZNCTest : public testing::Test {
App m_app;
QNetworkAccessManager m_network;
QTemporaryDir m_dir;
QTcpServer m_server;
std::list<QTcpSocket> m_clients;
QLocalServer m_server;
std::list<QLocalSocket> m_clients;
};
} // namespace znc_inttest
+29 -7
View File
@@ -90,7 +90,14 @@ TEST_F(ZNCTest, Channel) {
TEST_F(ZNCTest, HTTP) {
auto znc = Run();
auto ircd = ConnectIRCd();
auto reply = HttpGet(QNetworkRequest(QUrl("http://127.0.0.1:12345/")));
auto client = LoginClient();
int port = PickPortNumber();
client.Write(QStringLiteral("znc addport %1 all all").arg(port).toUtf8());
client.ReadUntil(":Port added");
auto reply = HttpGet(QNetworkRequest(
QUrl(QStringLiteral("http://127.0.0.1:%1/").arg(port))));
EXPECT_THAT(reply->rawHeader("Server").toStdString(), HasSubstr("ZNC"));
}
@@ -102,10 +109,17 @@ TEST_F(ZNCTest, FixCVE20149403) {
ircd.Write(":server PING :1");
ircd.ReadUntil("PONG 1");
auto client = LoginClient();
int port = PickPortNumber();
client.Write(QStringLiteral("znc addport %1 all all").arg(port).toUtf8());
client.ReadUntil(":Port added");
QNetworkRequest request;
request.setRawHeader("Authorization",
"Basic " + QByteArray("user:hunter2").toBase64());
request.setUrl(QUrl("http://127.0.0.1:12345/mods/global/webadmin/addchan"));
request.setUrl(
QUrl(QStringLiteral("http://127.0.0.1:%1/mods/global/webadmin/addchan")
.arg(port)));
HttpPost(request, {
{"user", "user"},
{"network", "test"},
@@ -136,10 +150,18 @@ TEST_F(ZNCTest, FixFixOfCVE20149403) {
ircd.Write(":server PING :12345");
ircd.ReadUntil("PONG 12345");
auto client = LoginClient();
int port = PickPortNumber();
client.Write(QStringLiteral("znc addport %1 all all").arg(port).toUtf8());
client.ReadUntil(":Port added");
QNetworkRequest request;
request.setRawHeader("Authorization",
"Basic " + QByteArray("user:hunter2").toBase64());
request.setUrl(QUrl("http://127.0.0.1:12345/mods/global/webadmin/addchan"));
request.setUrl(
QUrl(QStringLiteral("http://127.0.0.1:%1/mods/global/webadmin/addchan")
.arg(port)
.toUtf8()));
auto reply = HttpPost(request, {
{"user", "user"},
{"network", "test"},
@@ -968,13 +990,13 @@ TEST_F(ZNCTest, SpacedServerPassword) {
auto znc = Run();
auto ircd = ConnectIRCd();
auto client = LoginClient();
client.Write("znc delserver 127.0.0.1");
client.Write("znc addserver 127.0.0.1 6667 a b");
client.Write(("znc delserver unix:" + m_dir.path() + "/inttest.ircd").toUtf8());
client.Write(("znc addserver unix:" + m_dir.path() + "/inttest.ircd a b").toUtf8());
client.Write("znc jump");
auto ircd2 = ConnectIRCd();
ircd2.ReadUntil("PASS :a b");
client.Write("znc delserver 127.0.0.1");
client.Write("znc addserver 127.0.0.1 6667 a");
client.Write(("znc delserver unix:" + m_dir.path() + "/inttest.ircd").toUtf8());
client.Write(("znc addserver unix:" + m_dir.path() + "/inttest.ircd a").toUtf8());
client.Write("znc jump");
auto ircd3 = ConnectIRCd();
// No :
+38 -22
View File
@@ -20,6 +20,7 @@
#include "znctest.h"
#include <QSslSocket>
#include <QTcpServer>
using testing::HasSubstr;
using testing::Not;
@@ -38,23 +39,23 @@ TEST_F(ZNCTest, NotifyConnectModule) {
client2.Write("PASS :hunter2");
client2.Write("NICK nick");
client2.Write("USER user/test x x :x");
client.ReadUntil("NOTICE nick :*** user attached from 127.0.0.1");
client.ReadUntil("NOTICE nick :*** user attached from ");
auto client3 = ConnectClient();
client3.Write("PASS :hunter2");
client3.Write("NICK nick");
client3.Write("USER user@identifier/test x x :x");
client.ReadUntil(
"NOTICE nick :*** user@identifier attached from 127.0.0.1");
"NOTICE nick :*** user@identifier attached from ");
client2.ReadUntil(
"NOTICE nick :*** user@identifier attached from 127.0.0.1");
"NOTICE nick :*** user@identifier attached from ");
client2.Write("QUIT");
client.ReadUntil("NOTICE nick :*** user detached from 127.0.0.1");
client.ReadUntil("NOTICE nick :*** user detached from ");
client3.Close();
client.ReadUntil(
"NOTICE nick :*** user@identifier detached from 127.0.0.1");
"NOTICE nick :*** user@identifier detached from ");
}
TEST_F(ZNCTest, ClientNotifyModule) {
@@ -64,41 +65,46 @@ TEST_F(ZNCTest, ClientNotifyModule) {
client.Write("znc loadmod clientnotify");
client.ReadUntil("Loaded module");
auto check_not_sent = [](Socket& client, std::string wrongAnswer){
auto result = QString{client.ReadRemainder()}.toStdString();
EXPECT_THAT(result, Not(HasSubstr((wrongAnswer)))) << "Got an answer from the ClientNotifyModule even though we didnt want one with the given configuration";
auto check_not_sent = [](Socket& client, QString wrongAnswer) {
QString result = QString::fromUtf8(client.ReadRemainder());
QRegularExpression expr(wrongAnswer);
QRegularExpressionMatch match = expr.match(result);
EXPECT_FALSE(match.hasMatch())
<< "Got an answer from the ClientNotifyModule even though we didnt "
"want one with the given configuration: "
<< wrongAnswer.toStdString() << result.toStdString();
};
auto client2 = LoginClient();
client.ReadUntil(":Another client (127.0.0.1) authenticated as your user. Use the 'ListClients' command to see all 2 clients.");
client.ReadUntilRe(R"(:Another client \((localhost)?\) authenticated as your user. Use the 'ListClients' command to see all 2 clients.)");
auto client3 = LoginClient();
client.ReadUntil(":Another client (127.0.0.1) authenticated as your user. Use the 'ListClients' command to see all 3 clients.");
client.ReadUntilRe(R"(:Another client \((localhost)?\) authenticated as your user. Use the 'ListClients' command to see all 3 clients.)");
// disable notifications for every message
client.Write("PRIVMSG *clientnotify :NewOnly on");
// check that we do not ge a notification after connecting from a know ip
auto client4 = LoginClient();
check_not_sent(client, ":Another client (127.0.0.1) authenticated as your user. Use the 'ListClients' command to see all 4 clients.");
check_not_sent(client, ":Another client (.*) authenticated as your user. Use the 'ListClients' command to see all 4 clients.");
// choose to notify only on new client ids
client.Write("PRIVMSG *clientnotify :NotifyOnNewID on");
auto client5 = LoginClient("identifier123");
client.ReadUntil(":Another client (127.0.0.1 / identifier123) authenticated as your user. Use the 'ListClients' command to see all 5 clients.");
client.ReadUntilRe(R"(:Another client \((localhost)? / identifier123\) authenticated as your user. Use the 'ListClients' command to see all 5 clients.)");
auto client6 = LoginClient("identifier123");
check_not_sent(client, ":Another client (127.0.0.1 / identifier123) authenticated as your user. Use the 'ListClients' command to see all 6 clients.");
check_not_sent(client, ":Another client (.* / identifier123) authenticated as your user. Use the 'ListClients' command to see all 6 clients.");
auto client7 = LoginClient("not_identifier123");
client.ReadUntil(":Another client (127.0.0.1 / not_identifier123) authenticated as your user. Use the 'ListClients' command to see all 7 clients.");
client.ReadUntilRe(R"(:Another client \((localhost)? / not_identifier123\) authenticated as your user. Use the 'ListClients' command to see all 7 clients.)");
// choose to notify from both clientids and new IPs
client.Write("PRIVMSG *clientnotify :NotifyOnNewIP on");
auto client8 = LoginClient();
check_not_sent(client, ":Another client (127.0.0.1 / identifier123) authenticated as your user. Use the 'ListClients' command to see all 8 clients.");
check_not_sent(client, ":Another client (.* / identifier123) authenticated as your user. Use the 'ListClients' command to see all 8 clients.");
auto client9 = LoginClient("definitely_not_identifier123");
client.ReadUntil(":Another client (127.0.0.1 / definitely_not_identifier123) authenticated as your user. Use the 'ListClients' command to see all 9 clients.");
client.ReadUntilRe(R"(:Another client \((localhost)? / definitely_not_identifier123\) authenticated as your user. Use the 'ListClients' command to see all 9 clients.)");
}
TEST_F(ZNCTest, ShellModule) {
@@ -241,13 +247,18 @@ TEST_F(ZNCTest, KeepNickModule) {
}
TEST_F(ZNCTest, ModuleCSRFOverride) {
// TODO: Qt 6.8 introduced QNetworkRequest::FullLocalServerNameAttribute to
// let it connect to unix socket
int port = PickPortNumber();
auto znc = Run();
auto ircd = ConnectIRCd();
auto client = LoginClient();
client.Write(QStringLiteral("znc addport %1 all all").arg(port).toUtf8());
client.Write("znc loadmod samplewebapi");
client.ReadUntil("Loaded module");
auto request = QNetworkRequest(
QUrl("http://127.0.0.1:12345/mods/global/samplewebapi/"));
QUrl(QStringLiteral("http://127.0.0.1:%1/mods/global/samplewebapi/")
.arg(port)));
auto reply =
HttpPost(request, {{"text", "ipsum"}})->readAll().toStdString();
EXPECT_THAT(reply, HasSubstr("ipsum"));
@@ -352,9 +363,12 @@ TEST_F(ZNCTest, SaslAuthPlainImapAuth) {
auto znc = Run();
auto ircd = ConnectIRCd();
QTcpServer imap;
ASSERT_TRUE(imap.listen(QHostAddress::LocalHost, 12346)) << imap.errorString().toStdString();
ASSERT_TRUE(imap.listen(QHostAddress::LocalHost)) << imap.errorString().toStdString();
auto client = LoginClient();
client.Write("znc loadmod imapauth 127.0.0.1 12346 %@mail.test.com");
client.Write(
QStringLiteral("znc loadmod imapauth 127.0.0.1 %1 %@mail.test.com")
.arg(imap.serverPort())
.toUtf8());
client.ReadUntil("Loaded");
auto client2 = ConnectClient();
@@ -375,11 +389,13 @@ TEST_F(ZNCTest, SaslAuthPlainImapAuth) {
}
TEST_F(ZNCTest, SaslAuthExternal) {
int port = PickPortNumber();
auto znc = Run();
auto ircd = ConnectIRCd();
ircd.Write(":server 001 nick :Hello");
auto client = LoginClient();
client.Write("znc addport +12346 all all");
client.Write(QStringLiteral("znc addport +%1 all all").arg(port).toUtf8());
client.ReadUntil(":Port added");
client.Write("znc loadmod certauth");
client.ReadUntil("Loaded");
@@ -390,7 +406,7 @@ TEST_F(ZNCTest, SaslAuthExternal) {
sock.setLocalCertificate(m_dir.path() + "/znc.pem");
sock.setPrivateKey(m_dir.path() + "/znc.pem");
sock.setPeerVerifyMode(QSslSocket::VerifyNone);
sock.connectToHostEncrypted("127.0.0.1", 12346);
sock.connectToHostEncrypted("127.0.0.1", port);
ASSERT_TRUE(sock.waitForConnected()) << sock.errorString().toStdString();
ASSERT_TRUE(sock.waitForEncrypted()) << sock.errorString().toStdString();
auto client2 = WrapIO(&sock);
@@ -404,7 +420,7 @@ TEST_F(ZNCTest, SaslAuthExternal) {
client2.Close();
ASSERT_TRUE(sock.state() == QAbstractSocket::UnconnectedState || sock.waitForDisconnected())
<< sock.errorString().toStdString();
sock.connectToHostEncrypted("127.0.0.1", 12346);
sock.connectToHostEncrypted("127.0.0.1", port);
ASSERT_TRUE(sock.waitForConnected())
<< sock.errorString().toStdString();
ASSERT_TRUE(sock.waitForEncrypted())
+122 -6
View File
@@ -156,6 +156,121 @@ TEST_F(ZNCTest, ModperlSocket) {
client.ReadUntil("received 4 bytes");
}
TEST_F(ZNCTest, ModpythonUnixSocket) {
#ifndef WANT_PYTHON
GTEST_SKIP() << "Modpython is disabled";
#endif
#ifdef __CYGWIN__
GTEST_SKIP() << "Bug to fix: https://github.com/znc/znc/issues/1947";
#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.TestSockPath())
def OnModCommand(self, cmd):
sock = self.CreateSocket()
sock.ConnectUnix(self.TestSockPath())
sock.WriteBytes(b'blah')
def TestSockPath(self):
path = self.GetSavePath() + "/sock"
# https://unix.stackexchange.com/questions/367008/why-is-socket-path-length-limited-to-a-hundred-chars
if len(path) < 100:
return path
return "./testsock.modpython"
)");
auto ircd = ConnectIRCd();
auto client = LoginClient();
client.Write("znc loadmod modpython");
client.Write("znc loadmod socktest");
client.ReadUntil("Loaded module socktest");
client.Write("PRIVMSG *socktest :foo");
client.ReadUntil("received 4 bytes");
}
TEST_F(ZNCTest, ModperlUnixSocket) {
#ifndef WANT_PERL
GTEST_SKIP() << "Modperl is disabled";
#endif
#ifdef __CYGWIN__
GTEST_SKIP() << "Bug to fix: https://github.com/znc/znc/issues/1947";
#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->TestSockPath);
}
sub OnModCommand {
my ($self, $cmd) = @_;
my $sock = $self->CreateSocket('socktest::conn');
$sock->ConnectUnix($self->TestSockPath);
$sock->Write('blah');
}
sub TestSockPath {
my $self = shift;
my $path = $self->GetSavePath . "/sock";
# https://unix.stackexchange.com/questions/367008/why-is-socket-path-length-limited-to-a-hundred-chars
if (length($path) < 100) {
return $path;
}
return "./testsock.modperl";
}
1;
)");
auto ircd = ConnectIRCd();
auto client = LoginClient();
client.Write("znc loadmod modperl");
client.Write("znc loadmod socktest");
client.ReadUntil("Loaded module socktest");
client.Write("PRIVMSG *socktest :foo");
client.ReadUntil("received 4 bytes");
}
TEST_F(ZNCTest, ModpythonVCString) {
#ifndef WANT_PYTHON
GTEST_SKIP() << "Modpython is disabled";
@@ -176,6 +291,7 @@ TEST_F(ZNCTest, ModpythonVCString) {
auto client = LoginClient();
client.Write("znc loadmod modpython");
client.Write("znc loadmod test");
sleep(1);
client.Write("PRIVMSG *test :foo");
client.ReadUntil("'*test', 'foo'");
}
@@ -399,9 +515,9 @@ TEST_F(ZNCTest, ModpythonSaslAuth) {
client2.Write("AUTHENTICATE FOO");
client2.ReadUntil("AUTHENTICATE " + QByteArrayLiteral("Welcome").toBase64());
client2.Write("AUTHENTICATE +");
client2.ReadUntil(
":irc.znc.in 900 nick nick!user@127.0.0.1 user :You are now logged in "
"as user");
client2.ReadUntilRe(
":irc.znc.in 900 nick nick!user@(localhost)? user :You are now logged "
"in as user");
}
TEST_F(ZNCTest, ModperlSaslAuth) {
@@ -456,9 +572,9 @@ TEST_F(ZNCTest, ModperlSaslAuth) {
client2.Write("AUTHENTICATE FOO");
client2.ReadUntil("AUTHENTICATE " + QByteArrayLiteral("Welcome").toBase64());
client2.Write("AUTHENTICATE +");
client2.ReadUntil(
":irc.znc.in 900 nick nick!user@127.0.0.1 user :You are now logged in "
"as user");
client2.ReadUntilRe(
":irc.znc.in 900 nick nick!user@(localhost)? user :You are now logged "
"in as user");
}
} // namespace
Vendored Submodule
+1