diff --git a/include/znc/IRCNetwork.h b/include/znc/IRCNetwork.h index 63dc5447..bc3affd6 100644 --- a/include/znc/IRCNetwork.h +++ b/include/znc/IRCNetwork.h @@ -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); diff --git a/include/znc/Server.h b/include/znc/Server.h index 4598666f..9f237ab2 100644 --- a/include/znc/Server.h +++ b/include/znc/Server.h @@ -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 diff --git a/modules/controlpanel.cpp b/modules/controlpanel.cpp index a8c3f121..ae23945c 100644 --- a/modules/controlpanel.cpp +++ b/modules/controlpanel.cpp @@ -21,6 +21,7 @@ #include #include #include +#include "znc/Server.h" using std::map; using std::vector; @@ -1249,6 +1250,10 @@ class CAdminMod : public CModule { PutModule( t_s("Usage: AddServer [[+]port] " "[password]")); + if (GetUser()->IsAdmin()) { + PutModule(t_s("Or: AddServer 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())); diff --git a/modules/data/webadmin/files/webadmin.js b/modules/data/webadmin/files/webadmin.js index 0fb776be..7611d4a6 100644 --- a/modules/data/webadmin/files/webadmin.js +++ b/modules/data/webadmin/files/webadmin.js @@ -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( - $("").append($("").attr({"type":"text"}) + $("").append($("").attr({"type":"text","disabled":disable}) .addClass("servers_row_host").val(host)), - $("").append($("").attr({"type":"number"}) + $("").append($("").attr({"type":"number","disabled":disable}) .addClass("servers_row_port").val(port)), - $("").append($("").attr({"type":"checkbox"}) + $("").append($("").attr({"type":"checkbox","disabled":disable}) .addClass("servers_row_ssl").prop("checked", ssl)), - $("").append($("").attr({"type":"text"}) + $("").append($("").attr({"type":"text","disabled":disable}) .addClass("servers_row_pass").val(pass)), $("").append($("").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); }); diff --git a/modules/data/webadmin/tmpl/add_edit_network.tmpl b/modules/data/webadmin/tmpl/add_edit_network.tmpl index a0239711..c2f0ddc7 100644 --- a/modules/data/webadmin/tmpl/add_edit_network.tmpl +++ b/modules/data/webadmin/tmpl/add_edit_network.tmpl @@ -5,6 +5,7 @@
diff --git a/modules/webadmin.cpp b/modules/webadmin.cpp index 692f125c..feacc452 100644 --- a/modules/webadmin.cpp +++ b/modules/webadmin.cpp @@ -972,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())); @@ -1147,9 +1148,22 @@ class CWebAdminMod : public CModule { VCString vsArgs; if (spSession->IsAdmin() || !spSession->GetUser()->DenySetNetwork()) { + std::set 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()); } } @@ -1404,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()); } } diff --git a/src/ClientCommand.cpp b/src/ClientCommand.cpp index 40557c30..d20a23d7 100644 --- a/src/ClientCommand.cpp +++ b/src/ClientCommand.cpp @@ -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 [[+]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 [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") : ""); diff --git a/src/IRCNetwork.cpp b/src/IRCNetwork.cpp index 70511704..2f097ce2 100644 --- a/src/IRCNetwork.cpp +++ b/src/IRCNetwork.cpp @@ -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; } diff --git a/src/Server.cpp b/src/Server.cpp index 53dc0e09..bebb3f39 100644 --- a/src/Server.cpp +++ b/src/Server.cpp @@ -17,11 +17,12 @@ #include 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; } diff --git a/third_party/Csocket b/third_party/Csocket index cf9a613c..83993952 160000 --- a/third_party/Csocket +++ b/third_party/Csocket @@ -1 +1 @@ -Subproject commit cf9a613c2b963ab0e2946580e9f0493cbc46d524 +Subproject commit 839939522b07986d81239dcc253e3155fe45d71b