QNativeSocketEngine: use sendmsg/recvmsg instead of sendto/recvfrom
authorThiago Macieira <thiago.macieira@intel.com>
Tue, 10 Mar 2015 22:09:29 +0000 (15:09 -0700)
committerThiago Macieira <thiago.macieira@intel.com>
Sat, 22 Aug 2015 22:26:30 +0000 (22:26 +0000)
We'll need to use these functions instead of the ones we're currently
using in order to access the ancillary data.

Note that on Windows the two functions aren't globals, but must be
obtained via ioctl, which means they can fail. If they do, we fall back
to using WSARecvFrom/WSASendTo

Change-Id: Iee8cbc07c4434ce9b560ffff13ca4284acd24132
Reviewed-by: Richard J. Moore <rich@kde.org>
src/network/socket/qnativesocketengine_p.h
src/network/socket/qnativesocketengine_unix.cpp
src/network/socket/qnativesocketengine_win.cpp
src/network/socket/qnet_unix_p.h

index fb36ce620438790294498c231f6028d46fd7bcb1..29063b3a85f3f59f4832d356decfe2d8784a4785 100644 (file)
@@ -51,6 +51,7 @@
 #  include <netinet/in.h>
 #else
 #  include <winsock2.h>
+#  include <mswsock.h>
 #endif
 
 QT_BEGIN_NAMESPACE
@@ -70,6 +71,36 @@ struct qt_sockaddr_storage {
 #ifdef Q_OS_WIN
 #define QT_SOCKLEN_T int
 #define QT_SOCKOPTLEN_T int
+
+// The following definitions are copied from the MinGW header mswsock.h which
+// was placed in the public domain. The WSASendMsg and WSARecvMsg functions
+// were introduced with Windows Vista, so some Win32 headers are lacking them.
+// There are no known versions of Windows CE or Embedded that contain them.
+#ifndef Q_OS_WINCE
+#  ifndef WSAID_WSARECVMSG
+typedef INT (WINAPI *LPFN_WSARECVMSG)(SOCKET s, LPWSAMSG lpMsg,
+                                      LPDWORD lpdwNumberOfBytesRecvd,
+                                      LPWSAOVERLAPPED lpOverlapped,
+                                      LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine);
+#    define WSAID_WSARECVMSG {0xf689d7c8,0x6f1f,0x436b,{0x8a,0x53,0xe5,0x4f,0xe3,0x51,0xc3,0x22}}
+#  endif
+#  ifndef WSAID_WSASENDMSG
+typedef struct {
+  LPWSAMSG lpMsg;
+  DWORD dwFlags;
+  LPDWORD lpNumberOfBytesSent;
+  LPWSAOVERLAPPED lpOverlapped;
+  LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine;
+} WSASENDMSG, *LPWSASENDMSG;
+
+typedef INT (WSAAPI *LPFN_WSASENDMSG)(SOCKET s, LPWSAMSG lpMsg, DWORD dwFlags,
+                                      LPDWORD lpNumberOfBytesSent,
+                                      LPWSAOVERLAPPED lpOverlapped,
+                                      LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine);
+
+#    define WSAID_WSASENDMSG {0xa441e712,0x754f,0x43ca,{0x84,0xa7,0x0d,0xee,0x44,0xcf,0x60,0x6d}}
+#  endif
+#endif
 #endif
 
 // sockaddr_in6 size changed between old and new SDK
@@ -185,6 +216,10 @@ public:
 
     QSocketNotifier *readNotifier, *writeNotifier, *exceptNotifier;
 
+#if defined(Q_OS_WIN) && !defined(Q_OS_WINCE)
+    LPFN_WSASENDMSG sendmsg;
+    LPFN_WSARECVMSG recvmsg;
+#  endif
     enum ErrorString {
         NonBlockingInitFailedErrorString,
         BroadcastingInitFailedErrorString,
index 0e040962964159f87c89a30648fb3d9d9ca5b8d5..37651dcbc3d6468dc5ba2de783c0743095e07fca 100644 (file)
@@ -847,19 +847,29 @@ qint64 QNativeSocketEnginePrivate::nativePendingDatagramSize() const
 qint64 QNativeSocketEnginePrivate::nativeReceiveDatagram(char *data, qint64 maxSize, QIpPacketHeader *header,
                                                          QAbstractSocketEngine::PacketHeaderOptions options)
 {
+    struct msghdr msg;
+    struct iovec vec;
     qt_sockaddr aa;
+    char c;
+    memset(&msg, 0, sizeof(msg));
     memset(&aa, 0, sizeof(aa));
-    QT_SOCKLEN_T sz;
-    sz = sizeof(aa);
 
-    ssize_t recvFromResult = 0;
+    // we need to receive at least one byte, even if our user isn't interested in it
+    vec.iov_base = maxSize ? data : &c;
+    vec.iov_len = maxSize ? maxSize : 1;
+    msg.msg_iov = &vec;
+    msg.msg_iovlen = 1;
+    if (options & QAbstractSocketEngine::WantDatagramSender) {
+        msg.msg_name = &aa;
+        msg.msg_namelen = sizeof(aa);
+    }
+
+    ssize_t recvResult = 0;
     do {
-        char c;
-        recvFromResult = ::recvfrom(socketDescriptor, maxSize ? data : &c, maxSize ? maxSize : 1,
-                                    0, &aa.a, &sz);
-    } while (recvFromResult == -1 && errno == EINTR);
+        recvResult = ::recvmsg(socketDescriptor, &msg, 0);
+    } while (recvResult == -1 && errno == EINTR);
 
-    if (recvFromResult == -1) {
+    if (recvResult == -1) {
         setError(QAbstractSocket::NetworkError, ReceiveDatagramErrorString);
         if (header)
             header->clear();
@@ -870,46 +880,50 @@ qint64 QNativeSocketEnginePrivate::nativeReceiveDatagram(char *data, qint64 maxS
 
 #if defined (QNATIVESOCKETENGINE_DEBUG)
     qDebug("QNativeSocketEnginePrivate::nativeReceiveDatagram(%p \"%s\", %lli, %s, %i) == %lli",
-           data, qt_prettyDebug(data, qMin(recvFromResult, ssize_t(16)), recvFromResult).data(), maxSize,
+           data, qt_prettyDebug(data, qMin(recvResult, ssize_t(16)), recvResult).data(), maxSize,
            address ? address->toString().toLatin1().constData() : "(nil)",
-           port ? *port : 0, (qint64) recvFromResult);
+           port ? *port : 0, (qint64) recvResult);
 #endif
 
-    return qint64(maxSize ? recvFromResult : recvFromResult == -1 ? -1 : 0);
+    return qint64(maxSize ? recvResult : recvResult == -1 ? -1 : 0);
 }
 
 qint64 QNativeSocketEnginePrivate::nativeSendDatagram(const char *data, qint64 len, const QIpPacketHeader &header)
 {
-    struct sockaddr_in sockAddrIPv4;
-    struct sockaddr *sockAddrPtr = 0;
-    QT_SOCKLEN_T sockAddrSize = 0;
+    struct msghdr msg;
+    struct iovec vec;
+    qt_sockaddr aa;
 
-    struct sockaddr_in6 sockAddrIPv6;
-    const QHostAddress &host = header.destinationAddress;
-    quint16 port = header.destinationPort;
-    if (host.protocol() == QAbstractSocket::IPv6Protocol
+    memset(&msg, 0, sizeof(msg));
+    memset(&aa, 0, sizeof(aa));
+    vec.iov_base = const_cast<char *>(data);
+    vec.iov_len = len;
+    msg.msg_iov = &vec;
+    msg.msg_iovlen = 1;
+    msg.msg_name = &aa.a;
+
+    if (header.destinationAddress.protocol() == QAbstractSocket::IPv6Protocol
         || socketProtocol == QAbstractSocket::IPv6Protocol
         || socketProtocol == QAbstractSocket::AnyIPProtocol) {
-        memset(&sockAddrIPv6, 0, sizeof(sockAddrIPv6));
-        sockAddrIPv6.sin6_family = AF_INET6;
-        sockAddrIPv6.sin6_port = htons(port);
-        sockAddrIPv6.sin6_scope_id = makeScopeId(host);
-
-        Q_IPV6ADDR tmp = host.toIPv6Address();
-        memcpy(&sockAddrIPv6.sin6_addr.s6_addr, &tmp, sizeof(tmp));
-        sockAddrSize = sizeof(sockAddrIPv6);
-        sockAddrPtr = (struct sockaddr *)&sockAddrIPv6;
-    } else if (host.protocol() == QAbstractSocket::IPv4Protocol) {
-        memset(&sockAddrIPv4, 0, sizeof(sockAddrIPv4));
-        sockAddrIPv4.sin_family = AF_INET;
-        sockAddrIPv4.sin_port = htons(port);
-        sockAddrIPv4.sin_addr.s_addr = htonl(host.toIPv4Address());
-        sockAddrSize = sizeof(sockAddrIPv4);
-        sockAddrPtr = (struct sockaddr *)&sockAddrIPv4;
+        aa.a6.sin6_family = AF_INET6;
+        aa.a6.sin6_port = htons(header.destinationPort);
+        aa.a6.sin6_scope_id = makeScopeId(header.destinationAddress);
+
+        Q_IPV6ADDR tmp = header.destinationAddress.toIPv6Address();
+        memcpy(&aa.a6.sin6_addr, &tmp, sizeof(tmp));
+        msg.msg_namelen = sizeof(aa.a6);
+    } else if (header.destinationAddress.protocol() == QAbstractSocket::IPv4Protocol) {
+        aa.a4.sin_family = AF_INET;
+        aa.a4.sin_port = htons(header.destinationPort);
+        aa.a4.sin_addr.s_addr = htonl(header.destinationAddress.toIPv4Address());
+        msg.msg_namelen = sizeof(aa.a4);
+    } else {
+        // Don't know what IP type this is, let's hope it sends
+        msg.msg_name = 0;
+        msg.msg_namelen = 0;
     }
 
-    ssize_t sentBytes = qt_safe_sendto(socketDescriptor, data, len,
-                                       0, sockAddrPtr, sockAddrSize);
+    ssize_t sentBytes = qt_safe_sendmsg(socketDescriptor, &msg, 0);
 
     if (sentBytes < 0) {
         switch (errno) {
index ed376eed951860d8dede1b88126f064ad4206d20..9171ee36e04ec8a503d2eda1bae3408f11ceb9fa 100644 (file)
@@ -420,6 +420,20 @@ 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;
@@ -1196,33 +1210,39 @@ 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, QIpPacketHeader *header,
                                                          QAbstractSocketEngine::PacketHeaderOptions options)
 {
-    qint64 ret = 0;
-
+    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);
 
     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
@@ -1256,26 +1276,34 @@ qint64 QNativeSocketEnginePrivate::nativeReceiveDatagram(char *data, qint64 maxL
 qint64 QNativeSocketEnginePrivate::nativeSendDatagram(const char *data, qint64 len,
                                                       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, header.destinationPort,
-                      header.destinationAddress, &sockAddrPtr, &sockAddrSize);
-
+    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;
     buf.len = len;
+
+    setPortAndAddress(&aa.a4, &aa.a6, header.destinationPort,
+                      header.destinationAddress, &msg.name, &msg.namelen);
+
     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) {
index a5a87fc7c15f51c7958b6c9811da0a63caf22550..9626c53711e996d4cf0fc9bb4792a4b495dd2ffb 100644 (file)
@@ -173,8 +173,7 @@ static inline in_addr_t qt_safe_inet_addr(const char *cp)
 #endif
 }
 
-// VxWorks' headers do not specify any const modifiers
-static inline int qt_safe_sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *to, QT_SOCKLEN_T tolen)
+static inline int qt_safe_sendmsg(int sockfd, const struct msghdr *msg, int flags)
 {
 #ifdef MSG_NOSIGNAL
     flags |= MSG_NOSIGNAL;
@@ -183,11 +182,7 @@ static inline int qt_safe_sendto(int sockfd, const void *buf, size_t len, int fl
 #endif
 
     int ret;
-#ifdef Q_OS_VXWORKS
-    EINTR_LOOP(ret, ::sendto(sockfd, (char *) buf, len, flags, (struct sockaddr *) to, tolen));
-#else
-    EINTR_LOOP(ret, ::sendto(sockfd, buf, len, flags, to, tolen));
-#endif
+    EINTR_LOOP(ret, ::sendmsg(sockfd, msg, flags));
     return ret;
 }
 
@@ -199,24 +194,6 @@ static inline int qt_safe_recvmsg(int sockfd, struct msghdr *msg, int flags)
     return ret;
 }
 
-// VxWorks' headers do not specify any const modifiers
-static inline int qt_safe_sendmsg(int sockfd, const struct msghdr *msg, int flags)
-{
-#ifdef MSG_NOSIGNAL
-    flags |= MSG_NOSIGNAL;
-#else
-    qt_ignore_sigpipe();
-#endif
-
-    int ret;
-#ifdef Q_OS_VXWORKS
-    EINTR_LOOP(ret, ::sendmsg(sockfd, (struct msghdr *) msg, flags);
-#else
-    EINTR_LOOP(ret, ::sendmsg(sockfd, msg, flags));
-#endif
-    return ret;
-}
-
 QT_END_NAMESPACE
 
 #endif // QNET_UNIX_P_H