summaryrefslogtreecommitdiffstats
path: root/src/knx/netip/qknxnetipsearchresponse.cpp
blob: 42adcb2e7eb0796c6ff635c87dd4d5b50a75993f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
/******************************************************************************
**
** Copyright (C) 2018 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtKnx module.
**
** $QT_BEGIN_LICENSE:GPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 or (at your option) any later version
** approved by the KDE Free Qt Foundation. The licenses are as published by
** the Free Software Foundation and appearing in the file LICENSE.GPL3
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
******************************************************************************/

#include "qknxbuilderdata_p.h"
#include "qknxnetipsearchresponse.h"

QT_BEGIN_NAMESPACE

/*!
    \class QKnxNetIpSearchResponseProxy

    \inmodule QtKnx
    \ingroup qtknx-netip

    \brief The QKnxNetIpSearchResponseProxy class provides the means to read a
    search response from the generic \l QKnxNetIpFrame class and to create a
    KNXnet/IP frame based on the information.

    In networks where IP addresses are assigned at runtime, via BootP or DHCP,
    for example, it is important to be able to discover KNXnet/IP servers within
    a subnetwork and to establish connections to them without manual input.

    A KNXnet/IP client sends a search request frame, via multicast using its
    discovery endpoint. All KNXnet/IP servers that receive the request
    immediately send a search response using the host address protocol
    information (HPAI) of the client's discovery endpoint. The search response
    frame contains the HPAI of the server's control endpoint for all further
    communication.

    In addition, a search response can contain information about the server
    hardware and the supported service families.

    In most programs, this class will not be used directly. Instead, the
    \l QKnxNetIpServerDiscoveryAgent and \l QKnxNetIpServerInfo are provided to
    discover KNXnet/IP servers on the network.

    \note When using QKnxNetIpSearchResponseProxy, care must be taken to
    ensure that the referenced KNXnet/IP frame outlives the proxy on all code
    paths, lest the proxy ends up referencing deleted data.

    The following code sample illustrates how to read the search response
    information sent by a KNXnet/IP server:

    \code
        auto netIpFrame = QKnxNetIpFrame::fromBytes(...);

        const QKnxNetIpSearchResponseProxy proxy(netIpFrame);
        if (!proxy.isValid())
            return

        auto netIpHpai = proxy.controlEndpoint();
        auto hardwareNetIpDib = proxy.deviceHardware();
        auto supportedFamillies = proxy.supportedFamilies();
        // ...
    \endcode

    \sa builder(), QKnxNetIpSearchRequestProxy, {Qt KNXnet/IP Connection Classes}
*/

/*!
    \fn QKnxNetIpSearchResponseProxy::QKnxNetIpSearchResponseProxy()
    \internal
*/

/*!
    \fn QKnxNetIpSearchResponseProxy::~QKnxNetIpSearchResponseProxy()
    \internal
*/

/*!
    \fn QKnxNetIpSearchResponseProxy::QKnxNetIpSearchResponseProxy(const QKnxNetIpFrame &&)
    \internal
*/

/*!
    Constructs a proxy object to read the search response information carried
    by the specified KNXnet/IP frame \a frame.
*/
QKnxNetIpSearchResponseProxy::QKnxNetIpSearchResponseProxy(const QKnxNetIpFrame &frame)
    : m_frame(frame)
{}

/*!
    Returns the control endpoint of the KNXnet/IP server.
*/
QKnxNetIpHpai QKnxNetIpSearchResponseProxy::controlEndpoint() const
{
    return QKnxNetIpHpai::fromBytes(m_frame.constData(), 0);
}

/*!
    Returns information about the KNXnet/IP server hardware.
*/
QKnxNetIpDib QKnxNetIpSearchResponseProxy::deviceHardware() const
{
    if (!isExtended())
        return QKnxNetIpDib::fromBytes(m_frame.constData(), 8);

    const auto variable = variableDibs();
    for (const auto &dib : variable) {
        if (dib.code() == QKnxNetIp::DescriptionType::DeviceInfo)
            return dib;
    }
    return {};
}

/*!
    Returns information about the service families that the KNXnet/IP server
    supports.

    \note The returned service families must be allowed in a search response. A
    service family higher than or equal to the security family is not allowed
    in the case of a search response. In an extended search response there is
    no such restriction.
*/
QKnxNetIpDib QKnxNetIpSearchResponseProxy::supportedFamilies() const
{
    if (!isExtended())
        return QKnxNetIpDib::fromBytes(m_frame.constData(), 62);

    const auto variable = variableDibs();
    for (const auto &dib : variable) {
        if (dib.code() == QKnxNetIp::DescriptionType::SupportedServiceFamilies)
            return dib;
    }
    return {};
}

/*!
    \since 5.12

    Returns the list of optional KNXnet/IP server device information block
    (DIB) structures of the extended search response frame. The function
    therefor will remove the mandatory device hardware and service family DIB.

    The list can be empty if no optional structures are present or in
    case of an error while extracting the optional DIBs.

    \note The function does not perform validity checks on the
    \l QKnxNetIpFrame used to create the description response proxy object.
*/
QList<QKnxNetIpDib> QKnxNetIpSearchResponseProxy::optionalDibs() const
{
    auto variable = variableDibs();
    variable.erase(std::remove_if(variable.begin(), variable.end(), [](const QKnxNetIpDib &dib) {
        return (dib.code() == QKnxNetIp::DescriptionType::DeviceInfo)
            || (dib.code() == QKnxNetIp::DescriptionType::SupportedServiceFamilies);
    }), variable.end());
    return variable;
}

/*!
    \since 5.12

    Returns the list of KNXnet/IP server device information blocks (DIBs)
    structure of the extended search response frame. The list can be empty
    if no such structures are present or in case of an error while extracting
    the optional DIBs.

    \note The function does not perform validity checks on the
    \l QKnxNetIpFrame used to create the description response proxy object.
*/
QList<QKnxNetIpDib> QKnxNetIpSearchResponseProxy::variableDibs() const
{
    if (!isExtended())
         return {};

    const auto &data = m_frame.constData();
    quint16 index = 8;

    QList<QKnxNetIpDib> dibs;
    while (index < data.size()) {
        auto dib = QKnxNetIpDib::fromBytes(data, index);
        if (!dib.isValid())
            return {};
        dibs.append(dib);
        index += dib.size(); // advance of total size of last read DIB
    }
    return dibs;
}

/*!
    Returns \c true if the frame contains initialized values and is in itself
    valid, otherwise returns \c false. A valid KNXnet/IP frame consists of
    at least a valid header and a size in bytes corresponding to the total size
    of the KNXnet/IP frame header.

    \sa QKnxNetIpFrameHeader::totalSize()
*/
bool QKnxNetIpSearchResponseProxy::isValid() const
{
    const auto serviceType = m_frame.serviceType();
    return m_frame.isValid() && m_frame.size() >= 70 && (m_frame.size() % 2 == 0)
        && (serviceType == QKnxNetIp::ServiceType::SearchResponse
            || serviceType == QKnxNetIp::ServiceType::ExtendedSearchResponse)
        && controlEndpoint().code() == QKnxNetIp::HostProtocol::UDP_IPv4;
}

/*!
    \since 5.12

    Returns \c true if the frame service type is an extended search response,
    otherwise returns \c false.
*/
bool QKnxNetIpSearchResponseProxy::isExtended() const
{
    return (m_frame.serviceType() == QKnxNetIp::ServiceType::ExtendedSearchResponse);
}

/*!
    Returns a builder object to create a KNXnet/IP search response frame.
*/
QKnxNetIpSearchResponseProxy::Builder QKnxNetIpSearchResponseProxy::builder()
{
    return QKnxNetIpSearchResponseProxy::Builder();
}

/*!
    \since 5.12

    Returns a builder object to create a KNXnet/IP extended search response
    frame.
*/
QKnxNetIpSearchResponseProxy::ExtendedBuilder QKnxNetIpSearchResponseProxy::extendedBuilder()
{
    return QKnxNetIpSearchResponseProxy::ExtendedBuilder();
}


/*!
    \class QKnxNetIpSearchResponseProxy::Builder

    \inmodule QtKnx
    \inheaderfile QKnxNetIpSearchResponseProxy

    \brief The QKnxNetIpSearchResponseProxy::Builder class provides the means to
    create a KNXnet/IP search response frame.

    A search response frame contains the host address protocol information
    (HPAI) of the KNXnet/IP server's control endpoint to use for communication
    with a KNXnet/IP client.

    In addition, a search response can contain information about the server
    hardware and the supported service families.

    In most programs, this class will not be used directly. Instead, the
    \l QKnxNetIpServerDiscoveryAgent and \l QKnxNetIpServerInfo are provided to
    discover KNXnet/IP servers on the network.

    The common way to create a a search response is:

    \code
        QKnxNetIpHpai controlEndpoint;
        QKnxNetIpDib deviceHardware, supportedFamillies;
        auto netIpFrame = QKnxNetIpSearchResponseProxy::builder()
            .setControlEndpoint(controlEndpoint)
            .setSupportedFamilies(deviceHardware)
            .setDeviceHardware(supportedFamillies)
            .create();
    \endcode

    \sa QKnxNetIpHpaiProxy::builder(), QKnxNetIpServiceFamiliesDibProxy::builder(),
    QKnxNetIpDeviceDibProxy::builder()

    Typically, after discovering a KNXnet/IP server, the KNXnet/IP client sends
    a description request, \l QKnxNetIpDescriptionRequestProxy, through a
    unicast or point-to-point connection to all control endpoints of the
    KNXnet/IP server.
*/

/*!
    Sets the control endpoint of the KNXnet/IP server to \a hpai and returns a
    reference to the builder.
*/
QKnxNetIpSearchResponseProxy::Builder &
    QKnxNetIpSearchResponseProxy::Builder::setControlEndpoint(const QKnxNetIpHpai &hpai)
{
    m_hpai = hpai;
    return *this;
}

/*!
    Sets the KNXnet/IP server device information block (DIB) to \a ddib and
    returns a reference to the builder.
*/
QKnxNetIpSearchResponseProxy::Builder &
    QKnxNetIpSearchResponseProxy::Builder::setDeviceHardware(const QKnxNetIpDib &ddib)
{
    if (QKnxNetIpDeviceDibProxy(ddib).isValid())
        m_ddib = ddib;
    return *this;
}

/*!
    Sets the device families supported by the KNXnet/IP server to \a sdib and
    returns a reference to the builder.

    \note A service family higher than or equal to the security is not allowed
    in a search response and it shall not be set in the builder if it is passed
    in via this method.
*/
QKnxNetIpSearchResponseProxy::Builder &
    QKnxNetIpSearchResponseProxy::Builder::setSupportedFamilies(const QKnxNetIpDib &sdib)
{
    QKnxNetIpServiceFamiliesDibProxy supFamily(sdib);
    if (!supFamily.isValid())
            return *this;

    for (const auto &serviceInfo : supFamily.serviceInfos()) {
        if (serviceInfo.ServiceFamily >= QKnxNetIp::ServiceFamily::Security)
            return *this;
    }

    m_sdib = sdib;
    return *this;
}

/*!
    Creates and returns a KNXnet/IP search response frame.

    \note The returned frame may be invalid depending on the values used during
    setup.

    \sa isValid()
*/
QKnxNetIpFrame QKnxNetIpSearchResponseProxy::Builder::create() const
{
    return { QKnxNetIp::ServiceType::SearchResponse, m_hpai.bytes() + m_ddib.bytes() + m_sdib
        .bytes() };
}
/*!
    \class QKnxNetIpSearchResponseProxy::ExtendedBuilder

    \since 5.12
    \inmodule QtKnx
    \inheaderfile QKnxNetIpSearchResponseProxy

    \brief The QKnxNetIpSearchResponseProxy::ExtendedBuilder class provides
    the means to create a KNXnet/IP extended search response.

    The KNXnet/IP server sends the extended search response frame as an
    answer to a received extended search request frame. It is addressed
    to the KNXnet/IP client’s discovery endpoint using the host address
    protocol information (HPAI) included in the received extended search
    request frame. The HPAI of the KNXnet/IP server’s own control endpoint
    is carried in the KNXnet/IP body of the extended search response frame
    along with the description of the device hardware and the supported service
    families. If the KNXnet/IP server supports more than one KNX connection,
    the KNXnet/IP server announces each of its own control endpoints in
    a single extended search response frame. KNXnet/IP servers supporting
    TCP only report the UDP address of their control endpoint in the
    extended search response frame. The KNXnet/IP server reports the
    data information blocks (DIBs) in the response in any order. Each DIB
    is present only once in the response.

    The common way to create an extended search response is:

    \code
        QKnxNetIpHpai controlEndpoint;
        QKnxNetIpDib deviceHardware, supportedFamillies;

        QSet<QKnxNetIpDib> optDibs = ...;

        auto netIpFrame = QKnxNetIpSearchResponseProxy::extendedBuilder()
            .setControlEndpoint(controlEndpoint)
            .setSupportedFamilies(deviceHardware)
            .setDeviceHardware(supportedFamillies)
            .setOptionalDibs(optDibs)
            .create();
    \endcode


    \sa QKnxNetIpSearchResponseProxy::Builder, QKnxNetIpHpaiProxy::Builder,
    QKnxNetIpDeviceDibProxy::Builder, QKnxNetIpServiceFamiliesDibProxy::Builder
*/

/*!
    Creates an extended search response builder.
*/
QKnxNetIpSearchResponseProxy::ExtendedBuilder::ExtendedBuilder()
    : d_ptr(new QKnxNetIpSearchResponseExtendedBuilderPrivate)
{}

/*!
    Destroys an extended search response builder.
*/
QKnxNetIpSearchResponseProxy::ExtendedBuilder::~ExtendedBuilder() = default;

/*!
    Sets the control endpoint of the KNXnet/IP client to \a hpai and returns a
    reference to the builder.
*/
QKnxNetIpSearchResponseProxy::ExtendedBuilder &
    QKnxNetIpSearchResponseProxy::ExtendedBuilder::setControlEndpoint(const QKnxNetIpHpai &hpai)
{
    d_ptr->m_hpai = hpai;
    return *this;
}

/*!
    Sets the device hardware device information block (DIB) to \a ddib and
    returns a reference to the builder.
*/
QKnxNetIpSearchResponseProxy::ExtendedBuilder &
    QKnxNetIpSearchResponseProxy::ExtendedBuilder::setDeviceHardware(const QKnxNetIpDib &ddib)
{
    if (QKnxNetIpDeviceDibProxy(ddib).isValid())
        d_ptr->m_hardware = ddib;
    return *this;
}

/*!
    Sets the supported families information block (DIB) to \a sdib and returns
    a reference to the builder.
*/
QKnxNetIpSearchResponseProxy::ExtendedBuilder &
    QKnxNetIpSearchResponseProxy::ExtendedBuilder::setSupportedFamilies(const QKnxNetIpDib &sdib)
{
    if (QKnxNetIpServiceFamiliesDibProxy(sdib).isValid())
        d_ptr->m_supFamilies = sdib;
    return *this;
}

/*!
    Sets the optional KNXnet/IP server device information block (DIB) structure
    to \a dibs and returns a reference to the builder.

    \note The device information blocks \a dibs argument may not contain the
    mandatory device hardware DIB and supported families DIB.
    To set the mandatory DIBs use the dedicated setter functions instead.

    \sa setDeviceHardware(), setSupportedFamilies()
*/
QKnxNetIpSearchResponseProxy::ExtendedBuilder &
    QKnxNetIpSearchResponseProxy::ExtendedBuilder::setOptionalDibs(const QSet<QKnxNetIpDib> &dibs)
{
    d_ptr->m_optionalDibs = dibs;
    return *this;
}

/*!
    Creates and returns a KNXnet/IP extended search response frame.

    \note The returned frame may be invalid depending on the values used
    during setup. For the frame to be valid, at least a device hardware
    information block (DIB) and a supported families DIB must be set in the
    builder.

    \sa isValid()
*/
QKnxNetIpFrame QKnxNetIpSearchResponseProxy::ExtendedBuilder::create() const
{
    if (!(QKnxNetIpDeviceDibProxy(d_ptr->m_hardware).isValid()
        && QKnxNetIpServiceFamiliesDibProxy(d_ptr->m_supFamilies).isValid())) {
            return { QKnxNetIp::ServiceType::ExtendedSearchResponse };
    }
    auto dibsBytes = d_ptr->m_hpai.bytes();
    dibsBytes += d_ptr->m_hardware.bytes();
    dibsBytes += d_ptr->m_supFamilies.bytes();

    for (auto &dib : d_ptr->m_optionalDibs) {
        if (dib.code() != QKnxNetIp::DescriptionType::DeviceInfo
            && dib.code() != QKnxNetIp::DescriptionType::SupportedServiceFamilies) {
                dibsBytes += dib.bytes();
        }
    }
    return { QKnxNetIp::ServiceType::ExtendedSearchResponse, dibsBytes };
}

/*!
    Constructs a copy of \a other.
*/
QKnxNetIpSearchResponseProxy::ExtendedBuilder::ExtendedBuilder(const ExtendedBuilder &other)
    : d_ptr(other.d_ptr)
{}

/*!
    Assigns \a other to this builder.
*/
QKnxNetIpSearchResponseProxy::ExtendedBuilder &
    QKnxNetIpSearchResponseProxy::ExtendedBuilder::operator=(const ExtendedBuilder &other)
{
    d_ptr = other.d_ptr;
    return *this;
}

QT_END_NAMESPACE