mirror of
https://github.com/znc/znc.git
synced 2026-08-07 09:22:55 +02:00
+2
-2
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
|
||||
@@ -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
@@ -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
@@ -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
|
||||
|
||||
@@ -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
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()));
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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) {
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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();
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 :
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -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
|
||||
|
||||
+1
Submodule third_party/gtest-parallel added at 96f4f90492
Reference in New Issue
Block a user