summaryrefslogtreecommitdiffstats
path: root/src/network/socket/qnativesocketengine_win.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/network/socket/qnativesocketengine_win.cpp')
-rw-r--r--src/network/socket/qnativesocketengine_win.cpp333
1 files changed, 213 insertions, 120 deletions
diff --git a/src/network/socket/qnativesocketengine_win.cpp b/src/network/socket/qnativesocketengine_win.cpp
index 708be2dea7..9aed0caa25 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)
@@ -168,10 +171,10 @@ static QByteArray qt_prettyDebug(const char *data, int len, int maxLength)
static inline void qt_socket_getPortAndAddress(SOCKET socketDescriptor, const qt_sockaddr *sa, quint16 *port, QHostAddress *address)
{
if (sa->a.sa_family == AF_INET6) {
- const qt_sockaddr_in6 *sa6 = &sa->a6;
+ const sockaddr_in6 *sa6 = &sa->a6;
Q_IPV6ADDR tmp;
for (int i = 0; i < 16; ++i)
- tmp.c[i] = sa6->sin6_addr.qt_s6_addr[i];
+ tmp.c[i] = sa6->sin6_addr.s6_addr[i];
if (address) {
QHostAddress a;
a.setAddress(tmp);
@@ -252,40 +255,24 @@ static void convertToLevelAndOption(QNativeSocketEngine::SocketOption opt,
n = IP_MULTICAST_LOOP;
}
break;
- }
-}
-
-/*! \internal
-
- Sets the port and address to a sockaddr. Requires that sa point to the IPv6 struct if the address is IPv6.
-*/
-void QNativeSocketEnginePrivate::setPortAndAddress(sockaddr_in * sockAddrIPv4, qt_sockaddr_in6 * sockAddrIPv6,
- quint16 port, const QHostAddress & address, sockaddr ** sockAddrPtr, QT_SOCKLEN_T *sockAddrSize)
-{
- if (address.protocol() == QAbstractSocket::IPv6Protocol
- || address.protocol() == QAbstractSocket::AnyIPProtocol
- || socketProtocol == QAbstractSocket::IPv6Protocol
- || socketProtocol == QAbstractSocket::AnyIPProtocol) {
- memset(sockAddrIPv6, 0, sizeof(qt_sockaddr_in6));
- sockAddrIPv6->sin6_family = AF_INET6;
- sockAddrIPv6->sin6_scope_id = address.scopeId().toUInt();
- WSAHtons(socketDescriptor, port, &(sockAddrIPv6->sin6_port));
- Q_IPV6ADDR tmp = address.toIPv6Address();
- memcpy(&(sockAddrIPv6->sin6_addr.qt_s6_addr), &tmp, sizeof(tmp));
- *sockAddrSize = sizeof(qt_sockaddr_in6);
- *sockAddrPtr = (struct sockaddr *) sockAddrIPv6;
- } else
-
- if (address.protocol() == QAbstractSocket::IPv4Protocol
- || address.protocol() == QAbstractSocket::UnknownNetworkLayerProtocol) {
- memset(sockAddrIPv4, 0, sizeof(sockaddr_in));
- sockAddrIPv4->sin_family = AF_INET;
- WSAHtons(socketDescriptor, port, &(sockAddrIPv4->sin_port));
- WSAHtonl(socketDescriptor, address.toIPv4Address(), &(sockAddrIPv4->sin_addr.s_addr));
- *sockAddrSize = sizeof(sockaddr_in);
- *sockAddrPtr = (struct sockaddr *) sockAddrIPv4;
- } else {
- // unreachable
+ 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;
}
}
@@ -320,25 +307,6 @@ static inline int qt_socket_getMaxMsgSize(qintptr socketDescriptor)
return value;
}
-QWindowsSockInit::QWindowsSockInit()
-: version(0)
-{
- //### should we try for 2.2 on all platforms ??
- WSAData wsadata;
-
- // IPv6 requires Winsock v2.0 or better.
- if (WSAStartup(MAKEWORD(2,0), &wsadata) != 0) {
- qWarning("QTcpSocketAPI: WinSock v2.0 initialization failed.");
- } else {
- version = 0x20;
- }
-}
-
-QWindowsSockInit::~QWindowsSockInit()
-{
- WSACleanup();
-}
-
// MS Transport Provider IOCTL to control
// reporting PORT_UNREACHABLE messages
// on UDP sockets via recv/WSARecv/etc.
@@ -354,6 +322,12 @@ QWindowsSockInit::~QWindowsSockInit()
# define SIO_UDP_CONNRESET _WSAIOW(IOC_VENDOR,12)
#endif
+// inline on purpose
+inline uint QNativeSocketEnginePrivate::scopeIdFromString(const QString &scopeid)
+{
+ return scopeid.toUInt();
+}
+
bool QNativeSocketEnginePrivate::createNewSocket(QAbstractSocket::SocketType socketType, QAbstractSocket::NetworkLayerProtocol &socketProtocol)
{
@@ -439,9 +413,27 @@ bool QNativeSocketEnginePrivate::createNewSocket(QAbstractSocket::SocketType soc
WS_ERROR_DEBUG(err);
}
}
+
+ // get the pointer to sendmsg and recvmsg
+ DWORD bytesReturned;
+ GUID recvmsgguid = WSAID_WSARECVMSG;
+ if (WSAIoctl(socketDescriptor, SIO_GET_EXTENSION_FUNCTION_POINTER,
+ &recvmsgguid, sizeof(recvmsgguid),
+ &recvmsg, sizeof(recvmsg), &bytesReturned, NULL, NULL) == SOCKET_ERROR)
+ recvmsg = 0;
+
+ GUID sendmsgguid = WSAID_WSASENDMSG;
+ if (WSAIoctl(socketDescriptor, SIO_GET_EXTENSION_FUNCTION_POINTER,
+ &sendmsgguid, sizeof(sendmsgguid),
+ &sendmsg, sizeof(sendmsg), &bytesReturned, NULL, NULL) == SOCKET_ERROR)
+ sendmsg = 0;
#endif
socketDescriptor = socket;
+ if (socket != INVALID_SOCKET) {
+ this->socketProtocol = socketProtocol;
+ this->socketType = socketType;
+ }
// Make the socket nonblocking.
if (!setOption(QAbstractSocketEngine::NonBlockingSocketOption, 1)) {
@@ -626,12 +618,10 @@ bool QNativeSocketEnginePrivate::nativeConnect(const QHostAddress &address, quin
qDebug("QNativeSocketEnginePrivate::nativeConnect() to %s :: %i", address.toString().toLatin1().constData(), port);
#endif
- struct sockaddr_in sockAddrIPv4;
- qt_sockaddr_in6 sockAddrIPv6;
- struct sockaddr *sockAddrPtr = 0;
+ qt_sockaddr aa;
QT_SOCKLEN_T sockAddrSize = 0;
- setPortAndAddress(&sockAddrIPv4, &sockAddrIPv6, port, address, &sockAddrPtr, &sockAddrSize);
+ setPortAndAddress(port, address, &aa, &sockAddrSize);
if ((socketProtocol == QAbstractSocket::IPv6Protocol || socketProtocol == QAbstractSocket::AnyIPProtocol) && address.toIPv4Address()) {
//IPV6_V6ONLY option must be cleared to connect to a V4 mapped address
@@ -642,7 +632,7 @@ bool QNativeSocketEnginePrivate::nativeConnect(const QHostAddress &address, quin
}
forever {
- int connectResult = ::WSAConnect(socketDescriptor, sockAddrPtr, sockAddrSize, 0,0,0,0);
+ int connectResult = ::WSAConnect(socketDescriptor, &aa.a, sockAddrSize, 0,0,0,0);
if (connectResult == SOCKET_ERROR) {
int err = WSAGetLastError();
WS_ERROR_DEBUG(err);
@@ -765,46 +755,36 @@ bool QNativeSocketEnginePrivate::nativeConnect(const QHostAddress &address, quin
bool QNativeSocketEnginePrivate::nativeBind(const QHostAddress &a, quint16 port)
{
QHostAddress address = a;
- DWORD ipv6only = 0;
- switch (address.protocol()) {
- case QAbstractSocket::IPv6Protocol:
- if (address.toIPv6Address()[0] == 0xff) {
- // binding to a multicast address
- address = QHostAddress(QHostAddress::AnyIPv6);
- }
- //This is default in current windows versions, it may change in future so set it explicitly
- if (QSysInfo::windowsVersion() >= QSysInfo::WV_6_0) {
- ipv6only = 1;
- ipv6only = ::setsockopt(socketDescriptor, IPPROTO_IPV6, IPV6_V6ONLY, (char*)&ipv6only, sizeof(ipv6only) );
- }
- break;
- case QAbstractSocket::IPv4Protocol:
+ if (address.protocol() == QAbstractSocket::IPv4Protocol) {
if ((address.toIPv4Address() & 0xffff0000) == 0xefff0000) {
// binding to a multicast address
address = QHostAddress(QHostAddress::AnyIPv4);
}
- break;
- case QAbstractSocket::AnyIPProtocol:
- if (QSysInfo::windowsVersion() >= QSysInfo::WV_6_0) {
- ipv6only = ::setsockopt(socketDescriptor, IPPROTO_IPV6, IPV6_V6ONLY, (char*)&ipv6only, sizeof(ipv6only) );
- } else {
- address = QHostAddress(QHostAddress::AnyIPv4); //xp/WS2003 and earlier don't support dual stack, so bind to IPv4
- socketProtocol = QAbstractSocket::IPv4Protocol;
- }
- break;
- default:
- break;
}
- struct sockaddr_in sockAddrIPv4;
- qt_sockaddr_in6 sockAddrIPv6;
- struct sockaddr *sockAddrPtr = 0;
+ qt_sockaddr aa;
QT_SOCKLEN_T sockAddrSize = 0;
+ setPortAndAddress(port, address, &aa, &sockAddrSize);
- setPortAndAddress(&sockAddrIPv4, &sockAddrIPv6, port, address, &sockAddrPtr, &sockAddrSize);
+ if (aa.a.sa_family == AF_INET6) {
+ // The default may change in future, so set it explicitly
+ int ipv6only = 0;
+ if (address.protocol() == QAbstractSocket::IPv6Protocol)
+ ipv6only = 1;
+ ::setsockopt(socketDescriptor, IPPROTO_IPV6, IPV6_V6ONLY, (char*)&ipv6only, sizeof(ipv6only) );
+ }
- int bindResult = ::bind(socketDescriptor, sockAddrPtr, sockAddrSize);
+ int bindResult = ::bind(socketDescriptor, &aa.a, sockAddrSize);
+ if (bindResult == SOCKET_ERROR && WSAGetLastError() == WSAEAFNOSUPPORT
+ && address.protocol() == QAbstractSocket::AnyIPProtocol) {
+ // retry with v4
+ aa.a4.sin_family = AF_INET;
+ aa.a4.sin_port = htons(port);
+ aa.a4.sin_addr.s_addr = htonl(address.toIPv4Address());
+ sockAddrSize = sizeof(aa.a4);
+ bindResult = ::bind(socketDescriptor, &aa.a, sockAddrSize);
+ }
if (bindResult == SOCKET_ERROR) {
int err = WSAGetLastError();
WS_ERROR_DEBUG(err);
@@ -1216,33 +1196,47 @@ qint64 QNativeSocketEnginePrivate::nativePendingDatagramSize() const
return ret;
}
+#ifdef Q_OS_WINCE
+// Windows CE has no support for sendmsg or recvmsg. We set it to null here to simplify the code below.
+static int (*const recvmsg)(...) = 0;
+static int (*const sendmsg)(...) = 0;
+#endif
-qint64 QNativeSocketEnginePrivate::nativeReceiveDatagram(char *data, qint64 maxLength,
- QHostAddress *address, quint16 *port)
+qint64 QNativeSocketEnginePrivate::nativeReceiveDatagram(char *data, qint64 maxLength, QIpPacketHeader *header,
+ QAbstractSocketEngine::PacketHeaderOptions options)
{
- qint64 ret = 0;
-
+ 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;
+ char c;
+ memset(&msg, 0, sizeof(msg));
memset(&aa, 0, sizeof(aa));
- QT_SOCKLEN_T sz;
- sz = sizeof(aa);
- WSABUF buf;
- buf.buf = data;
- buf.len = maxLength;
-#if !defined(Q_OS_WINCE)
- buf.buf = data;
- buf.len = maxLength;
-#else
- char tmpChar;
- buf.buf = data ? data : &tmpChar;
- buf.len = maxLength;
-#endif
+ // we need to receive at least one byte, even if our user isn't interested in it
+ buf.buf = maxLength ? data : &c;
+ buf.len = maxLength ? maxLength : 1;
+ msg.lpBuffers = &buf;
+ 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;
- int wsaRet = ::WSARecvFrom(socketDescriptor, &buf, 1, &bytesRead, &flags, &aa.a, &sz,0,0);
- if (wsaRet == SOCKET_ERROR) {
+ qint64 ret;
+
+ if (recvmsg)
+ ret = recvmsg(socketDescriptor, &msg, &bytesRead, 0,0);
+ else
+ ret = ::WSARecvFrom(socketDescriptor, &buf, 1, &bytesRead, &flags, msg.name, &msg.namelen,0,0);
+ if (ret == SOCKET_ERROR) {
int err = WSAGetLastError();
if (err == WSAEMSGSIZE) {
// it is ok the buffer was to small if bytesRead is larger than
@@ -1252,12 +1246,44 @@ qint64 QNativeSocketEnginePrivate::nativeReceiveDatagram(char *data, qint64 maxL
WS_ERROR_DEBUG(err);
setError(QAbstractSocket::NetworkError, ReceiveDatagramErrorString);
ret = -1;
+ if (header)
+ header->clear();
}
} else {
ret = qint64(bytesRead);
+ if (options & QNativeSocketEngine::WantDatagramSender)
+ qt_socket_getPortAndAddress(socketDescriptor, &aa, &header->senderPort, &header->senderAddress);
}
- qt_socket_getPortAndAddress(socketDescriptor, &aa, port, address);
+ 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",
@@ -1271,27 +1297,94 @@ qint64 QNativeSocketEnginePrivate::nativeReceiveDatagram(char *data, qint64 maxL
qint64 QNativeSocketEnginePrivate::nativeSendDatagram(const char *data, qint64 len,
- const QHostAddress &address, quint16 port)
+ const QIpPacketHeader &header)
{
- qint64 ret = -1;
- struct sockaddr_in sockAddrIPv4;
- qt_sockaddr_in6 sockAddrIPv6;
- struct sockaddr *sockAddrPtr = 0;
- QT_SOCKLEN_T sockAddrSize = 0;
-
- setPortAndAddress(&sockAddrIPv4, &sockAddrIPv6, port, address, &sockAddrPtr, &sockAddrSize);
-
+ union {
+ char cbuf[WSA_CMSG_SPACE(sizeof(struct in6_pktinfo)) + WSA_CMSG_SPACE(sizeof(int))];
+ WSACMSGHDR align; // ensures alignment
+ };
+ WSACMSGHDR *cmsgptr = &align;
+ WSAMSG msg;
WSABUF buf;
+ qt_sockaddr aa;
+
+ memset(&msg, 0, sizeof(msg));
+ memset(&aa, 0, sizeof(aa));
#if !defined(Q_OS_WINCE)
buf.buf = len ? (char*)data : 0;
#else
char tmp;
buf.buf = len ? (char*)data : &tmp;
#endif
+ msg.lpBuffers = &buf;
+ msg.dwBufferCount = 1;
+ msg.name = &aa.a;
buf.len = len;
+
+ setPortAndAddress(header.destinationPort, header.destinationAddress, &aa, &msg.namelen);
+
+ if (msg.namelen == sizeof(aa.a6)) {
+ // sending IPv6
+ if (header.hopLimit != -1) {
+ msg.Control.len += WSA_CMSG_SPACE(sizeof(int));
+ cmsgptr->cmsg_len = WSA_CMSG_LEN(sizeof(int));
+ cmsgptr->cmsg_level = IPPROTO_IPV6;
+ cmsgptr->cmsg_type = IPV6_HOPLIMIT;
+ memcpy(WSA_CMSG_DATA(cmsgptr), &header.hopLimit, sizeof(int));
+ cmsgptr = reinterpret_cast<WSACMSGHDR *>(reinterpret_cast<char *>(cmsgptr)
+ + WSA_CMSG_SPACE(sizeof(int)));
+ }
+ if (header.ifindex != 0 || !header.senderAddress.isNull()) {
+ struct in6_pktinfo *data = reinterpret_cast<in6_pktinfo *>(WSA_CMSG_DATA(cmsgptr));
+ memset(data, 0, sizeof(*data));
+ msg.Control.len += WSA_CMSG_SPACE(sizeof(*data));
+ cmsgptr->cmsg_len = WSA_CMSG_LEN(sizeof(*data));
+ cmsgptr->cmsg_level = IPPROTO_IPV6;
+ cmsgptr->cmsg_type = IPV6_PKTINFO;
+ data->ipi6_ifindex = header.ifindex;
+
+ Q_IPV6ADDR tmp = header.senderAddress.toIPv6Address();
+ memcpy(&data->ipi6_addr, &tmp, sizeof(tmp));
+ cmsgptr = reinterpret_cast<WSACMSGHDR *>(reinterpret_cast<char *>(cmsgptr)
+ + WSA_CMSG_SPACE(sizeof(*data)));
+ }
+ } else {
+ // sending IPv4
+ if (header.hopLimit != -1) {
+ msg.Control.len += WSA_CMSG_SPACE(sizeof(int));
+ cmsgptr->cmsg_len = WSA_CMSG_LEN(sizeof(int));
+ cmsgptr->cmsg_level = IPPROTO_IP;
+ cmsgptr->cmsg_type = IP_TTL;
+ memcpy(WSA_CMSG_DATA(cmsgptr), &header.hopLimit, sizeof(int));
+ cmsgptr = reinterpret_cast<WSACMSGHDR *>(reinterpret_cast<char *>(cmsgptr)
+ + WSA_CMSG_SPACE(sizeof(int)));
+ }
+ if (header.ifindex != 0 || !header.senderAddress.isNull()) {
+ struct in_pktinfo *data = reinterpret_cast<in_pktinfo *>(WSA_CMSG_DATA(cmsgptr));
+ memset(data, 0, sizeof(*data));
+ msg.Control.len += WSA_CMSG_SPACE(sizeof(*data));
+ cmsgptr->cmsg_len = WSA_CMSG_LEN(sizeof(*data));
+ cmsgptr->cmsg_level = IPPROTO_IP;
+ cmsgptr->cmsg_type = IP_PKTINFO;
+ data->ipi_ifindex = header.ifindex;
+ WSAHtonl(socketDescriptor, header.senderAddress.toIPv4Address(), &data->ipi_addr.s_addr);
+ cmsgptr = reinterpret_cast<WSACMSGHDR *>(reinterpret_cast<char *>(cmsgptr)
+ + WSA_CMSG_SPACE(sizeof(*data)));
+ }
+ }
+
+ if (msg.Control.len != 0)
+ msg.Control.buf = cbuf;
+
DWORD flags = 0;
DWORD bytesSent = 0;
- if (::WSASendTo(socketDescriptor, &buf, 1, &bytesSent, flags, sockAddrPtr, sockAddrSize, 0,0) == SOCKET_ERROR) {
+ qint64 ret = -1;
+ if (sendmsg) {
+ ret = sendmsg(socketDescriptor, &msg, flags, &bytesSent, 0,0);
+ } else {
+ ret = ::WSASendTo(socketDescriptor, &buf, 1, &bytesSent, flags, msg.name, msg.namelen, 0,0);
+ }
+ if (ret == SOCKET_ERROR) {
int err = WSAGetLastError();
WS_ERROR_DEBUG(err);
switch (err) {