From abe269bb72233b360bccbc8f54d3f13e8dc10b5a Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Tue, 10 Mar 2015 16:20:20 -0700 Subject: QNativeSocketEngine: add code to receive IP header data Change-Id: Iee8cbc07c4434ce9b560ffff13ca466263abcb1b Reviewed-by: Richard J. Moore Reviewed-by: Rafael Roquetto Reviewed-by: Andrew Knight --- mkspecs/common/mac/qplatformdefs.h | 1 + src/network/socket/qabstractsocketengine_p.h | 12 +++- src/network/socket/qnativesocketengine.cpp | 19 ++++-- src/network/socket/qnativesocketengine_unix.cpp | 87 +++++++++++++++++++++++- src/network/socket/qnativesocketengine_win.cpp | 58 ++++++++++++++++ src/network/socket/qnativesocketengine_winrt.cpp | 6 +- 6 files changed, 170 insertions(+), 13 deletions(-) diff --git a/mkspecs/common/mac/qplatformdefs.h b/mkspecs/common/mac/qplatformdefs.h index 44664933df..18f62e23f8 100644 --- a/mkspecs/common/mac/qplatformdefs.h +++ b/mkspecs/common/mac/qplatformdefs.h @@ -62,6 +62,7 @@ #include #include #include +#define __APPLE_USE_RFC_3542 #include #ifndef QT_NO_IPV6IFNAME #include diff --git a/src/network/socket/qabstractsocketengine_p.h b/src/network/socket/qabstractsocketengine_p.h index c485e80f5d..d2b5882d18 100644 --- a/src/network/socket/qabstractsocketengine_p.h +++ b/src/network/socket/qabstractsocketengine_p.h @@ -63,18 +63,22 @@ class QIpPacketHeader { public: QIpPacketHeader(const QHostAddress &dstAddr = QHostAddress(), quint16 port = 0) - : destinationAddress(dstAddr), destinationPort(port) + : destinationAddress(dstAddr), ifindex(0), hopLimit(-1), destinationPort(port) {} void clear() { senderAddress.clear(); destinationAddress.clear(); + ifindex = 0; + hopLimit = -1; } QHostAddress senderAddress; QHostAddress destinationAddress; + uint ifindex; + qint16 hopLimit; quint16 senderPort; quint16 destinationPort; }; @@ -114,12 +118,16 @@ public: KeepAliveOption, MulticastTtlOption, MulticastLoopbackOption, - TypeOfServiceOption + TypeOfServiceOption, + ReceivePacketInformation, + ReceiveHopLimit }; enum PacketHeaderOption { WantNone = 0, WantDatagramSender, + WantDatagramDestination, + WantDatagramHopLimit, WantAll = 0xff }; diff --git a/src/network/socket/qnativesocketengine.cpp b/src/network/socket/qnativesocketengine.cpp index 901dab5473..c11b889220 100644 --- a/src/network/socket/qnativesocketengine.cpp +++ b/src/network/socket/qnativesocketengine.cpp @@ -413,13 +413,18 @@ bool QNativeSocketEngine::initialize(QAbstractSocket::SocketType socketType, QAb return false; } - // Set the broadcasting flag if it's a UDP socket. - if (socketType == QAbstractSocket::UdpSocket - && !setOption(BroadcastSocketOption, 1)) { - d->setError(QAbstractSocket::UnsupportedSocketOperationError, - QNativeSocketEnginePrivate::BroadcastingInitFailedErrorString); - close(); - return false; + if (socketType == QAbstractSocket::UdpSocket) { + // Set the broadcasting flag if it's a UDP socket. + if (!setOption(BroadcastSocketOption, 1)) { + d->setError(QAbstractSocket::UnsupportedSocketOperationError, + QNativeSocketEnginePrivate::BroadcastingInitFailedErrorString); + close(); + return false; + } + + // Set some extra flags that are interesting to us, but accept failure + setOption(ReceivePacketInformation, 1); + setOption(ReceiveHopLimit, 1); } diff --git a/src/network/socket/qnativesocketengine_unix.cpp b/src/network/socket/qnativesocketengine_unix.cpp index 37651dcbc3..7e794e8c0f 100644 --- a/src/network/socket/qnativesocketengine_unix.cpp +++ b/src/network/socket/qnativesocketengine_unix.cpp @@ -49,6 +49,9 @@ #ifdef QT_LINUXBASE #include #endif +#ifdef Q_OS_BSD4 +#include +#endif #if defined QNATIVESOCKETENGINE_DEBUG #include @@ -202,6 +205,32 @@ static void convertToLevelAndOption(QNativeSocketEngine::SocketOption opt, n = IP_TOS; } break; + case QNativeSocketEngine::ReceivePacketInformation: + if (socketProtocol == QAbstractSocket::IPv6Protocol || socketProtocol == QAbstractSocket::AnyIPProtocol) { + level = IPPROTO_IPV6; + n = IPV6_RECVPKTINFO; + } else if (socketProtocol == QAbstractSocket::IPv4Protocol) { + level = IPPROTO_IP; +#ifdef IP_PKTINFO + n = IP_PKTINFO; +#elif defined(IP_RECVDSTADDR) + // variant found in QNX and FreeBSD; it will get us only the + // destination address, not the interface; we need IP_RECVIF for that. + n = IP_RECVDSTADDR; +#endif + } + break; + case QNativeSocketEngine::ReceiveHopLimit: + if (socketProtocol == QAbstractSocket::IPv6Protocol || socketProtocol == QAbstractSocket::AnyIPProtocol) { + level = IPPROTO_IPV6; + n = IPV6_RECVHOPLIMIT; + } else if (socketProtocol == QAbstractSocket::IPv4Protocol) { +#ifdef IP_RECVTTL // IP_RECVTTL is a non-standard extension supported on some OS + level = IPPROTO_IP; + n = IP_RECVTTL; +#endif + } + break; } } @@ -285,7 +314,7 @@ int QNativeSocketEnginePrivate::option(QNativeSocketEngine::SocketOption opt) co QT_SOCKOPTLEN_T len = sizeof(v); convertToLevelAndOption(opt, socketProtocol, level, n); - if (::getsockopt(socketDescriptor, level, n, (char *) &v, &len) != -1) + if (n != -1 && ::getsockopt(socketDescriptor, level, n, (char *) &v, &len) != -1) return v; return -1; @@ -847,6 +876,9 @@ qint64 QNativeSocketEnginePrivate::nativePendingDatagramSize() const qint64 QNativeSocketEnginePrivate::nativeReceiveDatagram(char *data, qint64 maxSize, QIpPacketHeader *header, QAbstractSocketEngine::PacketHeaderOptions options) { + // we use quintptr to force the alignment + quintptr cbuf[(CMSG_SPACE(sizeof(struct in6_pktinfo)) + CMSG_SPACE(sizeof(int)) + sizeof(quintptr) - 1) / sizeof(quintptr)]; + struct msghdr msg; struct iovec vec; qt_sockaddr aa; @@ -863,6 +895,10 @@ qint64 QNativeSocketEnginePrivate::nativeReceiveDatagram(char *data, qint64 maxS msg.msg_name = &aa; msg.msg_namelen = sizeof(aa); } + if (options & (QAbstractSocketEngine::WantDatagramHopLimit | QAbstractSocketEngine::WantDatagramDestination)) { + msg.msg_control = cbuf; + msg.msg_controllen = sizeof(cbuf); + } ssize_t recvResult = 0; do { @@ -876,6 +912,55 @@ qint64 QNativeSocketEnginePrivate::nativeReceiveDatagram(char *data, qint64 maxS } else if (options != QAbstractSocketEngine::WantNone) { Q_ASSERT(header); qt_socket_getPortAndAddress(&aa, &header->senderPort, &header->senderAddress); + header->destinationPort = localPort; + + // parse the ancillary data + struct cmsghdr *cmsgptr; + for (cmsgptr = CMSG_FIRSTHDR(&msg); cmsgptr != NULL; + cmsgptr = CMSG_NXTHDR(&msg, cmsgptr)) { + if (cmsgptr->cmsg_level == IPPROTO_IPV6 && cmsgptr->cmsg_type == IPV6_PKTINFO + && cmsgptr->cmsg_len >= CMSG_LEN(sizeof(in6_pktinfo))) { + in6_pktinfo *info = reinterpret_cast(CMSG_DATA(cmsgptr)); + + header->destinationAddress.setAddress(reinterpret_cast(&info->ipi6_addr)); + header->ifindex = info->ipi6_ifindex; + if (header->ifindex) + header->destinationAddress.setScopeId(QString::number(info->ipi6_ifindex)); + } + +#ifdef IP_PKTINFO + if (cmsgptr->cmsg_level == IPPROTO_IP && cmsgptr->cmsg_type == IP_PKTINFO + && cmsgptr->cmsg_len >= CMSG_LEN(sizeof(in_pktinfo))) { + in_pktinfo *info = reinterpret_cast(CMSG_DATA(cmsgptr)); + + header->destinationAddress.setAddress(ntohl(info->ipi_addr.s_addr)); + header->ifindex = info->ipi_ifindex; + } +#else +# ifdef IP_RECVDSTADDR + if (cmsgptr->cmsg_level == IPPROTO_IP && cmsgptr->cmsg_type == IP_RECVDSTADDR + && cmsgptr->cmsg_len >= CMSG_LEN(sizeof(in_addr))) { + in_addr *addr = reinterpret_cast(CMSG_DATA(cmsgptr)); + + header->destinationAddress.setAddress(ntohl(addr->s_addr)); + } +# endif +# if defined(IP_RECVIF) && defined(Q_OS_BSD4) + if (cmsgptr->cmsg_level == IPPROTO_IP && cmsgptr->cmsg_type == IP_RECVIF + && cmsgptr->cmsg_len >= CMSG_LEN(sizeof(sockaddr_dl))) { + sockaddr_dl *sdl = reinterpret_cast(CMSG_DATA(cmsgptr)); + + header->ifindex = LLINDEX(sdl); + } +# endif +#endif + + if (cmsgptr->cmsg_len == CMSG_LEN(sizeof(int)) + && ((cmsgptr->cmsg_level == IPPROTO_IPV6 && cmsgptr->cmsg_type == IPV6_HOPLIMIT) + || (cmsgptr->cmsg_level == IPPROTO_IP && cmsgptr->cmsg_type == IP_TTL))) { + header->hopLimit = *reinterpret_cast(CMSG_DATA(cmsgptr)); + } + } } #if defined (QNATIVESOCKETENGINE_DEBUG) diff --git a/src/network/socket/qnativesocketengine_win.cpp b/src/network/socket/qnativesocketengine_win.cpp index 9171ee36e0..2e505d62f0 100644 --- a/src/network/socket/qnativesocketengine_win.cpp +++ b/src/network/socket/qnativesocketengine_win.cpp @@ -59,6 +59,9 @@ QT_BEGIN_NAMESPACE #ifndef IPV6_V6ONLY #define IPV6_V6ONLY 27 #endif +#ifndef IP_HOPLIMIT +#define IP_HOPLIMIT 21 // Receive packet hop limit. +#endif #if defined(QNATIVESOCKETENGINE_DEBUG) @@ -252,6 +255,24 @@ static void convertToLevelAndOption(QNativeSocketEngine::SocketOption opt, n = IP_MULTICAST_LOOP; } break; + case QNativeSocketEngine::ReceivePacketInformation: + if (socketProtocol == QAbstractSocket::IPv6Protocol || socketProtocol == QAbstractSocket::AnyIPProtocol) { + level = IPPROTO_IPV6; + n = IPV6_PKTINFO; + } else if (socketProtocol == QAbstractSocket::IPv4Protocol) { + level = IPPROTO_IP; + n = IP_PKTINFO; + } + break; + case QNativeSocketEngine::ReceiveHopLimit: + if (socketProtocol == QAbstractSocket::IPv6Protocol || socketProtocol == QAbstractSocket::AnyIPProtocol) { + level = IPPROTO_IPV6; + n = IPV6_HOPLIMIT; + } else if (socketProtocol == QAbstractSocket::IPv4Protocol) { + level = IPPROTO_IP; + n = IP_HOPLIMIT; + } + break; } } @@ -1219,6 +1240,10 @@ static int (*const sendmsg)(...) = 0; qint64 QNativeSocketEnginePrivate::nativeReceiveDatagram(char *data, qint64 maxLength, QIpPacketHeader *header, QAbstractSocketEngine::PacketHeaderOptions options) { + union { + char cbuf[WSA_CMSG_SPACE(sizeof(struct in6_pktinfo)) + WSA_CMSG_SPACE(sizeof(int))]; + WSACMSGHDR align; // only to ensure alignment + }; WSAMSG msg; WSABUF buf; qt_sockaddr aa; @@ -1233,6 +1258,10 @@ qint64 QNativeSocketEnginePrivate::nativeReceiveDatagram(char *data, qint64 maxL msg.dwBufferCount = 1; msg.name = reinterpret_cast(&aa); msg.namelen = sizeof(aa); + if (options & (QAbstractSocketEngine::WantDatagramHopLimit | QAbstractSocketEngine::WantDatagramDestination)) { + msg.Control.buf = cbuf; + msg.Control.len = sizeof(cbuf); + } DWORD flags = 0; DWORD bytesRead = 0; @@ -1261,6 +1290,35 @@ qint64 QNativeSocketEnginePrivate::nativeReceiveDatagram(char *data, qint64 maxL qt_socket_getPortAndAddress(socketDescriptor, &aa, &header->senderPort, &header->senderAddress); } + if (ret != -1 && recvmsg) { + // get the ancillary data + WSACMSGHDR *cmsgptr; + for (cmsgptr = WSA_CMSG_FIRSTHDR(&msg); cmsgptr != NULL; + cmsgptr = WSA_CMSG_NXTHDR(&msg, cmsgptr)) { + if (cmsgptr->cmsg_level == IPPROTO_IPV6 && cmsgptr->cmsg_type == IPV6_PKTINFO + && cmsgptr->cmsg_len >= WSA_CMSG_LEN(sizeof(in6_pktinfo))) { + in6_pktinfo *info = reinterpret_cast(WSA_CMSG_DATA(cmsgptr)); + QHostAddress target(reinterpret_cast(&info->ipi6_addr)); + if (info->ipi6_ifindex) + target.setScopeId(QString::number(info->ipi6_ifindex)); + } + if (cmsgptr->cmsg_level == IPPROTO_IP && cmsgptr->cmsg_type == IP_PKTINFO + && cmsgptr->cmsg_len >= WSA_CMSG_LEN(sizeof(in_pktinfo))) { + in_pktinfo *info = reinterpret_cast(WSA_CMSG_DATA(cmsgptr)); + u_long addr; + WSANtohl(socketDescriptor, info->ipi_addr.s_addr, &addr); + QHostAddress target(addr); + if (info->ipi_ifindex) + target.setScopeId(QString::number(info->ipi_ifindex)); + } + + if (cmsgptr->cmsg_len == WSA_CMSG_LEN(sizeof(int)) + && ((cmsgptr->cmsg_level == IPPROTO_IPV6 && cmsgptr->cmsg_type == IPV6_HOPLIMIT) + || (cmsgptr->cmsg_level == IPPROTO_IP && cmsgptr->cmsg_type == IP_TTL))) { + header->hopLimit = *reinterpret_cast(WSA_CMSG_DATA(cmsgptr)); + } + } + } #if defined (QNATIVESOCKETENGINE_DEBUG) qDebug("QNativeSocketEnginePrivate::nativeReceiveDatagram(%p \"%s\", %li, %s, %i) == %li", diff --git a/src/network/socket/qnativesocketengine_winrt.cpp b/src/network/socket/qnativesocketengine_winrt.cpp index 4a92ca5a95..025e3e5017 100644 --- a/src/network/socket/qnativesocketengine_winrt.cpp +++ b/src/network/socket/qnativesocketengine_winrt.cpp @@ -539,7 +539,7 @@ qint64 QNativeSocketEngine::write(const char *data, qint64 len) } qint64 QNativeSocketEngine::readDatagram(char *data, qint64 maxlen, QIpPacketHeader *header, - PacketHeaderOptions options) + PacketHeaderOptions) { Q_D(QNativeSocketEngine); if (d->socketType != QAbstractSocket::UdpSocket || d->pendingDatagrams.isEmpty()) { @@ -577,13 +577,13 @@ qint64 QNativeSocketEngine::writeDatagram(const char *data, qint64 len, const QI HRESULT hr = GetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Networking_HostName).Get(), &hostNameFactory); RETURN_IF_FAILED("Could not obtain hostname factory", return -1); - const QString addressString = addr.toString(); + const QString addressString = header.destinationAddress.toString(); HStringReference hostNameRef(reinterpret_cast(addressString.utf16())); hostNameFactory->CreateHostName(hostNameRef.Get(), &remoteHost); ComPtr> streamOperation; ComPtr stream; - const QString portString = QString::number(port); + const QString portString = QString::number(header.destinationPort); HStringReference portRef(reinterpret_cast(portString.utf16())); hr = d->udpSocket()->GetOutputStreamAsync(remoteHost.Get(), portRef.Get(), &streamOperation); RETURN_IF_FAILED("Failed to get output stream to socket", return -1); -- cgit v1.2.3