mirror of
https://github.com/znc/znc.git
synced 2026-03-28 17:42:41 +01:00
@@ -97,9 +97,7 @@ class CTCPListener : public CListener {
|
||||
class CUnixListener : public CListener {
|
||||
public:
|
||||
CUnixListener(const CString& sPath, const CString& sURIPrefix, bool bSSL,
|
||||
EAcceptType eAccept)
|
||||
: CListener(sURIPrefix, bSSL, eAccept),
|
||||
m_sPath(sPath) {}
|
||||
EAcceptType eAccept, const CString& sGid, const CString& sMode);
|
||||
~CUnixListener();
|
||||
|
||||
CUnixListener(const CUnixListener&) = delete;
|
||||
@@ -107,6 +105,8 @@ class CUnixListener : public CListener {
|
||||
|
||||
// Getters
|
||||
const CString& GetPath() const { return m_sPath; }
|
||||
const CString& GetGroup() const { return m_sGid; }
|
||||
CString GetMode() const;
|
||||
// !Getters
|
||||
|
||||
bool Listen() override;
|
||||
@@ -114,6 +114,8 @@ class CUnixListener : public CListener {
|
||||
|
||||
protected:
|
||||
CString m_sPath;
|
||||
CString m_sGid;
|
||||
int m_iMode;
|
||||
};
|
||||
|
||||
class CRealListener : public CZNCSock {
|
||||
|
||||
@@ -209,8 +209,9 @@ class CZNC : private CCoreTranslationMixin {
|
||||
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 AddUnixListener(const CString& sPath, const CString& sURIPrefix,
|
||||
bool bSSL, CListener::EAcceptType eAccept,
|
||||
const CString& sGroup, const CString& sMode, CString& sError);
|
||||
bool DelListener(CListener*);
|
||||
|
||||
// For backwards-compatibility TODO: Remove
|
||||
|
||||
@@ -21,10 +21,9 @@
|
||||
<th><? FORMAT "Delete" ?></th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<? LOOP ListenLoop ?>
|
||||
<? LOOP ListenLoop ?>
|
||||
<? IF Type == "TCP" ?>
|
||||
<tr class="<? IF __EVEN__ ?>evenrow<? ELSE ?>oddrow<? ENDIF ?>">
|
||||
<? IF Type == "TCP" ?>
|
||||
<td><? VAR Port ?></td>
|
||||
<td><? VAR BindHost DEFAULT=** ?></td>
|
||||
<td>
|
||||
@@ -33,9 +32,6 @@
|
||||
<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>
|
||||
@@ -51,14 +47,10 @@
|
||||
<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 ?>
|
||||
@@ -66,7 +58,8 @@
|
||||
<? ENDIF ?>
|
||||
</td>
|
||||
</tr>
|
||||
<? ENDLOOP ?>
|
||||
<? ENDIF ?>
|
||||
<? ENDLOOP ?>
|
||||
<tr>
|
||||
<form action="<? VAR URIPrefix TOP ?><? VAR ModPath TOP ?>add_listener" method="post">
|
||||
<? INC _csrf_check.tmpl ?>
|
||||
@@ -82,11 +75,58 @@
|
||||
<td><input type="submit" value="<? FORMAT "Add" ?>"/></td>
|
||||
</form>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="subsection">
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th><? FORMAT "Path" ?></th>
|
||||
<th><? FORMAT "Mode" ?></th>
|
||||
<th><? FORMAT "Group" ?></th>
|
||||
<th><? FORMAT "SSL" ?></th>
|
||||
<th><? FORMAT "IRC" ?></th>
|
||||
<th><? FORMAT "HTTP" ?></th>
|
||||
<th><? FORMAT "URIPrefix" ?></th>
|
||||
<th><? FORMAT "Delete" ?></th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<? LOOP ListenLoop ?>
|
||||
<? IF Type == "Unix" ?>
|
||||
<tr class="<? IF __EVEN__ ?>evenrow<? ELSE ?>oddrow<? ENDIF ?>">
|
||||
<td>unix:<? VAR Path ?></td>
|
||||
<td><? VAR Mode ?></td>
|
||||
<td><? VAR Group ?></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 IsIRC ?>checked="checked"<? ENDIF ?>/></div>
|
||||
</td>
|
||||
<td>
|
||||
<div class="checkbox"><input type="checkbox" disabled="disabled" <? IF IsHTTP ?>checked="checked"<? ENDIF ?>/></div>
|
||||
</td>
|
||||
<td><? VAR URIPrefix ?></td>
|
||||
<td>
|
||||
<form action="<? VAR URIPrefix TOP ?><? VAR ModPath TOP ?>del_listener" method="post">
|
||||
<? INC _csrf_check.tmpl ?>
|
||||
<input name="type" type="hidden" value="<? VAR Type ?>"/>
|
||||
<input name="path" type="hidden" value="<? VAR Path ?>"/>
|
||||
<input type="submit" value="<? FORMAT "Del" ?>"/>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
<? ENDIF ?>
|
||||
<? ENDLOOP ?>
|
||||
<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>unix:<input name="path" type="text" value="/" class="third"/></td>
|
||||
<td><input name="mode" type="number" min="-1" max="777" class="number third" value="-1" title="<? FORMAT "Octal number; -1 means do not chmod" ?>"/></td>
|
||||
<td><input name="group" type="text" value="" class="sixth"/></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>
|
||||
|
||||
@@ -1910,8 +1910,10 @@ class CWebAdminMod : public CModule {
|
||||
eAddr, eAccept, sMessage);
|
||||
} else {
|
||||
CString sPath = WebSock.GetParam("path");
|
||||
CString sMode = WebSock.GetParam("mode");
|
||||
CString sGroup = WebSock.GetParam("group");
|
||||
bResult = CZNC::Get().AddUnixListener(sPath, sURIPrefix, bSSL,
|
||||
eAccept, sMessage);
|
||||
eAccept, sGroup, sMode, sMessage);
|
||||
}
|
||||
|
||||
if (bResult) {
|
||||
@@ -2030,6 +2032,8 @@ class CWebAdminMod : public CModule {
|
||||
dynamic_cast<const CUnixListener*>(pListener)) {
|
||||
l["Type"] = "Unix";
|
||||
l["Path"] = pUnixListener->GetPath();
|
||||
l["Mode"] = pUnixListener->GetMode();
|
||||
l["Group"] = pUnixListener->GetGroup();
|
||||
// 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.
|
||||
|
||||
@@ -1641,29 +1641,39 @@ void CClient::UserPortCommand(CString& sLine) {
|
||||
const CString sCommand = sLine.Token(0);
|
||||
|
||||
if (sCommand.Equals("LISTPORTS")) {
|
||||
CTable Table;
|
||||
Table.AddColumn(t_s("Port", "listports"));
|
||||
Table.AddColumn(t_s("BindHost", "listports"));
|
||||
Table.AddColumn(t_s("SSL", "listports"));
|
||||
Table.AddColumn(t_s("Protocol", "listports"));
|
||||
Table.AddColumn(t_s("IRC", "listports"));
|
||||
Table.AddColumn(t_s("Web", "listports"));
|
||||
CTable TableT;
|
||||
TableT.AddColumn(t_s("Port", "listports"));
|
||||
TableT.AddColumn(t_s("BindHost", "listports"));
|
||||
TableT.AddColumn(t_s("Protocol", "listports"));
|
||||
TableT.AddColumn(t_s("SSL", "listports"));
|
||||
TableT.AddColumn(t_s("IRC", "listports"));
|
||||
TableT.AddColumn(t_s("Web", "listports"));
|
||||
|
||||
CTable TableU;
|
||||
TableU.AddColumn(t_s("Path", "listports"));
|
||||
TableU.AddColumn(t_s("Mode", "listports"));
|
||||
TableU.AddColumn(t_s("Group", "listports"));
|
||||
TableU.AddColumn(t_s("SSL", "listports"));
|
||||
TableU.AddColumn(t_s("IRC", "listports"));
|
||||
TableU.AddColumn(t_s("Web", "listports"));
|
||||
|
||||
const vector<CListener*>& vpListeners = CZNC::Get().GetListeners();
|
||||
|
||||
for (const CListener* pListener : vpListeners) {
|
||||
Table.AddRow();
|
||||
CTable* pTable;
|
||||
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()));
|
||||
TableT.AddRow();
|
||||
pTable = &TableT;
|
||||
TableT.SetCell(t_s("Port", "listports"),
|
||||
CString(pTCPListener->GetPort()));
|
||||
TableT.SetCell(t_s("BindHost", "listports"),
|
||||
(pTCPListener->GetBindHost().empty()
|
||||
? CString("*")
|
||||
: pTCPListener->GetBindHost()));
|
||||
|
||||
EAddrType eAddr = pTCPListener->GetAddrType();
|
||||
Table.SetCell(
|
||||
TableT.SetCell(
|
||||
t_s("Protocol", "listports"),
|
||||
eAddr == ADDR_ALL
|
||||
? t_s("IPv4 and IPv6", "listports")
|
||||
@@ -1671,28 +1681,37 @@ void CClient::UserPortCommand(CString& sLine) {
|
||||
: t_s("IPv6", "listports")));
|
||||
} else if (const CUnixListener* pUnixListener =
|
||||
dynamic_cast<const CUnixListener*>(pListener)) {
|
||||
Table.SetCell(t_s("Port", "listports"),
|
||||
pUnixListener->GetPath());
|
||||
TableU.AddRow();
|
||||
pTable = &TableU;
|
||||
TableU.SetCell(t_s("Path", "listports"),
|
||||
pUnixListener->GetPath());
|
||||
TableU.SetCell(t_s("Mode", "listports"),
|
||||
pUnixListener->GetMode());
|
||||
TableU.SetCell(t_s("Group", "listports"),
|
||||
pUnixListener->GetGroup());
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
Table.SetCell(t_s("SSL", "listports"),
|
||||
pListener->IsSSL() ? t_s("yes", "listports|ssl")
|
||||
: t_s("no", "listports|ssl"));
|
||||
pTable->SetCell(t_s("SSL", "listports"),
|
||||
pListener->IsSSL() ? t_s("yes", "listports|ssl")
|
||||
: t_s("no", "listports|ssl"));
|
||||
|
||||
CListener::EAcceptType eAccept = pListener->GetAcceptType();
|
||||
Table.SetCell(t_s("IRC", "listports"),
|
||||
eAccept == CListener::ACCEPT_ALL ||
|
||||
eAccept == CListener::ACCEPT_IRC
|
||||
? t_s("yes", "listports|irc")
|
||||
: t_s("no", "listports|irc"));
|
||||
Table.SetCell(t_s("Web", "listports"),
|
||||
eAccept == CListener::ACCEPT_ALL ||
|
||||
eAccept == CListener::ACCEPT_HTTP
|
||||
? t_f("yes, on {1}", "listports|irc")(
|
||||
pListener->GetURIPrefix() + "/")
|
||||
: t_s("no", "listports|web"));
|
||||
pTable->SetCell(t_s("IRC", "listports"),
|
||||
eAccept == CListener::ACCEPT_ALL ||
|
||||
eAccept == CListener::ACCEPT_IRC
|
||||
? t_s("yes", "listports|irc")
|
||||
: t_s("no", "listports|irc"));
|
||||
pTable->SetCell(t_s("Web", "listports"),
|
||||
eAccept == CListener::ACCEPT_ALL ||
|
||||
eAccept == CListener::ACCEPT_HTTP
|
||||
? t_f("yes, on {1}", "listports|irc")(
|
||||
pListener->GetURIPrefix() + "/")
|
||||
: t_s("no", "listports|web"));
|
||||
}
|
||||
|
||||
PutStatus(Table);
|
||||
PutStatus(TableT);
|
||||
PutStatus(TableU);
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -1732,12 +1751,30 @@ void CClient::UserPortCommand(CString& sLine) {
|
||||
|
||||
std::unique_ptr<CListener> pListener;
|
||||
if (sPort.TrimPrefix("unix:")) {
|
||||
bool bSSL = sPort.TrimPrefix("ssl:");
|
||||
bool bSSL = false;
|
||||
CString sMode;
|
||||
CString sGroup;
|
||||
if (auto colon = sPort.find_first_of(':'); colon != std::string::npos) {
|
||||
VCString vsOpts;
|
||||
CString(sPort.substr(0, colon)).Split(",", vsOpts, false);
|
||||
for (CString& sOpt : vsOpts) {
|
||||
if (sOpt == "ssl") {
|
||||
bSSL = true;
|
||||
} else if (sOpt.TrimPrefix("mode=")) {
|
||||
sMode = sOpt;
|
||||
} else if (sOpt.TrimPrefix("group=")) {
|
||||
sGroup = sOpt;
|
||||
} else {
|
||||
throw PortCommandUsage{};
|
||||
}
|
||||
}
|
||||
sPort = sPort.substr(colon + 1);
|
||||
}
|
||||
const CString& sPath = sPort;
|
||||
CListener::EAcceptType eAccept = ParseEAccept(sLine.Token(2));
|
||||
CString sURIPrefix = sLine.Token(3);
|
||||
|
||||
pListener.reset(new CUnixListener(sPath, sURIPrefix, bSSL, eAccept));
|
||||
pListener.reset(new CUnixListener(sPath, sURIPrefix, bSSL, eAccept, sGroup, sMode));
|
||||
} else {
|
||||
bool bSSL = sPort.StartsWith("+");
|
||||
EAddrType eAddr = ParseEAddr(sLine.Token(2));
|
||||
@@ -1765,7 +1802,7 @@ void CClient::UserPortCommand(CString& sLine) {
|
||||
"[bindhost [uriprefix]]"));
|
||||
PutStatus(t_s("+ means SSL"));
|
||||
PutStatus(
|
||||
t_s("Or: AddPort unix:[ssl:]/path/to/socket <web|irc|all> "
|
||||
t_s("Or: AddPort unix:[ssl,mode=NNN,group=foo]:/path/to/socket <web|irc|all> "
|
||||
"[uriprefix]"));
|
||||
}
|
||||
} else if (sCommand.Equals("DELPORT")) {
|
||||
|
||||
@@ -14,6 +14,12 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <grp.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#include <znc/Listener.h>
|
||||
#include <znc/Config.h>
|
||||
#include <znc/znc.h>
|
||||
@@ -85,9 +91,21 @@ CConfig CTCPListener::ToConfig() const {
|
||||
return listenerConfig;
|
||||
}
|
||||
|
||||
CUnixListener::~CUnixListener() {
|
||||
CUnixListener::CUnixListener(const CString& sPath, const CString& sURIPrefix,
|
||||
bool bSSL, EAcceptType eAccept,
|
||||
const CString& sGid, const CString& sMode)
|
||||
: CListener(sURIPrefix, bSSL, eAccept),
|
||||
m_sPath(sPath),
|
||||
m_sGid(sGid),
|
||||
m_iMode(-1) {
|
||||
if (!sMode.empty()) {
|
||||
std::istringstream s(sMode);
|
||||
s >> std::oct >> m_iMode;
|
||||
}
|
||||
}
|
||||
|
||||
CUnixListener::~CUnixListener() {}
|
||||
|
||||
bool CUnixListener::Listen() {
|
||||
if (m_pListener) {
|
||||
errno = EINVAL;
|
||||
@@ -97,18 +115,85 @@ bool CUnixListener::Listen() {
|
||||
m_pListener = new CRealListener(*this);
|
||||
SetupSSL();
|
||||
|
||||
return CZNC::Get().GetManager().ListenUnix("UNIX_LISTENER", m_sPath,
|
||||
m_pListener);
|
||||
if (!CZNC::Get().GetManager().ListenUnix("UNIX_LISTENER", m_sPath,
|
||||
m_pListener))
|
||||
return false;
|
||||
|
||||
if (!m_sGid.empty()) {
|
||||
bool bSuccess = [&]() -> bool {
|
||||
std::vector<char> buffer(100);
|
||||
group gr{};
|
||||
group* result;
|
||||
retrysize:
|
||||
int err = getgrnam_r(m_sGid.c_str(), &gr, buffer.data(),
|
||||
buffer.size(), &result);
|
||||
switch (err) {
|
||||
case ERANGE: {
|
||||
if (buffer.size() > 10000) {
|
||||
DEBUG("Can't get gid due to memory size");
|
||||
return false;
|
||||
}
|
||||
buffer.resize(buffer.size() + 100);
|
||||
goto retrysize;
|
||||
}
|
||||
case 0: {
|
||||
if (!result) {
|
||||
DEBUG("Group not found");
|
||||
return false;
|
||||
}
|
||||
if (chown(m_sPath.c_str(), -1, result->gr_gid)) {
|
||||
char* e = strerror(errno);
|
||||
DEBUG("Can't chmod: " << e);
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
char* e = strerror(err);
|
||||
DEBUG("Error while getting gid " << e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}();
|
||||
if (!bSuccess) {
|
||||
m_pListener->Close();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_iMode != -1) {
|
||||
if (chmod(m_sPath.c_str(), m_iMode)) {
|
||||
char* e = strerror(errno);
|
||||
DEBUG("Error while chmod " << e);
|
||||
m_pListener->Close();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
CConfig CUnixListener::ToConfig() const {
|
||||
CConfig listenerConfig = CListener::ToConfig();
|
||||
|
||||
listenerConfig.AddKeyValuePair("Path", GetPath());
|
||||
if (!m_sGid.empty()) listenerConfig.AddKeyValuePair("Group", m_sGid);
|
||||
if (m_iMode != -1) {
|
||||
listenerConfig.AddKeyValuePair("Mode", GetMode());
|
||||
}
|
||||
|
||||
return listenerConfig;
|
||||
}
|
||||
|
||||
CString CUnixListener::GetMode() const {
|
||||
std::ostringstream s;
|
||||
if (m_iMode != -1) {
|
||||
s << std::oct << m_iMode;
|
||||
}
|
||||
return s.str();
|
||||
}
|
||||
|
||||
void CListener::ResetRealListener() { m_pListener = nullptr; }
|
||||
|
||||
CRealListener::~CRealListener() { m_Listener.ResetRealListener(); }
|
||||
|
||||
16
src/znc.cpp
16
src/znc.cpp
@@ -1744,13 +1744,16 @@ bool CZNC::AddTCPListener(unsigned short uPort, const CString& sBindHost,
|
||||
|
||||
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" : ""));
|
||||
const CString& sGroup, const CString& sMode, CString& sError) {
|
||||
CUtils::PrintAction("Binding to path [" + sPath + "]" +
|
||||
(bSSL ? " with SSL" : "") +
|
||||
(sGroup.empty() ? CString() : " gid=" + sGroup) +
|
||||
(sMode.empty() ? CString() : " mode=" + sMode));
|
||||
|
||||
if (!CheckSslAndPemFile(bSSL, sError)) return false;
|
||||
|
||||
CListener* pListener =
|
||||
new CUnixListener(sPath, sURIPrefix, bSSL, eAccept);
|
||||
new CUnixListener(sPath, sURIPrefix, bSSL, eAccept, sGroup, sMode);
|
||||
return FinishAddingListener(pListener, sError);
|
||||
}
|
||||
|
||||
@@ -1841,8 +1844,13 @@ bool CZNC::AddListener(CConfig* pConfig, CString& sError) {
|
||||
|
||||
return AddTCPListener(uPort, sBindHost, sURIPrefix, bSSL, eAddr,
|
||||
eAccept, sError);
|
||||
} else {
|
||||
CString sGroup;
|
||||
CString sMode;
|
||||
pConfig->FindStringEntry("group", sGroup);
|
||||
pConfig->FindStringEntry("mode", sMode);
|
||||
return AddUnixListener(sPath, sURIPrefix, bSSL, eAccept, sGroup, sMode, sError);
|
||||
}
|
||||
return AddUnixListener(sPath, sURIPrefix, bSSL, eAccept, sError);
|
||||
}
|
||||
|
||||
bool CZNC::AddListener(CListener* pListener) {
|
||||
|
||||
Reference in New Issue
Block a user