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:
parent
5b38454714
commit
ef05ad0ac5
@ -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,
|
||||
|
@ -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) {
|
||||
|
@ -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) {
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user