diff options
Diffstat (limited to 'src/network/kernel/qnetworkdatagram.cpp')
-rw-r--r-- | src/network/kernel/qnetworkdatagram.cpp | 535 |
1 files changed, 535 insertions, 0 deletions
diff --git a/src/network/kernel/qnetworkdatagram.cpp b/src/network/kernel/qnetworkdatagram.cpp new file mode 100644 index 0000000000..7a1b168b74 --- /dev/null +++ b/src/network/kernel/qnetworkdatagram.cpp @@ -0,0 +1,535 @@ +/**************************************************************************** +** +** Copyright (C) 2016 Intel Corporation. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtNetwork module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or 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.GPL2 and 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-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qnetworkdatagram.h" +#include "qnetworkdatagram_p.h" + +#ifndef QT_NO_UDPSOCKET + +QT_BEGIN_NAMESPACE + +/*! + \class QNetworkDatagram + \brief The QNetworkDatagram class provides the data and matadata of a UDP datagram. + \since 5.7 + \ingroup network + \inmodule QtNetwork + \reentrant + + QNetworkDatagram can be used with the \l QUdpSocket class to represent the full + information contained in a UDP (User Datagram Protocol) datagram. + QNetworkDatagram encapsulates the following information of a datagram: + \list + \li the payload data; + \li the sender address and port number; + \li the destination address and port number; + \li the remaining hop count limit (on IPv4, this field is usually called "time to live" - TTL); + \li the network interface index the datagram was received on or to be sent on. + \endlist + + QUdpSocket will try to match a common behavior as much as possible on all + operating systems, but not all of the metadata above can be obtained in + some operating systems. Metadata that cannot be set on the datagram when + sending with QUdpSocket::writeDatagram() will be silently discarded. + + Upon reception, the senderAddress() and senderPort() properties contain the + address and port of the peer that sent the datagram, while + destinationAddress() and destinationPort() contain the target that was + contained in the datagram. That is usually an address local to the current + machine, but it can also be an IPv4 broadcast address (such as + "255.255.255.255") or an IPv4 or IPv6 multicast address. Applications may + find it useful to determine if the datagram was sent specifically to this + machine via unicast addressing or whether it was sent to multiple destinations. + + When sending, the senderAddress() and senderPort() should contain the local + address to be used when sending. The sender address must be an address that + is assigned to this machine, which can be obtained using + \l{QNetworkInterface}, and the port number must be the port number that the + socket is bound to. Either field can be left unset and will be filled in by + the operating system with default values. The destinationAddress() and + destinationPort() fields may be set to a target address different from the + one the UDP socket is currently associated with. + + Usually, when sending a datagram in reply to a datagram previously + received, one will set the destinationAddress() to be the senderAddress() + of the incoming datagram and similarly for the port numbers. To facilitate + this common process, QNetworkDatagram provides the function makeReply(). + + The hopCount() function contains, for a received datagram, the remaining + hop count limit for the packet. When sending, it contains the hop count + limit to be set. Most protocols will leave this value set to the default + and let the operating system decide on the best value to be used. + Multicasting over IPv4 often uses this field to indicate the scope of the + multicast group (link-local, local to an organization or global). + + The interfaceIndex() function contains the index of the operating system's + interface that received the packet. This value is the same one that can be + set on a QHostAddress::scopeId() property and matches the + QNetworkInterface::index() property. When sending packets to global + addresses, it is not necessary to set the interface index as the operating + system will choose the correct one using the system routing table. This + property is important when sending datagrams to link-local destinations, + whether unicast or multicast. + + \section1 Feature support + + Some features of QNetworkDatagram are not supported in all operating systems. + Only the address and ports of the remote host (sender in received packets + and destination for outgoing packets) are supported in all systems. On most + operating systems, the other features are supported only for IPv6. Software + should check at runtime whether the rest could be determined for IPv4 + addresses. + + The current feature support is as follows: + + \table + \header \li Operating system \li Local address \li Hop count \li Interface index + \row \li FreeBSD \li Supported \li Supported \li Only for IPv6 + \row \li Linux \li Supported \li Supported \li Supported + \row \li OS X \li Supported \li Supported \li Only for IPv6 + \row \li Other Unix supporting RFC 3542 \li Only for IPv6 \li Only for IPv6 \li Only for IPv6 + \row \li Windows XP and older \li Not supported \li Not supported \li Not supported + \row \li Windows Vista & up \li Supported \li Supported \li Supported + \row \li Windows CE \li Not supported \li Not supported \li Not supported + \row \li Windows RT \li Not supported \li Not supported \li Not supported + \endtable + + \sa QUdpSocket, QNetworkInterface + */ + +/*! + Creates a QNetworkDatagram object with no payload data and undefined destination address. + + The payload can be modified by using setData() and the destination address + can be set with setDestination(). + + If the destination address is left undefined, QUdpSocket::writeDatagram() + will attempt to send the datagram to the address last associated with, by + using QUdpSocket::connectToHost(). + */ +QNetworkDatagram::QNetworkDatagram() + : d(new QNetworkDatagramPrivate) +{ +} + +/*! + Creates a QNetworkDatagram object and sets \a data as the payload data, along with + \a destinationAddress and \a port as the destination address of the datagram. + */ +QNetworkDatagram::QNetworkDatagram(const QByteArray &data, const QHostAddress &destinationAddress, quint16 port) + : d(new QNetworkDatagramPrivate(data, destinationAddress, port)) +{ +} + +/*! + Creates a copy of the \a other datagram, including the payload and metadata. + + To create a datagram suitable for sending in a reply, use QNetworkDatagram::makeReply(); + */ +QNetworkDatagram::QNetworkDatagram(const QNetworkDatagram &other) + : d(new QNetworkDatagramPrivate(*other.d)) +{ +} + +/*! \internal */ +QNetworkDatagram::QNetworkDatagram(QNetworkDatagramPrivate &dd) + : d(&dd) +{ +} + +/*! + Copies the \a other datagram, including the payload and metadata. + + To create a datagram suitable for sending in a reply, use QNetworkDatagram::makeReply(); + */ +QNetworkDatagram &QNetworkDatagram::operator=(const QNetworkDatagram &other) +{ + *d = *other.d; + return *this; +} + +/*! + Clears the payload data and metadata in this QNetworkDatagram object, resetting + them to their default values. + */ +void QNetworkDatagram::clear() +{ + d->data.clear(); + d->header.senderAddress.clear(); + d->header.destinationAddress.clear(); + d->header.hopLimit = -1; + d->header.ifindex = 0; +} + +/*! + \fn QNetworkDatagram::isNull() const + Returns true if this QNetworkDatagram object is null. This function is the + opposite of isValid(). + */ + +/*! + Returns true if this QNetworkDatagram object is valid. A valid QNetworkDatagram + object contains at least one sender or receiver address. Valid datagrams + can contain empty payloads. + */ +bool QNetworkDatagram::isValid() const +{ + return d->header.senderAddress.protocol() != QAbstractSocket::UnknownNetworkLayerProtocol || + d->header.destinationAddress.protocol() != QAbstractSocket::UnknownNetworkLayerProtocol; +} + +/*! + Returns the sender address associated with this datagram. For a datagram + received from the network, it is the address of the peer node that sent the + datagram. For an outgoing datagrams, it is the local address to be used + when sending. + + If no sender address was set on this datagram, the returned object will + report true to QHostAddress::isNull(). + + \sa destinationAddress(), senderPort(), setSender() +*/ +QHostAddress QNetworkDatagram::senderAddress() const +{ + return d->header.senderAddress; +} + +/*! + Returns the destination address associated with this datagram. For a + datagram received from the network, it is the address the peer node sent + the datagram to, which can either be a local address of this machine or a + multicast or broadcast address. For an outgoing datagrams, it is the + address the datagram should be sent to. + + If no destination address was set on this datagram, the returned object + will report true to QHostAddress::isNull(). + + \sa senderAddress(), destinationPort(), setDestination() +*/ +QHostAddress QNetworkDatagram::destinationAddress() const +{ + return d->header.destinationAddress; +} + +/*! + Returns the port number of the sender associated with this datagram. For a + datagram received from the network, it is the port number that the peer + node sent the datagram from. For an outgoing datagram, it is the local port + the datagram should be sent from. + + If no sender address was associated with this datagram, this function + returns -1. + + \sa senderAddress(), destinationPort(), setSender() +*/ +int QNetworkDatagram::senderPort() const +{ + return d->header.senderAddress.protocol() == QAbstractSocket::UnknownNetworkLayerProtocol + ? -1 : d->header.senderPort; +} + +/*! + Returns the port number of the destination associated with this datagram. + For a datagram received from the network, it is the local port number that + the peer node sent the datagram to. For an outgoing datagram, it is the + peer port the datagram should be sent to. + + If no destination address was associated with this datagram, this function + returns -1. + + \sa destinationAddress(), senderPort(), setDestination() +*/ +int QNetworkDatagram::destinationPort() const +{ + return d->header.destinationAddress.protocol() == QAbstractSocket::UnknownNetworkLayerProtocol + ? -1 : d->header.destinationPort; +} + +/*! + Sets the sender address associated with this datagram to be the address \a + address and port number \a port. The sender address and port numbers are + usually set by \l QUdpSocket upon reception, so there's no need to call + this function on a received datagram. + + For outgoing datagrams, this function can be used to set the address the + datagram should carry. The address \a address must usually be one of the + local addresses assigned to this machine, which can be obtained using \l + QNetworkInterface. If left unset, the operating system will choose the most + appropriate address to use given the destination in question. + + The port number \a port must be the port number associated with the socket, + if there is one. The value of 0 can be used to indicate that the operating + system should choose the port number. + + \sa QUdpSocket::writeDatagram(), senderAddress(), senderPort(), setDestination() + */ +void QNetworkDatagram::setSender(const QHostAddress &address, quint16 port) +{ + d->header.senderAddress = address; + d->header.senderPort = port; +} + +/*! + Sets the destination address associated with this datagram to be the + address \a address and port number \a port. The destination address and + port numbers are usually set by \l QUdpSocket upon reception, so there's no + need to call this function on a received datagram. + + For outgoing datagrams, this function can be used to set the address the + datagram should be sent to. It can be the unicast address used to + communicate with the peer or a broadcast or multicast address to send to a + group of devices. + + \sa QUdpSocket::writeDatagram(), destinationAddress(), destinationPort(), setSender() + */ +void QNetworkDatagram::setDestination(const QHostAddress &address, quint16 port) +{ + d->header.destinationAddress = address; + d->header.destinationPort = port; +} + +/*! + Returns the hop count limit associated with this datagram. The hop count + limit is the number of nodes that are allowed to forward the IP packet + before it expires and an error is sent back to the sender of the datagram. + In IPv4, this value is usually known as "time to live" (TTL). + + If this datagram was received from the network, this is the remaining hop + count of the datagram after reception and was decremented by 1 by each node + that forwarded the packet. A value of -1 indicates that the hop limit count + not be obtained. + + If this is an outgoing datagram, this is the value to be set in the IP header + upon sending. A value of -1 indicates the operating system should choose + the value. + + \sa setHopLimit() + */ +int QNetworkDatagram::hopLimit() const +{ + return d->header.hopLimit; +} + +/*! + Sets the hop count limit associated with this datagram to \a count. The hop + count limit is the number of nodes that are allowed to forward the IP + packet before it expires and an error is sent back to the sender of the + datagram. In IPv4, this value is usually known as "time to live" (TTL). + + It is usually not necessary to call this function on datagrams received + from the network. + + If this is an outgoing packet, this is the value to be set in the IP header + upon sending. The valid range for the value is 1 to 255. This function also + accepts a value of -1 to indicate that the operating system should choose + the value. + + \sa hopLimit() + */ +void QNetworkDatagram::setHopLimit(int count) +{ + d->header.hopLimit = count; +} + +/*! + Returns the interface index this datagram is associated with. The interface + index is a positive number that uniquely identifies the network interface + in the operating system. This number matches the value returned by + QNetworkInterface::index() for the interface. + + If this datagram was received from the network, this is the index of the + interface that the packet was received from. If this is an outgoing + datagram, this is the index of the interface that the datagram should be + sent on. + + A value of 0 indicates that the interface index is unknown. + + \sa setInterfaceIndex() + */ +uint QNetworkDatagram::interfaceIndex() const +{ + return d->header.ifindex; +} + +/*! + Sets the interface index this datagram is associated with to \a index. The + interface index is a positive number that uniquely identifies the network + interface in the operating system. This number matches the value returned + by QNetworkInterface::index() for the interface. + + It is usually not necessary to call this function on datagrams received + from the network. + + If this is an outgoing packet, this is the index of the interface the + datagram should be sent on. A value of 0 indicates that the operating + system should choose the interface based on other factors. + + Note that the interface index can also be set with + QHostAddress::setScopeId() for IPv6 destination addresses and then with + setDestination(). If the scope ID set in the destination address and \a + index are different and neither is zero, it is undefined which interface + the operating system will send the datagram on. + + \sa setInterfaceIndex() + */ +void QNetworkDatagram::setInterfaceIndex(uint index) +{ + d->header.ifindex = index; +} + +/*! + Returns the data payload of this datagram. For a datagram received from the + network, it contains the payload of the datagram. For an outgoing datagram, + it is the datagram to be sent. + + Note that datagrams can be transmitted with no data, so the returned + QByteArray may be empty. + + \sa setData() + */ +QByteArray QNetworkDatagram::data() const +{ + return d->data; +} + +/*! + Sets the data payload of this datagram to \a data. It is usually not + necessary to call this function on received datagrams. For outgoing + datagrams, this function sets the data to be sent on the network. + + Since datagrams can empty, an empty QByteArray is a valid value for \a + data. + + \sa data() + */ +void QNetworkDatagram::setData(const QByteArray &data) +{ + d->data = data; +} + +/*! + \fn QNetworkDatagram QNetworkDatagram::makeReply(const QByteArray &data) const + + Creates a new QNetworkDatagram representing a reply to this incoming datagram + and sets the payload data to \a data. This function is a very convenient + way of responding to a datagram back to the original sender. + + Example: + \code + void Server::readPendingDatagrams() + { + while (udpSocket->hasPendingDatagrams()) { + QNetworkDatagram datagram = udpSocket->receiveDatagram(); + QByteArray replyData = processThePayload(datagram.data()); + udpSocket->writeDatagram(datagram.makeReply(replyData)); + } + } + \endcode + + This function is especially convenient since it will automatically copy + parameters from this datagram to the new datagram as appropriate: + + \list + \li this datagram's sender address and port are copied to the new + datagram's destination address and port; + \li this datagram's interface index, if any, is copied to the new + datagram's interface index; + \li this datagram's destination address and port are copied to the new + datagram's sender address and port only if the address is IPv6 + global (non-multicast) address; + \li the hop count limit on the new datagram is reset to the default (-1); + \endlist + + If QNetworkDatagram is modified in a future version of Qt to carry further + metadata, this function will copy that metadata as appropriate. + + This datagram's destination address is not copied if it is an IPv4 address + because it is not possible to tell an IPv4 broadcast address apart from a + regular IPv4 address without an exhaustive search of all addresses assigned + to this machine. Attempting to send a datagram with the sender address + equal to the broadcast address is likely to fail. However, this should not + affect the communication as network interfaces with multiple IPv4 addresses + are uncommon, so the address the operating system will select will likely + be one the peer will understand. + + \note This function comes with both rvalue- and lvalue-reference qualifier + overloads, so it is a good idea to make sure this object is rvalue, if + possible, before calling makeReply, so as to make better use of move + semantics. To achieve that, the example above would use: + \code + udpSocket->writeDatagram(std::move(datagram).makeReply(replyData)); + \endcode + */ + +static bool isNonMulticast(const QHostAddress &addr) +{ + // is it a multicast address? + return !addr.isMulticast(); +} + +QNetworkDatagram QNetworkDatagram::makeReply_helper(const QByteArray &data) const +{ + QNetworkDatagramPrivate *x = new QNetworkDatagramPrivate(data, d->header.senderAddress, d->header.senderPort); + x->header.ifindex = d->header.ifindex; + if (isNonMulticast(d->header.destinationAddress)) { + x->header.senderAddress = d->header.destinationAddress; + x->header.senderPort = d->header.destinationPort; + } + return QNetworkDatagram(*x); +} + +void QNetworkDatagram::makeReply_helper_inplace(const QByteArray &data) +{ + d->data = data; + d->header.hopLimit = -1; + qSwap(d->header.destinationPort, d->header.senderPort); + qSwap(d->header.destinationAddress, d->header.senderAddress); + if (!isNonMulticast(d->header.senderAddress)) + d->header.senderAddress.clear(); +} + +void QNetworkDatagram::destroy(QNetworkDatagramPrivate *d) +{ + Q_ASSUME(d); + delete d; +} + +QT_END_NAMESPACE + +#endif // QT_NO_UDPSOCKET |