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:
parent
ef05ad0ac5
commit
abe269bb72
@ -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>
|
||||
|
@ -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
|
||||
};
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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",
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user