QNativeSocketEngine: add code to receive IP header data

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>
This commit is contained in:
Thiago Macieira 2015-03-10 16:20:20 -07:00
parent ef05ad0ac5
commit abe269bb72
6 changed files with 170 additions and 13 deletions

View 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>

View 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
};

View File

@ -413,15 +413,20 @@ bool QNativeSocketEngine::initialize(QAbstractSocket::SocketType socketType, QAb
return false;
}
if (socketType == QAbstractSocket::UdpSocket) {
// Set the broadcasting flag if it's a UDP socket.
if (socketType == QAbstractSocket::UdpSocket
&& !setOption(BroadcastSocketOption, 1)) {
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);
}
// Make sure we receive out-of-band data
if (socketType == QAbstractSocket::TcpSocket

View 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)

View 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",

View 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);