Browse Source

Added UDP and TCP dns servers and associated tests

develop
Robin Thoni 6 years ago
parent
commit
9e7bb0ef47

+ 5
- 1
CMakeLists.txt View File

@@ -14,5 +14,9 @@ add_executable(${PROJECT_NAME}
14 14
         )
15 15
 
16 16
 add_subdirectory(src/modules/dns-base)
17
+add_subdirectory(src/modules/dns-server)
17 18
 
18
-target_link_libraries(${PROJECT_NAME} Qt5::Core qdnsagg-dns-base)
19
+include_directories(src/modules/dns-base/includes)
20
+include_directories(src/modules/dns-server/includes)
21
+
22
+target_link_libraries(${PROJECT_NAME} Qt5::Core qdnsagg-dns-base qdnsagg-dns-server)

+ 28
- 1
src/main.cpp View File

@@ -1,9 +1,36 @@
1 1
 #include <iostream>
2 2
 #include <QCoreApplication>
3
+#include "dns-server/QUdpDnsServer.h"
4
+#include "dns-server/QTcpDnsServer.h"
5
+#include "dns-base/QDnsBase.h"
3 6
 
4 7
 int main(int argc, char* argv[])
5 8
 {
6 9
     QCoreApplication app(argc, argv);
7
-    std::cout << "Hello, World!" << std::endl;
10
+
11
+    QHostAddress bindAddress("127.0.0.1");
12
+    quint16 bindPort = 5053;
13
+
14
+    auto onRequest = [](QDns::Server::QUdpDnsServer::OnRequestParams params)
15
+    {
16
+        QDns::Base::QDnsPacket answer = params.packet.makeAnswer();
17
+        auto header = answer.getHeader();
18
+        header.setReplyCode(QDns::Base::HeaderReplyCode::Refused);
19
+        answer.setHeader(header);
20
+
21
+        params.sendAnswer(QDns::Server::QDnsServer::SendAnswerParams
22
+                                  {
23
+                                          answer
24
+                                  });
25
+    };
26
+
27
+    QDns::Server::QTcpDnsServer tcpServer;
28
+    tcpServer.start(bindAddress, bindPort);
29
+    QObject::connect(&tcpServer, &QDns::Server::QDnsServer::onRequest, onRequest);
30
+
31
+    QDns::Server::QUdpDnsServer udpServer;
32
+    udpServer.start(bindAddress, bindPort);
33
+    QObject::connect(&udpServer, &QDns::Server::QDnsServer::onRequest, onRequest);
34
+
8 35
     return app.exec();
9 36
 }

+ 29
- 0
src/modules/dns-server/CMakeLists.txt View File

@@ -0,0 +1,29 @@
1
+cmake_minimum_required(VERSION 3.9)
2
+project(qdnsagg-dns-server)
3
+enable_testing()
4
+
5
+include_directories(includes ../dns-base/includes)
6
+
7
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
8
+set(CMAKE_AUTOMOC ON)
9
+set(CMAKE_CXX_STANDARD 17)
10
+
11
+find_package(Qt5Core REQUIRED)
12
+find_package(Qt5Network REQUIRED)
13
+
14
+add_library(${PROJECT_NAME}
15
+        src/QDnsServer.cpp
16
+        includes/dns-server/QDnsServer.h
17
+        src/QUdpDnsServer.cpp
18
+        includes/dns-server/QUdpDnsServer.h
19
+        src/QTcpDnsServer.cpp
20
+        includes/dns-server/QTcpDnsServer.h
21
+        src/QTcpDnsServerClient.cpp
22
+        src/QTcpDnsServerClient.h)
23
+
24
+target_link_libraries(${PROJECT_NAME} Qt5::Core Qt5::Network qdnsagg-dns-base)
25
+
26
+find_package(GTest)
27
+if (GTEST_FOUND)
28
+    add_subdirectory(tests)
29
+endif (GTEST_FOUND)

+ 44
- 0
src/modules/dns-server/includes/dns-server/QDnsServer.h View File

@@ -0,0 +1,44 @@
1
+//
2
+// Created by robin on 1/31/18.
3
+//
4
+
5
+#ifndef QDNSAGG_QDNSSERVER_H
6
+#define QDNSAGG_QDNSSERVER_H
7
+
8
+#include <QtCore/QObject>
9
+#include <dns-base/QDnsPacket.h>
10
+#include <QtNetwork/QHostAddress>
11
+#include <functional>
12
+
13
+namespace QDns
14
+{
15
+    namespace Server
16
+    {
17
+        class QDnsServer : public QObject
18
+        {
19
+            Q_OBJECT
20
+        public:
21
+            struct SendAnswerParams
22
+            {
23
+                QDns::Base::QDnsPacket answer;
24
+            };
25
+
26
+            struct OnRequestParams
27
+            {
28
+                QDns::Base::QDnsPacket packet;
29
+                QHostAddress senderAddress;
30
+                quint16 senderPort;
31
+                std::function<void(SendAnswerParams)> sendAnswer;
32
+            };
33
+
34
+        signals:
35
+            void onRequest(OnRequestParams params);
36
+
37
+        public slots:
38
+            virtual bool start(const QHostAddress& host, quint16 port) = 0;
39
+        };
40
+    }
41
+}
42
+
43
+
44
+#endif //QDNSAGG_QDNSSERVER_H

+ 37
- 0
src/modules/dns-server/includes/dns-server/QTcpDnsServer.h View File

@@ -0,0 +1,37 @@
1
+//
2
+// Created by robin on 1/31/18.
3
+//
4
+
5
+#ifndef QDNSAGG_QTCPDNSSERVER_H
6
+#define QDNSAGG_QTCPDNSSERVER_H
7
+
8
+#include <QTcpServer>
9
+#include "QDnsServer.h"
10
+
11
+namespace QDns
12
+{
13
+    namespace Server
14
+    {
15
+        class QTcpDnsServerClient;
16
+
17
+        class QTcpDnsServer : public QDnsServer
18
+        {
19
+            Q_OBJECT
20
+        public:
21
+            QTcpDnsServer();
22
+
23
+        public slots:
24
+            bool start(const QHostAddress& host, quint16 port) override;
25
+
26
+        private slots:
27
+            void onNewConnection();
28
+
29
+        private:
30
+            QTcpServer* m_socket;
31
+
32
+            QList<QTcpDnsServerClient*> m_clients;
33
+        };
34
+    }
35
+}
36
+
37
+#endif //QDNSAGG_QTCPDNSSERVER_H

+ 37
- 0
src/modules/dns-server/includes/dns-server/QUdpDnsServer.h View File

@@ -0,0 +1,37 @@
1
+//
2
+// Created by robin on 1/24/18.
3
+//
4
+
5
+#ifndef QDNSAGG_QDNSUDPSERVER_H
6
+#define QDNSAGG_QDNSUDPSERVER_H
7
+
8
+#include <QObject>
9
+#include <QUdpSocket>
10
+#include "dns-base/QDnsPacket.h"
11
+#include "QDnsServer.h"
12
+
13
+namespace QDns
14
+{
15
+    namespace Server
16
+    {
17
+        class QUdpDnsServer : public QDnsServer
18
+        {
19
+            Q_OBJECT
20
+        public:
21
+            QUdpDnsServer();
22
+
23
+        public slots:
24
+            bool start(const QHostAddress& host, quint16 port) override;
25
+
26
+        private slots:
27
+            void onSocketReadyRead();
28
+
29
+            void sendAnswer(const QDns::Server::QDnsServer::SendAnswerParams& answerParams, const QHostAddress& senderAddress, quint16 senderPort);
30
+
31
+        private:
32
+            QUdpSocket* m_socket;
33
+        };
34
+    }
35
+}
36
+
37
+#endif //QDNSAGG_QDNSUDPSERVER_H

+ 12
- 0
src/modules/dns-server/src/QDnsServer.cpp View File

@@ -0,0 +1,12 @@
1
+//
2
+// Created by robin on 1/31/18.
3
+//
4
+
5
+#include "dns-server/QDnsServer.h"
6
+
7
+namespace QDns
8
+{
9
+    namespace Server
10
+    {
11
+    }
12
+}

+ 44
- 0
src/modules/dns-server/src/QTcpDnsServer.cpp View File

@@ -0,0 +1,44 @@
1
+//
2
+// Created by robin on 1/31/18.
3
+//
4
+
5
+#include <QTcpSocket>
6
+#include "dns-server/QTcpDnsServer.h"
7
+#include "QTcpDnsServerClient.h"
8
+
9
+namespace QDns
10
+{
11
+    namespace Server
12
+    {
13
+        QTcpDnsServer::QTcpDnsServer()
14
+            : m_socket(new QTcpServer(this))
15
+        {
16
+            connect(m_socket, &QTcpServer::newConnection, this, &QTcpDnsServer::onNewConnection);
17
+        }
18
+
19
+        bool QTcpDnsServer::start(const QHostAddress &host, quint16 port)
20
+        {
21
+            return m_socket->listen(host, port);
22
+        }
23
+
24
+        void QTcpDnsServer::onNewConnection()
25
+        {
26
+            auto socket = m_socket->nextPendingConnection();
27
+            if (socket == nullptr)
28
+            {
29
+                return;
30
+            }
31
+            auto client = new QTcpDnsServerClient(socket, this);
32
+            connect(socket, &QTcpSocket::disconnected, [client, this]()
33
+            {
34
+                m_clients.removeOne(client);
35
+                client->deleteLater();
36
+            });
37
+            connect(client, &QTcpDnsServerClient::onRequest, [this](QDnsServer::OnRequestParams params)
38
+            {
39
+                emit onRequest(params);
40
+            });
41
+            m_clients.append(client);
42
+        }
43
+    }
44
+}

+ 69
- 0
src/modules/dns-server/src/QTcpDnsServerClient.cpp View File

@@ -0,0 +1,69 @@
1
+//
2
+// Created by robin on 2/1/18.
3
+//
4
+
5
+#include "QTcpDnsServerClient.h"
6
+
7
+namespace QDns
8
+{
9
+    namespace Server
10
+    {
11
+        QTcpDnsServerClient::QTcpDnsServerClient(QTcpSocket *socket, QObject* parent)
12
+            : QObject(parent)
13
+            , m_socket(socket)
14
+            , m_size(0)
15
+        {
16
+            m_socket->setParent(this);
17
+            connect(m_socket, &QTcpSocket::readyRead, this, &QTcpDnsServerClient::onReadyRead);
18
+            // TODO implement timeout (~2 mins, RFC 7766)
19
+        }
20
+
21
+        void QTcpDnsServerClient::sendAnswer(const QDns::Server::QDnsServer::SendAnswerParams &answerParams)
22
+        {
23
+            auto answerRaw = answerParams.answer.serialize(); // TODO split packet if too big (>65535)
24
+            QByteArray size;
25
+            QDataStream dataStream(&size, QIODevice::WriteOnly);
26
+            dataStream << (quint16) answerRaw.size();
27
+            m_socket->write(size.constData(), size.size());
28
+            m_socket->write(answerRaw.constData(), answerRaw.size());
29
+        }
30
+
31
+        void QTcpDnsServerClient::onReadyRead()
32
+        {
33
+            m_buffer.append(m_socket->readAll());
34
+
35
+            bool readAgain;
36
+
37
+            do
38
+            {
39
+                readAgain = false;
40
+                if (m_buffer.size() > 2 && m_size == 0)
41
+                {
42
+                    QDataStream dataStream(m_buffer);
43
+                    dataStream >> m_size;
44
+                }
45
+                if (m_size != 0 && m_buffer.size() >= m_size + 2)
46
+                {
47
+                    auto data = m_buffer.mid(2, m_size);
48
+                    m_buffer.remove(0, m_size + 2);
49
+                    m_size = 0;
50
+                    readAgain = m_buffer.size() != 0;
51
+                    QDns::Base::QDnsPacket packet(data);
52
+                    if (packet.isValid())
53
+                    {
54
+                        emit onRequest(QDnsServer::OnRequestParams
55
+                                               {
56
+                                                       packet,
57
+                                                       m_socket->peerAddress(),
58
+                                                       m_socket->peerPort(),
59
+                                                       [this](QDns::Server::QDnsServer::SendAnswerParams answerParams)
60
+                                                       {
61
+                                                           sendAnswer(answerParams);
62
+                                                       }
63
+                                               });
64
+                    }
65
+                }
66
+            } while (readAgain);
67
+        }
68
+    }
69
+}

+ 41
- 0
src/modules/dns-server/src/QTcpDnsServerClient.h View File

@@ -0,0 +1,41 @@
1
+//
2
+// Created by robin on 2/1/18.
3
+//
4
+
5
+#ifndef QDNSAGG_QTCPDNSSERVERCLIENT_H
6
+#define QDNSAGG_QTCPDNSSERVERCLIENT_H
7
+
8
+#include <QtCore/QObject>
9
+#include <QtNetwork/QTcpSocket>
10
+#include <dns-server/QDnsServer.h>
11
+
12
+namespace QDns
13
+{
14
+    namespace Server
15
+    {
16
+        class QTcpDnsServerClient : public QObject
17
+        {
18
+            Q_OBJECT
19
+        public:
20
+            explicit QTcpDnsServerClient(QTcpSocket* socket, QObject* parent = nullptr);
21
+
22
+        signals:
23
+            void onRequest(QDnsServer::OnRequestParams params);
24
+
25
+        public slots:
26
+            void sendAnswer(const QDns::Server::QDnsServer::SendAnswerParams& answerParams);
27
+
28
+        private slots:
29
+            void onReadyRead();
30
+
31
+        private:
32
+            QTcpSocket* m_socket;
33
+
34
+            QByteArray m_buffer;
35
+
36
+            quint16 m_size;
37
+        };
38
+    }
39
+}
40
+
41
+#endif //QDNSAGG_QTCPDNSSERVERCLIENT_H

+ 56
- 0
src/modules/dns-server/src/QUdpDnsServer.cpp View File

@@ -0,0 +1,56 @@
1
+//
2
+// Created by robin on 1/24/18.
3
+//
4
+
5
+#include "dns-server/QUdpDnsServer.h"
6
+
7
+namespace QDns
8
+{
9
+    namespace Server
10
+    {
11
+        QUdpDnsServer::QUdpDnsServer()
12
+            : m_socket(new QUdpSocket(this))
13
+        {
14
+            connect(m_socket, &QUdpSocket::readyRead, this, &QUdpDnsServer::onSocketReadyRead);
15
+        }
16
+
17
+        bool QUdpDnsServer::start(const QHostAddress &host, quint16 port)
18
+        {
19
+            return m_socket->bind(host, port);
20
+        }
21
+
22
+        void QUdpDnsServer::onSocketReadyRead()
23
+        {
24
+            while (m_socket->hasPendingDatagrams())
25
+            {
26
+                QByteArray datagram;
27
+                datagram.resize(m_socket->pendingDatagramSize());
28
+                QHostAddress senderAddress;
29
+                quint16 senderPort;
30
+
31
+                m_socket->readDatagram(datagram.data(), datagram.size(), &senderAddress, &senderPort);
32
+                QDns::Base::QDnsPacket packet(datagram);
33
+                if (packet.isValid())
34
+                {
35
+                    emit onRequest(OnRequestParams
36
+                                           {
37
+                                                   packet,
38
+                                                   senderAddress,
39
+                                                   senderPort,
40
+                                                   [this, senderAddress, senderPort](QDns::Server::QDnsServer::SendAnswerParams answerParams)
41
+                                                   {
42
+                                                       sendAnswer(answerParams, senderAddress, senderPort);
43
+                                                   }
44
+                                           });
45
+                }
46
+            }
47
+        }
48
+
49
+        void QUdpDnsServer::sendAnswer(const QDns::Server::QDnsServer::SendAnswerParams& answerParams,
50
+                                       const QHostAddress& senderAddress, quint16 senderPort)
51
+        {
52
+            auto answerRaw = answerParams.answer.serialize(); // TODO split packet if too big
53
+            m_socket->writeDatagram(answerRaw, senderAddress, senderPort);
54
+        }
55
+    }
56
+}

+ 13
- 0
src/modules/dns-server/tests/CMakeLists.txt View File

@@ -0,0 +1,13 @@
1
+cmake_minimum_required(VERSION 3.9)
2
+project(qdnsagg-dns-server-tests)
3
+
4
+add_executable(${PROJECT_NAME}
5
+        main.cpp
6
+        UdpDnsServer.cpp
7
+        TcpDnsServer.cpp
8
+        serverTests.h)
9
+
10
+target_link_libraries(${PROJECT_NAME} gtest pthread qdnsagg-dns-server)
11
+
12
+enable_testing()
13
+add_test(${PROJECT_NAME} ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME})

+ 75
- 0
src/modules/dns-server/tests/TcpDnsServer.cpp View File

@@ -0,0 +1,75 @@
1
+//
2
+// Created by robin on 1/30/18.
3
+//
4
+
5
+
6
+#include <gtest/gtest.h>
7
+#include <QtCore/QCoreApplication>
8
+#include <QtCore/QTimer>
9
+#include <QtNetwork/QTcpSocket>
10
+
11
+#include "dns-server/QTcpDnsServer.h"
12
+
13
+#include "serverTests.h"
14
+
15
+struct TcpDnsServerTestParams
16
+{
17
+    QList<const char*> hexData;
18
+};
19
+
20
+class TcpDnsServerTest : public ::testing::TestWithParam<TcpDnsServerTestParams>
21
+{
22
+};
23
+
24
+TEST_P(TcpDnsServerTest, simple)
25
+{
26
+    auto params = GetParam();
27
+    QCoreApplication app(m_argc, m_argv);
28
+
29
+    QTimer timer;
30
+    QObject::connect(&timer, &QTimer::timeout, qApp, &QCoreApplication::quit);
31
+    timer.start(1000);
32
+
33
+    QDns::Server::QTcpDnsServer server;
34
+    ASSERT_TRUE(server.start(QHostAddress("127.0.0.1"), 5053));
35
+    QList<QDns::Base::QDnsPacket> packets;
36
+    QObject::connect(&server, &QDns::Server::QDnsServer::onRequest, [&packets](QDns::Server::QDnsServer::OnRequestParams params)
37
+    {
38
+        packets.append(params.packet);
39
+    });
40
+    QList<QDns::Base::QDnsPacket> expectedPackets;
41
+    QTcpSocket socket;
42
+    socket.connectToHost(QHostAddress("127.0.0.1"), 5053);
43
+    socket.waitForConnected();
44
+    for (auto hexData : params.hexData)
45
+    {
46
+        auto data = QByteArray::fromHex(hexData);
47
+        QByteArray size;
48
+        QDataStream dataStream(&size, QIODevice::WriteOnly);
49
+        dataStream << (quint16) data.size();
50
+        socket.write(size.constData(), size.size());
51
+        socket.write(data.constData(), data.length());
52
+        QDns::Base::QDnsPacket packet(data);
53
+        if (packet.isValid())
54
+        {
55
+            expectedPackets.append(packet);
56
+        }
57
+    }
58
+    socket.disconnectFromHost();
59
+    socket.waitForDisconnected();
60
+
61
+    ASSERT_EQ(app.exec(), 0);
62
+    ASSERT_EQ(packets.size(), expectedPackets.size());
63
+}
64
+
65
+INSTANTIATE_TEST_CASE_P(TcpDnsServerTestInst,
66
+                        TcpDnsServerTest,
67
+                        ::testing::Values(
68
+                                TcpDnsServerTestParams
69
+                                        {
70
+                                                QList<const char*>()
71
+                                                        << "e1bc010000010000000000000266720000010001"
72
+                                                        << "00"
73
+                                                        << "e1bc010000010000000000000266720000010001"
74
+                                        }
75
+                        ));

+ 67
- 0
src/modules/dns-server/tests/UdpDnsServer.cpp View File

@@ -0,0 +1,67 @@
1
+//
2
+// Created by robin on 1/30/18.
3
+//
4
+
5
+
6
+#include <gtest/gtest.h>
7
+#include <QtCore/QCoreApplication>
8
+#include <QtCore/QTimer>
9
+
10
+#include "dns-server/QUdpDnsServer.h"
11
+
12
+#include "serverTests.h"
13
+
14
+struct UdpDnsServerTestParams
15
+{
16
+    QList<const char*> hexData;
17
+};
18
+
19
+class UdpDnsServerTest : public ::testing::TestWithParam<UdpDnsServerTestParams>
20
+{
21
+};
22
+
23
+TEST_P(UdpDnsServerTest, simple)
24
+{
25
+    auto params = GetParam();
26
+    QCoreApplication app(m_argc, m_argv);
27
+
28
+    QTimer timer;
29
+    QObject::connect(&timer, &QTimer::timeout, qApp, &QCoreApplication::quit);
30
+    timer.start(1000);
31
+
32
+    QDns::Server::QUdpDnsServer server;
33
+    ASSERT_TRUE(server.start(QHostAddress("127.0.0.1"), 5053));
34
+    QList<QDns::Base::QDnsPacket> packets;
35
+    QObject::connect(&server, &QDns::Server::QDnsServer::onRequest, [&packets](QDns::Server::QDnsServer::OnRequestParams params)
36
+    {
37
+        packets.append(params.packet);
38
+    });
39
+    QList<QDns::Base::QDnsPacket> expectedPackets;
40
+    QUdpSocket socket;
41
+    socket.connectToHost(QHostAddress("127.0.0.1"), 5053);
42
+    for (auto hexData : params.hexData)
43
+    {
44
+        auto data = QByteArray::fromHex(hexData);
45
+        socket.write(data.constData(), data.length());
46
+        QDns::Base::QDnsPacket packet(data);
47
+        if (packet.isValid())
48
+        {
49
+            expectedPackets.append(packet);
50
+        }
51
+    }
52
+
53
+    ASSERT_EQ(app.exec(), 0);
54
+    ASSERT_EQ(packets.size(), expectedPackets.size());
55
+}
56
+
57
+INSTANTIATE_TEST_CASE_P(UdpDnsServerTestInst,
58
+                        UdpDnsServerTest,
59
+                        ::testing::Values(
60
+                                UdpDnsServerTestParams
61
+                                        {
62
+                                                QList<const char*>()
63
+                                                        << "e1bc010000010000000000000266720000010001"
64
+                                                        << "00"
65
+                                                        << "e1bc010000010000000000000266720000010001"
66
+                                        }
67
+                        ));

+ 14
- 0
src/modules/dns-server/tests/main.cpp View File

@@ -0,0 +1,14 @@
1
+#include <gtest/gtest.h>
2
+
3
+#include "serverTests.h"
4
+
5
+int m_argc;
6
+char** m_argv;
7
+
8
+int main(int argc, char* argv[])
9
+{
10
+    m_argc = argc;
11
+    m_argv = argv;
12
+    ::testing::InitGoogleTest(&argc, argv);
13
+    return RUN_ALL_TESTS();
14
+}

+ 11
- 0
src/modules/dns-server/tests/serverTests.h View File

@@ -0,0 +1,11 @@
1
+//
2
+// Created by robin on 1/30/18.
3
+//
4
+
5
+#ifndef QDNSAGG_SERVERTESTS_H
6
+#define QDNSAGG_SERVERTESTS_H
7
+
8
+extern int m_argc;
9
+extern char** m_argv;
10
+
11
+#endif //QDNSAGG_SERVERTESTS_H

Loading…
Cancel
Save