Selaa lähdekoodia

Added TCP and UDP clients

develop
Robin Thoni 6 vuotta sitten
vanhempi
commit
68d7a3c309

+ 23
- 0
src/modules/dns-client/CMakeLists.txt Näytä tiedosto

@@ -0,0 +1,23 @@
1
+cmake_minimum_required(VERSION 3.9)
2
+project(qdnsagg-dns-client)
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/QDnsClient.cpp includes/dns-client/QDnsClient.h src/QDnsUdpClient.cpp includes/dns-client/QDnsUdpClient.h src/QDnsTcpClient.cpp includes/dns-client/QDnsTcpClient.h)
16
+
17
+target_link_libraries(${PROJECT_NAME} Qt5::Core Qt5::Network qdnsagg-dns-base)
18
+
19
+find_package(GTest)
20
+if (GTEST_FOUND)
21
+    enable_testing()
22
+    add_subdirectory(tests)
23
+endif (GTEST_FOUND)

+ 115
- 0
src/modules/dns-client/includes/dns-client/QDnsClient.h Näytä tiedosto

@@ -0,0 +1,115 @@
1
+//
2
+// Created by robin on 2/2/18.
3
+//
4
+
5
+#ifndef QDNSAGG_QDNSCLIENT_H
6
+#define QDNSAGG_QDNSCLIENT_H
7
+
8
+#include <QObject>
9
+#include <QHostAddress>
10
+#include "dns-base/QDnsPacket.h"
11
+
12
+namespace QDns
13
+{
14
+    namespace Client
15
+    {
16
+        class QDnsClient : public QObject
17
+        {
18
+            Q_OBJECT
19
+        public:
20
+            enum ClientState
21
+            {
22
+                Disconnected = 0,
23
+                Connecting = 1,
24
+                Connected = 2,
25
+                Disconnecting = 3
26
+            };
27
+
28
+            enum ErrorCode
29
+            {
30
+                Unknown = 0,
31
+                Timeout = 1,
32
+                InvalidData = 2,
33
+                UnknownId = 3,
34
+                NotAnswer = 4,
35
+                SocketError = 5,
36
+                NotConnected = 6
37
+            };
38
+
39
+            struct OnStateChangedParams
40
+            {
41
+                quint64 reserved;
42
+                ClientState state;
43
+            };
44
+
45
+            struct OnAnswerParams
46
+            {
47
+                quint64 reserved;
48
+                QDns::Base::QDnsPacket query;
49
+                QDns::Base::QDnsPacket answer;
50
+                QHostAddress host;
51
+                quint16 port;
52
+            };
53
+
54
+            struct OnErrorParams
55
+            {
56
+                quint64 reserved;
57
+                ErrorCode error;
58
+                QAbstractSocket::SocketError socketError;
59
+                QDns::Base::QDnsPacket query;
60
+                QByteArray data;
61
+                QDns::Base::QDnsPacket answer;
62
+                QHostAddress host;
63
+                quint16 port;
64
+            };
65
+
66
+            explicit QDnsClient(const QHostAddress& host, quint16 port = 53);
67
+
68
+            const QHostAddress &getHost() const;
69
+
70
+            quint16 getPort() const;
71
+
72
+            quint32 getQueryTimeout() const;
73
+
74
+            void setQueryTimeout(quint32 queryTimeout);
75
+
76
+            ClientState getState() const;
77
+
78
+        signals:
79
+            void onStateChanged(OnStateChangedParams params);
80
+            void onAnswer(OnAnswerParams params);
81
+            void onError(OnErrorParams params);
82
+
83
+        public slots:
84
+            virtual void connectToHost() = 0;
85
+            virtual void query(QDns::Base::QDnsPacket packet);
86
+            virtual void disconnectFromHost() = 0;
87
+
88
+        protected:
89
+            struct ProcessDataParams
90
+            {
91
+                QByteArray data;
92
+                QHostAddress senderAddress;
93
+                quint16 senderPort;
94
+            };
95
+
96
+            virtual void doQuery(QDns::Base::QDnsPacket packet) = 0;
97
+
98
+            void processData(ProcessDataParams params);
99
+
100
+            void setState(ClientState state);
101
+
102
+            QHostAddress m_host;
103
+
104
+            quint16 m_port;
105
+
106
+            quint32 m_queryTimeout;
107
+
108
+            QMap<quint16, QDns::Base::QDnsPacket> m_pendingQueries;
109
+
110
+            ClientState m_state;
111
+        };
112
+    }
113
+}
114
+
115
+#endif //QDNSAGG_QDNSCLIENT_H

+ 43
- 0
src/modules/dns-client/includes/dns-client/QDnsTcpClient.h Näytä tiedosto

@@ -0,0 +1,43 @@
1
+//
2
+// Created by robin on 2/7/18.
3
+//
4
+
5
+#ifndef QDNSAGG_QDNSTCPCLIENT_H
6
+#define QDNSAGG_QDNSTCPCLIENT_H
7
+
8
+#include <QtNetwork/QTcpSocket>
9
+#include "QDnsClient.h"
10
+
11
+namespace QDns
12
+{
13
+    namespace Client
14
+    {
15
+        class QDnsTcpClient : public QDnsClient
16
+        {
17
+            Q_OBJECT
18
+        public:
19
+            explicit QDnsTcpClient(const QHostAddress &host, quint16 port = 53);
20
+
21
+        public slots:
22
+            void connectToHost() override;
23
+            void disconnectFromHost() override;
24
+
25
+        protected:
26
+            void doQuery(QDns::Base::QDnsPacket packet) override;
27
+
28
+        private slots:
29
+            void onSocketReadyRead();
30
+            void onSocketStateChanged(QAbstractSocket::SocketState socketState);
31
+            void onSocketError(QAbstractSocket::SocketError error);
32
+
33
+        private:
34
+            QTcpSocket* m_socket;
35
+
36
+            QByteArray m_buffer;
37
+
38
+            quint16 m_size;
39
+        };
40
+    }
41
+}
42
+
43
+#endif //QDNSAGG_QDNSTCPCLIENT_H

+ 38
- 0
src/modules/dns-client/includes/dns-client/QDnsUdpClient.h Näytä tiedosto

@@ -0,0 +1,38 @@
1
+//
2
+// Created by robin on 2/2/18.
3
+//
4
+
5
+#ifndef QDNSAGG_QDNSUDPCLIENT_H
6
+#define QDNSAGG_QDNSUDPCLIENT_H
7
+
8
+#include <QtNetwork/QUdpSocket>
9
+#include "QDnsClient.h"
10
+
11
+namespace QDns
12
+{
13
+    namespace Client
14
+    {
15
+        class QDnsUdpClient : public QDnsClient
16
+        {
17
+            Q_OBJECT
18
+        public:
19
+            explicit QDnsUdpClient(const QHostAddress& host, quint16 port = 53);
20
+
21
+        public slots:
22
+            void connectToHost() override;
23
+            void disconnectFromHost() override;
24
+
25
+        protected:
26
+            void doQuery(QDns::Base::QDnsPacket packet) override;
27
+
28
+        private slots:
29
+            void onReadyRead();
30
+            void onSocketError(QAbstractSocket::SocketError error);
31
+
32
+        private:
33
+            QUdpSocket* m_socket;
34
+        };
35
+    }
36
+}
37
+
38
+#endif //QDNSAGG_QDNSUDPCLIENT_H

+ 166
- 0
src/modules/dns-client/src/QDnsClient.cpp Näytä tiedosto

@@ -0,0 +1,166 @@
1
+//
2
+// Created by robin on 2/2/18.
3
+//
4
+
5
+#include <QtCore/QTimer>
6
+#include "dns-client/QDnsClient.h"
7
+
8
+namespace QDns
9
+{
10
+    namespace Client
11
+    {
12
+        QDnsClient::QDnsClient(const QHostAddress &host, quint16 port)
13
+            : m_host(host)
14
+            , m_port(port)
15
+            , m_queryTimeout(5000)
16
+            , m_state(ClientState::Disconnected)
17
+        {
18
+        }
19
+
20
+        const QHostAddress &QDnsClient::getHost() const
21
+        {
22
+            return m_host;
23
+        }
24
+
25
+        quint16 QDnsClient::getPort() const
26
+        {
27
+            return m_port;
28
+        }
29
+
30
+        quint32 QDnsClient::getQueryTimeout() const
31
+        {
32
+            return m_queryTimeout;
33
+        }
34
+
35
+        void QDnsClient::setQueryTimeout(quint32 queryTimeout)
36
+        {
37
+            m_queryTimeout = queryTimeout;
38
+        }
39
+
40
+        QDnsClient::ClientState QDnsClient::getState() const
41
+        {
42
+            return m_state;
43
+        }
44
+
45
+        void QDnsClient::query(QDns::Base::QDnsPacket packet)
46
+        {
47
+            if (m_state != ClientState::Connected)
48
+            {
49
+                emit onError(OnErrorParams
50
+                                     {
51
+                                             0,
52
+                                             ErrorCode::NotConnected,
53
+                                             QAbstractSocket::UnknownSocketError,
54
+                                             QDns::Base::QDnsPacket(""),
55
+                                             QByteArray(),
56
+                                             packet,
57
+                                             m_host,
58
+                                             m_port
59
+                                     });
60
+                return;
61
+            }
62
+            if (packet.getHeader().getId() == 0)
63
+            {
64
+                auto header = packet.getHeader();
65
+                header.setId((quint16) qrand());
66
+                packet.setHeader(header);
67
+            }
68
+            m_pendingQueries.insert(packet.getHeader().getId(), packet);
69
+
70
+            auto timer = new QTimer(this);
71
+            connect(timer, &QTimer::timeout, [this, packet]()
72
+            {
73
+                if (m_pendingQueries.contains(packet.getHeader().getId()))
74
+                {
75
+                    emit onError(OnErrorParams
76
+                                         {
77
+                                                 0,
78
+                                                 ErrorCode::Timeout,
79
+                                                 QAbstractSocket::UnknownSocketError,
80
+                                                 packet,
81
+                                                 QByteArray(),
82
+                                                 QDns::Base::QDnsPacket(""),
83
+                                                 m_host,
84
+                                                 m_port
85
+                                         });
86
+                }
87
+            });
88
+            timer->setSingleShot(true);
89
+            timer->start(m_queryTimeout);
90
+
91
+            doQuery(packet);
92
+        }
93
+
94
+        void QDnsClient::processData(QDnsClient::ProcessDataParams params)
95
+        {
96
+            QDns::Base::QDnsPacket packet(params.data);
97
+            if (packet.isValid())
98
+            {
99
+                ErrorCode error = ErrorCode::Unknown;
100
+                QDns::Base::QDnsPacket query("");
101
+                if (!m_pendingQueries.contains(packet.getHeader().getId()))
102
+                {
103
+                    error = ErrorCode::UnknownId;
104
+                }
105
+                else
106
+                {
107
+                    query = m_pendingQueries[packet.getHeader().getId()];
108
+                    m_pendingQueries.remove(packet.getHeader().getId());
109
+                }
110
+                if (packet.getHeader().isQuery())
111
+                {
112
+                    error = ErrorCode::NotAnswer;
113
+                }
114
+                if (error == ErrorCode::Unknown)
115
+                {
116
+                    emit onAnswer(OnAnswerParams
117
+                                          {
118
+                                                  0,
119
+                                                  query,
120
+                                                  packet,
121
+                                                  params.senderAddress,
122
+                                                  params.senderPort
123
+                                          });
124
+                }
125
+                else
126
+                {
127
+                    emit onError(OnErrorParams
128
+                                         {
129
+                                                 0,
130
+                                                 error,
131
+                                                 QAbstractSocket::UnknownSocketError,
132
+                                                 query,
133
+                                                 params.data,
134
+                                                 packet,
135
+                                                 params.senderAddress,
136
+                                                 params.senderPort
137
+                                         });
138
+                }
139
+            }
140
+            else
141
+            {
142
+                emit onError(OnErrorParams
143
+                                     {
144
+                                             0,
145
+                                             ErrorCode::InvalidData,
146
+                                             QAbstractSocket::UnknownSocketError,
147
+                                             QDns::Base::QDnsPacket(""),
148
+                                             params.data,
149
+                                             QDns::Base::QDnsPacket(""),
150
+                                             params.senderAddress,
151
+                                             params.senderPort
152
+                                     });
153
+            }
154
+        }
155
+
156
+        void QDnsClient::setState(QDnsClient::ClientState state)
157
+        {
158
+            m_state = state;
159
+            emit onStateChanged(OnStateChangedParams
160
+                                        {
161
+                                                0,
162
+                                                m_state
163
+                                        });
164
+        }
165
+    }
166
+}

+ 106
- 0
src/modules/dns-client/src/QDnsTcpClient.cpp Näytä tiedosto

@@ -0,0 +1,106 @@
1
+//
2
+// Created by robin on 2/7/18.
3
+//
4
+
5
+#include "dns-client/QDnsTcpClient.h"
6
+
7
+namespace QDns
8
+{
9
+    namespace Client
10
+    {
11
+        QDnsTcpClient::QDnsTcpClient(const QHostAddress &host, quint16 port)
12
+            : QDnsClient(host, port)
13
+            , m_socket(new QTcpSocket(this))
14
+            , m_size(0)
15
+        {
16
+            connect(m_socket, &QTcpSocket::readyRead, this, &QDnsTcpClient::onSocketReadyRead);
17
+            connect(m_socket, &QTcpSocket::stateChanged, this, &QDnsTcpClient::onSocketStateChanged);
18
+            connect(m_socket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(onSocketError(QAbstractSocket::SocketError)));
19
+        }
20
+
21
+        void QDnsTcpClient::connectToHost()
22
+        {
23
+            m_socket->connectToHost(m_host, m_port);
24
+        }
25
+
26
+        void QDnsTcpClient::disconnectFromHost()
27
+        {
28
+            m_socket->disconnectFromHost();
29
+        }
30
+
31
+        void QDnsTcpClient::doQuery(QDns::Base::QDnsPacket packet)
32
+        {
33
+            auto answerRaw = packet.serialize(); // TODO split packet if too big (>65535)
34
+            QByteArray size;
35
+            QDataStream dataStream(&size, QIODevice::WriteOnly);
36
+            dataStream << (quint16) answerRaw.size();
37
+            m_socket->write(size.constData(), size.size());
38
+            m_socket->write(answerRaw.constData(), answerRaw.size());
39
+        }
40
+
41
+        void QDnsTcpClient::onSocketReadyRead()
42
+        {
43
+            m_buffer.append(m_socket->readAll());
44
+
45
+            bool readAgain;
46
+
47
+            do
48
+            {
49
+                readAgain = false;
50
+                if (m_buffer.size() > 2 && m_size == 0)
51
+                {
52
+                    QDataStream dataStream(m_buffer);
53
+                    dataStream >> m_size;
54
+                }
55
+                if (m_size != 0 && m_buffer.size() >= m_size + 2)
56
+                {
57
+                    auto data = m_buffer.mid(2, m_size);
58
+                    m_buffer.remove(0, m_size + 2);
59
+                    m_size = 0;
60
+                    readAgain = m_buffer.size() != 0;
61
+                    processData(ProcessDataParams
62
+                                        {
63
+                                                data,
64
+                                                m_socket->peerAddress(),
65
+                                                m_socket->peerPort()
66
+                                        });
67
+                }
68
+            } while (readAgain);
69
+        }
70
+
71
+        void QDnsTcpClient::onSocketStateChanged(QAbstractSocket::SocketState socketState)
72
+        {
73
+            if (socketState == QAbstractSocket::SocketState::HostLookupState)
74
+            {
75
+                setState(ClientState::Connecting);
76
+            }
77
+            else if (socketState == QAbstractSocket::SocketState::ConnectedState)
78
+            {
79
+                setState(ClientState::Connected);
80
+            }
81
+            else if (socketState == QAbstractSocket::SocketState::ClosingState)
82
+            {
83
+                setState(ClientState::Disconnecting);
84
+            }
85
+            else if (socketState == QAbstractSocket::SocketState::UnconnectedState)
86
+            {
87
+                setState(ClientState::Disconnected);
88
+            }
89
+        }
90
+
91
+        void QDnsTcpClient::onSocketError(QAbstractSocket::SocketError error)
92
+        {
93
+            emit onError(OnErrorParams
94
+                                 {
95
+                                         0,
96
+                                         SocketError,
97
+                                         error,
98
+                                         QDns::Base::QDnsPacket(""),
99
+                                         QByteArray(),
100
+                                         QDns::Base::QDnsPacket(""),
101
+                                         m_host,
102
+                                         m_port
103
+                                 });
104
+        }
105
+    }
106
+}

+ 70
- 0
src/modules/dns-client/src/QDnsUdpClient.cpp Näytä tiedosto

@@ -0,0 +1,70 @@
1
+//
2
+// Created by robin on 2/2/18.
3
+//
4
+
5
+#include "dns-client/QDnsUdpClient.h"
6
+
7
+namespace QDns
8
+{
9
+    namespace Client
10
+    {
11
+        QDnsUdpClient::QDnsUdpClient(const QHostAddress &host, quint16 port)
12
+            : QDnsClient(host, port)
13
+            , m_socket(new QUdpSocket(this))
14
+        {
15
+            connect(m_socket, &QUdpSocket::readyRead, this, &QDnsUdpClient::onReadyRead);
16
+            connect(m_socket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(onSocketError(QAbstractSocket::SocketError)));
17
+        }
18
+
19
+        void QDnsUdpClient::connectToHost()
20
+        {
21
+            setState(ClientState::Connecting);
22
+            setState(ClientState::Connected);
23
+        }
24
+
25
+        void QDnsUdpClient::disconnectFromHost()
26
+        {
27
+            setState(ClientState::Disconnecting);
28
+            setState(ClientState::Disconnected);
29
+        }
30
+
31
+        void QDnsUdpClient::doQuery(QDns::Base::QDnsPacket packet)
32
+        {
33
+            m_socket->writeDatagram(packet.serialize(), m_host, m_port);
34
+        }
35
+
36
+        void QDnsUdpClient::onReadyRead()
37
+        {
38
+            while (m_socket->hasPendingDatagrams())
39
+            {
40
+                QByteArray datagram;
41
+                datagram.resize((int) m_socket->pendingDatagramSize());
42
+                QHostAddress senderAddress;
43
+                quint16 senderPort;
44
+
45
+                m_socket->readDatagram(datagram.data(), datagram.size(), &senderAddress, &senderPort); // TODO check sender and port? May not answer from same ip?
46
+                processData(ProcessDataParams
47
+                                    {
48
+                                            datagram,
49
+                                            senderAddress,
50
+                                            senderPort
51
+                                    });
52
+            }
53
+        }
54
+
55
+        void QDnsUdpClient::onSocketError(QAbstractSocket::SocketError error)
56
+        {
57
+            emit onError(OnErrorParams
58
+                                 {
59
+                                         0,
60
+                                         SocketError,
61
+                                         error,
62
+                                         QDns::Base::QDnsPacket(""),
63
+                                         QByteArray(),
64
+                                         QDns::Base::QDnsPacket(""),
65
+                                         m_host,
66
+                                         m_port
67
+                                 });
68
+        }
69
+    }
70
+}

+ 10
- 0
src/modules/dns-client/tests/CMakeLists.txt Näytä tiedosto

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

+ 7
- 0
src/modules/dns-client/tests/main.cpp Näytä tiedosto

@@ -0,0 +1,7 @@
1
+#include <gtest/gtest.h>
2
+
3
+int main(int argc, char* argv[])
4
+{
5
+    ::testing::InitGoogleTest(&argc, argv);
6
+    return RUN_ALL_TESTS();
7
+}

Loading…
Peruuta
Tallenna