diff --git a/src/znc.cpp b/src/znc.cpp index a7dc3ebc..9dc9daea 100644 --- a/src/znc.cpp +++ b/src/znc.cpp @@ -625,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 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); + 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(); @@ -679,9 +687,13 @@ bool CZNC::WriteNewConfig(const CString& sConfigFile) { #endif vsLines.push_back(""); - 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); @@ -788,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)) { diff --git a/test/integration/framework/base.cpp b/test/integration/framework/base.cpp index ee012143..1890564a 100644 --- a/test/integration/framework/base.cpp +++ b/test/integration/framework/base.cpp @@ -14,9 +14,12 @@ * limitations under the License. */ -#include #include "base.h" +#include + +#include + 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 diff --git a/test/integration/framework/base.h b/test/integration/framework/base.h index f3441c2e..71098fd7 100644 --- a/test/integration/framework/base.h +++ b/test/integration/framework/base.h @@ -21,6 +21,7 @@ #include #include #include +#include #include #include @@ -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 WrapIO(Device* d) { return IO(d); } -using Socket = IO; +using Socket = IO; class Process : public IO { public: @@ -202,6 +204,9 @@ void IO::Write(QByteArray s, bool new_line) { FlushIfCan(m_device); } +inline void DisconnectFromServer(QTcpSocket* s) { s->disconnectFromHost(); } +inline void DisconnectFromServer(QLocalSocket* s) { s->disconnectFromServer(); } + template void IO::Close() { #ifdef __CYGWIN__ @@ -209,8 +214,10 @@ void IO::Close() { // without this line sleep(1); #endif - m_device->disconnectFromHost(); + DisconnectFromServer(m_device); } +int PickPortNumber(); + } // namespace znc_inttest diff --git a/test/integration/framework/znctest.cpp b/test/integration/framework/znctest.cpp index 77628c3e..ffc8d625 100644 --- a/test/integration/framework/znctest.cpp +++ b/test/integration/framework/znctest.cpp @@ -15,6 +15,7 @@ */ #include +#include #include "znctest.h" #ifndef ZNC_BIN_DIR @@ -24,13 +25,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 +44,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 +64,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 +75,8 @@ 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.connectToServer(m_dir.path() + "/inttest.znc"); [&] { ASSERT_TRUE(sock.waitForConnected()) << sock.errorString().toStdString(); diff --git a/test/integration/framework/znctest.h b/test/integration/framework/znctest.h index ba12d369..36352c09 100644 --- a/test/integration/framework/znctest.h +++ b/test/integration/framework/znctest.h @@ -16,17 +16,18 @@ #pragma once -#include "base.h" +#include #include -#include #include -#include +#include #include #include #include #include #include +#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 m_clients; + QLocalServer m_server; + std::list m_clients; }; } // namespace znc_inttest diff --git a/test/integration/tests/core.cpp b/test/integration/tests/core.cpp index f1a0768e..9f056b97 100644 --- a/test/integration/tests/core.cpp +++ b/test/integration/tests/core.cpp @@ -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 : diff --git a/test/integration/tests/modules.cpp b/test/integration/tests/modules.cpp index 3b01147b..f000ecb5 100644 --- a/test/integration/tests/modules.cpp +++ b/test/integration/tests/modules.cpp @@ -20,6 +20,7 @@ #include "znctest.h" #include +#include 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 localhost"); 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 localhost"); client2.ReadUntil( - "NOTICE nick :*** user@identifier attached from 127.0.0.1"); + "NOTICE nick :*** user@identifier attached from localhost"); client2.Write("QUIT"); - client.ReadUntil("NOTICE nick :*** user detached from 127.0.0.1"); + client.ReadUntil("NOTICE nick :*** user detached from localhost"); client3.Close(); client.ReadUntil( - "NOTICE nick :*** user@identifier detached from 127.0.0.1"); + "NOTICE nick :*** user@identifier detached from localhost"); } TEST_F(ZNCTest, ClientNotifyModule) { @@ -70,35 +71,35 @@ TEST_F(ZNCTest, ClientNotifyModule) { }; 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.ReadUntil(":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.ReadUntil(":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 (localhost) 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.ReadUntil(":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 (localhost / 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.ReadUntil(":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 (localhost / 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.ReadUntil(":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 +242,16 @@ TEST_F(ZNCTest, KeepNickModule) { } TEST_F(ZNCTest, ModuleCSRFOverride) { + 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 +356,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 +382,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 +399,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 +413,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()) diff --git a/test/integration/tests/scripting.cpp b/test/integration/tests/scripting.cpp index afe75b91..fbba2f3d 100644 --- a/test/integration/tests/scripting.cpp +++ b/test/integration/tests/scripting.cpp @@ -491,7 +491,7 @@ TEST_F(ZNCTest, ModpythonSaslAuth) { 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 " + ":irc.znc.in 900 nick nick!user@localhost user :You are now logged in " "as user"); } @@ -548,7 +548,7 @@ TEST_F(ZNCTest, ModperlSaslAuth) { 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 " + ":irc.znc.in 900 nick nick!user@localhost user :You are now logged in " "as user"); }