1// Copyright (C) 2017 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3// Qt-Security score:critical reason:data-parser
4
5#include "qmodbustcpserver.h"
6#include "qmodbustcpserver_p.h"
7
8#include <QtCore/qurl.h>
9
10QT_BEGIN_NAMESPACE
11
12/*!
13 \class QModbusTcpServer
14 \inmodule QtSerialBus
15 \since 5.8
16
17 \brief The QModbusTcpServer class represents a Modbus server that uses a
18 TCP server for its communication with the Modbus client.
19
20 Communication via Modbus requires the interaction between a single Modbus
21 client instance and single Modbus server. This class provides the Modbus
22 server implementation via a TCP server.
23
24 Modbus TCP networks can have multiple servers. Servers are read/written by
25 a client device represented by \l QModbusTcpClient.
26*/
27
28/*!
29 Constructs a QModbusTcpServer with the specified \a parent. The
30 \l serverAddress preset is \c 255.
31*/
32QModbusTcpServer::QModbusTcpServer(QObject *parent)
33 : QModbusServer(*new QModbusTcpServerPrivate, parent)
34{
35 Q_D(QModbusTcpServer);
36 d->setupTcpServer();
37 setServerAddress(0xff);
38}
39
40/*!
41 Destroys the QModbusTcpServer instance.
42*/
43QModbusTcpServer::~QModbusTcpServer()
44{
45 close();
46}
47
48/*!
49 \internal
50*/
51QModbusTcpServer::QModbusTcpServer(QModbusTcpServerPrivate &dd, QObject *parent)
52 : QModbusServer(dd, parent)
53{
54 Q_D(QModbusTcpServer);
55 d->setupTcpServer();
56}
57
58/*!
59 \reimp
60*/
61bool QModbusTcpServer::open()
62{
63 if (state() == QModbusDevice::ConnectedState)
64 return true;
65
66 Q_D(QModbusTcpServer);
67 if (d->m_tcpServer->isListening())
68 return false;
69
70 const QUrl url = QUrl::fromUserInput(userInput: d->m_networkAddress + QStringLiteral(":")
71 + QString::number(d->m_networkPort));
72
73 if (!url.isValid()) {
74 setError(errorText: tr(s: "Invalid connection settings for TCP communication specified."),
75 error: QModbusDevice::ConnectionError);
76 qCWarning(QT_MODBUS) << "(TCP server) Invalid host:" << url.host() << "or port:"
77 << url.port();
78 return false;
79 }
80
81 if (d->m_tcpServer->listen(address: QHostAddress(url.host()), port: quint16(url.port())))
82 setState(QModbusDevice::ConnectedState);
83 else
84 setError(errorText: d->m_tcpServer->errorString(), error: QModbusDevice::ConnectionError);
85
86 return state() == QModbusDevice::ConnectedState;
87}
88
89/*!
90 \reimp
91*/
92void QModbusTcpServer::close()
93{
94 if (state() == QModbusDevice::UnconnectedState)
95 return;
96
97 Q_D(QModbusTcpServer);
98
99 if (d->m_tcpServer->isListening())
100 d->m_tcpServer->close();
101
102 const auto childSockets =
103 d->m_tcpServer->findChildren<QTcpSocket *>(options: Qt::FindDirectChildrenOnly);
104 for (auto socket : childSockets)
105 socket->disconnectFromHost();
106
107 setState(QModbusDevice::UnconnectedState);
108}
109
110/*!
111 \reimp
112
113 Processes the Modbus client request specified by \a request and returns a
114 Modbus response.
115
116 The following Modbus function codes are filtered out as they are serial
117 line only according to the Modbus Application Protocol Specification 1.1b:
118 \list
119 \li \l QModbusRequest::ReadExceptionStatus
120 \li \l QModbusRequest::Diagnostics
121 \li \l QModbusRequest::GetCommEventCounter
122 \li \l QModbusRequest::GetCommEventLog
123 \li \l QModbusRequest::ReportServerId
124 \endlist
125 A request to the TCP server will be answered with a Modbus exception
126 response with the exception code QModbusExceptionResponse::IllegalFunction.
127*/
128QModbusResponse QModbusTcpServer::processRequest(const QModbusPdu &request)
129{
130 switch (request.functionCode()) {
131 case QModbusRequest::ReadExceptionStatus:
132 case QModbusRequest::Diagnostics:
133 case QModbusRequest::GetCommEventCounter:
134 case QModbusRequest::GetCommEventLog:
135 case QModbusRequest::ReportServerId:
136 return QModbusExceptionResponse(request.functionCode(),
137 QModbusExceptionResponse::IllegalFunction);
138 default:
139 break;
140 }
141 return QModbusServer::processRequest(request);
142}
143
144/*!
145 Installs an \a observer that can be used to obtain notifications when a
146 new TCP client connects to this server instance. In addition, the \a observer
147 can be used to reject the incoming TCP connection.
148
149 QModbusTcpServer takes ownership of the given \a observer. Any previously set
150 observer will be deleted. The observer can be uninstalled by calling this
151 function with \c nullptr as parameter.
152
153 \sa QModbusTcpConnectionObserver
154 \since 5.13
155*/
156void QModbusTcpServer::installConnectionObserver(QModbusTcpConnectionObserver *observer)
157{
158 Q_D(QModbusTcpServer);
159
160 d->m_observer.reset(p: observer);
161}
162
163/*!
164 \class QModbusTcpConnectionObserver
165 \inmodule QtSerialBus
166 \since 5.13
167
168 \brief The QModbusTcpConnectionObserver class represents the interface for
169 objects that can be passed to \l QModbusTcpServer::installConnectionObserver.
170
171 The interface must be implemented by the developer to be able to monitor
172 every incoming TCP connection from another Modbus client.
173
174 \sa QModbusTcpServer::installConnectionObserver
175*/
176
177QModbusTcpConnectionObserver::~QModbusTcpConnectionObserver()
178{
179}
180
181/*!
182 \fn bool QModbusTcpConnectionObserver::acceptNewConnection(QTcpSocket *newClient)
183
184 This function is a callback for every incoming TCP connection. The user should
185 provide \a newClient to receive a notification when a new client connection
186 is established and to determine whether the connection is to be accepted.
187
188 The function should return \c true if the connection is to be accepted. Otherwise,
189 the socket is closed/rejected.
190*/
191
192/*!
193 \fn void QModbusTcpServer::modbusClientDisconnected(QTcpSocket *modbusClient)
194
195 This signal is emitted when a current TCP based \a modbusClient disconnects
196 from this Modbus TCP server. Note that there might be several TCP clients
197 connected at the same time.
198
199 Notifications on incoming new connections can be received by installing a
200 QModbusTcpConnectionObserver via \l installConnectionObserver().
201
202 \sa installConnectionObserver
203 \since 5.13
204*/
205
206QT_END_NAMESPACE
207

source code of qtserialbus/src/serialbus/qmodbustcpserver.cpp