QNativeSocketEngine: use sendmsg/recvmsg instead of sendto/recvfrom

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>
This commit is contained in:
Thiago Macieira 2015-03-10 15:09:29 -07:00
parent 5b38454714
commit ef05ad0ac5
4 changed files with 143 additions and 89 deletions

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

View 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);
aa.a6.sin6_family = AF_INET6;
aa.a6.sin6_port = htons(header.destinationPort);
aa.a6.sin6_scope_id = makeScopeId(header.destinationAddress);
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;
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) {

View 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;
qt_sockaddr aa;
memset(&aa, 0, sizeof(aa));
QT_SOCKLEN_T sz;
sz = sizeof(aa);
WSAMSG msg;
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
qt_sockaddr aa;
char c;
memset(&msg, 0, sizeof(msg));
memset(&aa, 0, sizeof(aa));
// 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) {

View 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