QNativeSocketEngine: add code to receive IP header data
authorThiago Macieira <thiago.macieira@intel.com>
Tue, 10 Mar 2015 23:20:20 +0000 (16:20 -0700)
committerThiago Macieira <thiago.macieira@intel.com>
Sat, 22 Aug 2015 22:26:33 +0000 (22:26 +0000)
Change-Id: Iee8cbc07c4434ce9b560ffff13ca466263abcb1b
Reviewed-by: Richard J. Moore <rich@kde.org>
Reviewed-by: Rafael Roquetto <rafael.roquetto@kdab.com>
Reviewed-by: Andrew Knight <andrew.knight@intopalo.com>
mkspecs/common/mac/qplatformdefs.h
src/network/socket/qabstractsocketengine_p.h
src/network/socket/qnativesocketengine.cpp
src/network/socket/qnativesocketengine_unix.cpp
src/network/socket/qnativesocketengine_win.cpp
src/network/socket/qnativesocketengine_winrt.cpp

index 44664933dfdc0ff76e2d7b4031d16f0af40fa0c4..18f62e23f8722617f8fafecb05c6bef9d470f355 100644 (file)
@@ -62,6 +62,7 @@
 #include <sys/socket.h>
 #include <sys/stat.h>
 #include <sys/wait.h>
+#define __APPLE_USE_RFC_3542
 #include <netinet/in.h>
 #ifndef QT_NO_IPV6IFNAME
 #include <net/if.h>
index c485e80f5d53c91e1ac7a3ca2e117a8bfaffc224..d2b5882d189ef43cfdf03813b10af8418e7c269e 100644 (file)
@@ -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
     };
index 901dab5473e6f7fc32012a52b2ee7f55b669078d..c11b889220e922dad9125fe4e8b587a271af1f8b 100644 (file)
@@ -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);
     }
 
 
index 37651dcbc3d6468dc5ba2de783c0743095e07fca..7e794e8c0ff49fd7bcf6f3cfca43ddf33b97056b 100644 (file)
@@ -49,6 +49,9 @@
 #ifdef QT_LINUXBASE
 #include <arpa/inet.h>
 #endif
+#ifdef Q_OS_BSD4
+#include <net/if_dl.h>
+#endif
 
 #if defined QNATIVESOCKETENGINE_DEBUG
 #include <qstring.h>
@@ -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<in6_pktinfo *>(CMSG_DATA(cmsgptr));
+
+                header->destinationAddress.setAddress(reinterpret_cast<quint8 *>(&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<in_pktinfo *>(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<in_addr *>(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<sockaddr_dl *>(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<int *>(CMSG_DATA(cmsgptr));
+            }
+        }
     }
 
 #if defined (QNATIVESOCKETENGINE_DEBUG)
index 9171ee36e04ec8a503d2eda1bae3408f11ceb9fa..2e505d62f0921f84126cec235d07a733b741d7e4 100644 (file)
@@ -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<LPSOCKADDR>(&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<in6_pktinfo *>(WSA_CMSG_DATA(cmsgptr));
+                QHostAddress target(reinterpret_cast<quint8 *>(&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<in_pktinfo *>(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<int *>(WSA_CMSG_DATA(cmsgptr));
+            }
+        }
+    }
 
 #if defined (QNATIVESOCKETENGINE_DEBUG)
     qDebug("QNativeSocketEnginePrivate::nativeReceiveDatagram(%p \"%s\", %li, %s, %i) == %li",
index 4a92ca5a95afdabc1df55515f82aeafdb3135839..025e3e501700a9e2e20736811386ea990b674b53 100644 (file)
@@ -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<LPCWSTR>(addressString.utf16()));
     hostNameFactory->CreateHostName(hostNameRef.Get(), &remoteHost);
 
     ComPtr<IAsyncOperation<IOutputStream *>> streamOperation;
     ComPtr<IOutputStream> stream;
-    const QString portString = QString::number(port);
+    const QString portString = QString::number(header.destinationPort);
     HStringReference portRef(reinterpret_cast<LPCWSTR>(portString.utf16()));
     hr = d->udpSocket()->GetOutputStreamAsync(remoteHost.Get(), portRef.Get(), &streamOperation);
     RETURN_IF_FAILED("Failed to get output stream to socket", return -1);