/**************************************************************************** | |
** | |
** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). | |
** All rights reserved. | |
** Contact: Nokia Corporation (qt-info@nokia.com) | |
** | |
** This file is part of the Qt3Support module of the Qt Toolkit. | |
** | |
** $QT_BEGIN_LICENSE:LGPL$ | |
** GNU Lesser General Public License Usage | |
** This file may be used under the terms of the GNU Lesser General Public | |
** License version 2.1 as published by the Free Software Foundation and | |
** appearing in the file LICENSE.LGPL included in the packaging of this | |
** file. Please review the following information to ensure the GNU Lesser | |
** General Public License version 2.1 requirements will be met: | |
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. | |
** | |
** In addition, as a special exception, Nokia gives you certain additional | |
** rights. These rights are described in the Nokia Qt LGPL Exception | |
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. | |
** | |
** GNU General Public License Usage | |
** Alternatively, this file may be used under the terms of the GNU General | |
** Public License version 3.0 as published by the Free Software Foundation | |
** and appearing in the file LICENSE.GPL included in the packaging of this | |
** file. Please review the following information to ensure the GNU General | |
** Public License version 3.0 requirements will be met: | |
** http://www.gnu.org/copyleft/gpl.html. | |
** | |
** Other Usage | |
** Alternatively, this file may be used in accordance with the terms and | |
** conditions contained in a signed written agreement between you and Nokia. | |
** | |
** | |
** | |
** | |
** | |
** $QT_END_LICENSE$ | |
** | |
****************************************************************************/ | |
#include "q3socketdevice.h" | |
#include "qwindowdefs.h" | |
#include "qdatetime.h" | |
#include <qcoreapplication.h> | |
#include <string.h> | |
#if defined (QT_NO_IPV6) | |
# include <winsock.h> | |
#else | |
# if defined (Q_CC_BOR) || defined (Q_CC_GNU) | |
# include <winsock2.h> | |
# else | |
# include <winsock.h> | |
# endif | |
// Use our own defines and structs which we know are correct | |
# define QT_SS_MAXSIZE 128 | |
# define QT_SS_ALIGNSIZE (sizeof(__int64)) | |
# define QT_SS_PAD1SIZE (QT_SS_ALIGNSIZE - sizeof (short)) | |
# define QT_SS_PAD2SIZE (QT_SS_MAXSIZE - (sizeof (short) + QT_SS_PAD1SIZE + QT_SS_ALIGNSIZE)) | |
QT_BEGIN_NAMESPACE | |
struct qt_sockaddr_storage { | |
short ss_family; | |
char __ss_pad1[QT_SS_PAD1SIZE]; | |
__int64 __ss_align; | |
char __ss_pad2[QT_SS_PAD2SIZE]; | |
}; | |
// sockaddr_in6 size changed between old and new SDK | |
// Only the new version is the correct one, so always | |
// use this structure. | |
struct qt_in6_addr { | |
u_char qt_s6_addr[16]; | |
}; | |
typedef struct { | |
short sin6_family; /* AF_INET6 */ | |
u_short sin6_port; /* Transport level port number */ | |
u_long sin6_flowinfo; /* IPv6 flow information */ | |
struct qt_in6_addr sin6_addr; /* IPv6 address */ | |
u_long sin6_scope_id; /* set of interfaces for a scope */ | |
} qt_sockaddr_in6; | |
#endif | |
#ifndef AF_INET6 | |
#define AF_INET6 23 /* Internetwork Version 6 */ | |
#endif | |
#ifndef NO_ERRNO_H | |
QT_BEGIN_INCLUDE_NAMESPACE | |
# if defined(Q_OS_WINCE) | |
# include "qfunctions_wince.h" | |
# else | |
# include <errno.h> | |
# endif | |
QT_END_INCLUDE_NAMESPACE | |
#endif | |
#if defined(SOCKLEN_T) | |
#undef SOCKLEN_T | |
#endif | |
#define SOCKLEN_T int // #### Winsock 1.1 | |
static int initialized = 0x00; // Holds the Winsock version | |
static void cleanupWinSock() // post-routine | |
{ | |
WSACleanup(); | |
initialized = 0x00; | |
} | |
static inline void qt_socket_getportaddr( struct sockaddr *sa, | |
quint16 *port, QHostAddress *addr ) | |
{ | |
#if !defined (QT_NO_IPV6) | |
if (sa->sa_family == AF_INET6) { | |
qt_sockaddr_in6 *sa6 = (qt_sockaddr_in6 *)sa; | |
Q_IPV6ADDR tmp; | |
for ( int i = 0; i < 16; ++i ) | |
tmp.c[i] = sa6->sin6_addr.qt_s6_addr[i]; | |
QHostAddress a( tmp ); | |
*addr = a; | |
*port = ntohs( sa6->sin6_port ); | |
return; | |
} | |
#endif | |
struct sockaddr_in *sa4 = (struct sockaddr_in *)sa; | |
QHostAddress a( ntohl( sa4->sin_addr.s_addr ) ); | |
*port = ntohs( sa4->sin_port ); | |
*addr = a; | |
} | |
void Q3SocketDevice::init() | |
{ | |
#if !defined(QT_NO_IPV6) | |
if ( !initialized ) { | |
WSAData wsadata; | |
// IPv6 requires Winsock v2.0 or better. | |
if ( WSAStartup( MAKEWORD(2,0), &wsadata ) != 0 ) { | |
# if defined(QSOCKETDEVICE_DEBUG) | |
qDebug( "Q3SocketDevice: WinSock v2.0 initialization failed, disabling IPv6 support." ); | |
# endif | |
} else { | |
qAddPostRoutine( cleanupWinSock ); | |
initialized = 0x20; | |
return; | |
} | |
} | |
#endif | |
if (!initialized) { | |
WSAData wsadata; | |
if ( WSAStartup( MAKEWORD(1,1), &wsadata ) != 0 ) { | |
#if defined(QT_CHECK_NULL) | |
qWarning( "Q3SocketDevice: WinSock initialization failed" ); | |
#endif | |
#if defined(QSOCKETDEVICE_DEBUG) | |
qDebug( "Q3SocketDevice: WinSock initialization failed" ); | |
#endif | |
return; | |
} | |
qAddPostRoutine( cleanupWinSock ); | |
initialized = 0x11; | |
} | |
} | |
Q3SocketDevice::Protocol Q3SocketDevice::getProtocol() const | |
{ | |
if ( isValid() ) { | |
#if !defined (QT_NO_IPV6) | |
struct qt_sockaddr_storage sa; | |
#else | |
struct sockaddr_in sa; | |
#endif | |
memset( &sa, 0, sizeof(sa) ); | |
SOCKLEN_T sz = sizeof( sa ); | |
if ( !::getsockname(fd, (struct sockaddr *)&sa, &sz) ) { | |
#if !defined (QT_NO_IPV6) | |
switch ( sa.ss_family ) { | |
case AF_INET: | |
return IPv4; | |
case AF_INET6: | |
return IPv6; | |
default: | |
return Unknown; | |
} | |
#else | |
switch ( sa.sin_family ) { | |
case AF_INET: | |
return IPv4; | |
default: | |
return Unknown; | |
} | |
#endif | |
} | |
} | |
return Unknown; | |
} | |
int Q3SocketDevice::createNewSocket( ) | |
{ | |
#if !defined(QT_NO_IPV6) | |
SOCKET s; | |
// Support IPv6 for Winsock v2.0++ | |
if ( initialized >= 0x20 && protocol() == IPv6 ) { | |
s = ::socket( AF_INET6, t==Datagram?SOCK_DGRAM:SOCK_STREAM, 0 ); | |
} else { | |
s = ::socket( AF_INET, t==Datagram?SOCK_DGRAM:SOCK_STREAM, 0 ); | |
} | |
#else | |
SOCKET s = ::socket( AF_INET, t==Datagram?SOCK_DGRAM:SOCK_STREAM, 0 ); | |
#endif | |
if ( s == INVALID_SOCKET ) { | |
switch( WSAGetLastError() ) { | |
case WSANOTINITIALISED: | |
e = Impossible; | |
break; | |
case WSAENETDOWN: | |
// ### what to use here? | |
e = NetworkFailure; | |
//e = Inaccessible; | |
break; | |
case WSAEMFILE: | |
e = NoFiles; // special case for this | |
break; | |
case WSAEINPROGRESS: | |
case WSAENOBUFS: | |
e = NoResources; | |
break; | |
case WSAEAFNOSUPPORT: | |
case WSAEPROTOTYPE: | |
case WSAEPROTONOSUPPORT: | |
case WSAESOCKTNOSUPPORT: | |
e = InternalError; | |
break; | |
default: | |
e = UnknownError; | |
break; | |
} | |
} else { | |
return s; | |
} | |
return -1; | |
} | |
void Q3SocketDevice::close() | |
{ | |
if ( fd == -1 || !isOpen() ) // already closed | |
return; | |
resetStatus(); | |
setOpenMode(NotOpen); | |
::closesocket( fd ); | |
#if defined(QSOCKETDEVICE_DEBUG) | |
qDebug( "Q3SocketDevice::close: Closed socket %x", fd ); | |
#endif | |
fd = -1; | |
fetchConnectionParameters(); | |
QIODevice::close(); | |
} | |
bool Q3SocketDevice::blocking() const | |
{ | |
return true; | |
} | |
void Q3SocketDevice::setBlocking( bool enable ) | |
{ | |
#if defined(QSOCKETDEVICE_DEBUG) | |
qDebug( "Q3SocketDevice::setBlocking( %d )", enable ); | |
#endif | |
if ( !isValid() ) | |
return; | |
unsigned long dummy = enable ? 0 : 1; | |
ioctlsocket( fd, FIONBIO, &dummy ); | |
} | |
int Q3SocketDevice::option( Option opt ) const | |
{ | |
if ( !isValid() ) | |
return -1; | |
int n = -1; | |
int v = -1; | |
switch ( opt ) { | |
case Broadcast: | |
n = SO_BROADCAST; | |
break; | |
case ReceiveBuffer: | |
n = SO_RCVBUF; | |
break; | |
case ReuseAddress: | |
n = SO_REUSEADDR; | |
break; | |
case SendBuffer: | |
n = SO_SNDBUF; | |
break; | |
} | |
if ( n != -1 ) { | |
SOCKLEN_T len = sizeof(v); | |
int r = ::getsockopt( fd, SOL_SOCKET, n, (char*)&v, &len ); | |
if ( r != SOCKET_ERROR ) | |
return v; | |
if ( !e ) { | |
Q3SocketDevice *that = (Q3SocketDevice*)this; // mutable function | |
switch( WSAGetLastError() ) { | |
case WSANOTINITIALISED: | |
that->e = Impossible; | |
break; | |
case WSAENETDOWN: | |
that->e = NetworkFailure; | |
break; | |
case WSAEFAULT: | |
case WSAEINVAL: | |
case WSAENOPROTOOPT: | |
that->e = InternalError; | |
break; | |
case WSAEINPROGRESS: | |
that->e = NoResources; | |
break; | |
case WSAENOTSOCK: | |
that->e = Impossible; | |
break; | |
default: | |
that->e = UnknownError; | |
break; | |
} | |
} | |
return -1; | |
} | |
return v; | |
} | |
void Q3SocketDevice::setOption( Option opt, int v ) | |
{ | |
if ( !isValid() ) | |
return; | |
int n = -1; // for really, really bad compilers | |
switch ( opt ) { | |
case Broadcast: | |
n = SO_BROADCAST; | |
break; | |
case ReceiveBuffer: | |
n = SO_RCVBUF; | |
break; | |
case ReuseAddress: | |
n = SO_REUSEADDR; | |
break; | |
case SendBuffer: | |
n = SO_SNDBUF; | |
break; | |
default: | |
return; | |
} | |
int r = ::setsockopt( fd, SOL_SOCKET, n, (char*)&v, sizeof(v) ); | |
if ( r == SOCKET_ERROR && e == NoError ) { | |
switch( WSAGetLastError() ) { | |
case WSANOTINITIALISED: | |
e = Impossible; | |
break; | |
case WSAENETDOWN: | |
e = NetworkFailure; | |
break; | |
case WSAEFAULT: | |
case WSAEINVAL: | |
case WSAENOPROTOOPT: | |
e = InternalError; | |
break; | |
case WSAEINPROGRESS: | |
e = NoResources; | |
break; | |
case WSAENETRESET: | |
case WSAENOTCONN: | |
e = Impossible; // ### ? | |
break; | |
case WSAENOTSOCK: | |
e = Impossible; | |
break; | |
default: | |
e = UnknownError; | |
break; | |
} | |
} | |
} | |
bool Q3SocketDevice::connect( const QHostAddress &addr, quint16 port ) | |
{ | |
if ( !isValid() ) | |
return false; | |
pa = addr; | |
pp = port; | |
struct sockaddr_in a4; | |
struct sockaddr *aa; | |
SOCKLEN_T aalen; | |
#if !defined(QT_NO_IPV6) | |
qt_sockaddr_in6 a6; | |
if ( initialized >= 0x20 && addr.isIPv6Address() ) { | |
memset(&a6, 0, sizeof(a6)); | |
a6.sin6_family = AF_INET6; | |
a6.sin6_port = htons( port ); | |
Q_IPV6ADDR ip6 = addr.toIPv6Address(); | |
memcpy( &a6.sin6_addr.qt_s6_addr, &ip6, sizeof(ip6) ); | |
aalen = sizeof( a6 ); | |
aa = (struct sockaddr *)&a6; | |
} else | |
#endif | |
if ( addr.isIPv4Address() ) { | |
memset(&a4, 0, sizeof(a4)); | |
a4.sin_family = AF_INET; | |
a4.sin_port = htons(port); | |
a4.sin_addr.s_addr = htonl(addr.toIPv4Address()); | |
aalen = sizeof(a4); | |
aa = (struct sockaddr *)&a4; | |
} else { | |
e = Impossible; | |
return false; | |
} | |
int r = ::connect( fd, aa, aalen ); | |
if ( r == SOCKET_ERROR ) | |
{ | |
switch( WSAGetLastError() ) { | |
case WSANOTINITIALISED: | |
e = Impossible; | |
break; | |
case WSAENETDOWN: | |
e = NetworkFailure; | |
break; | |
case WSAEADDRINUSE: | |
case WSAEINPROGRESS: | |
case WSAENOBUFS: | |
e = NoResources; | |
break; | |
case WSAEINTR: | |
e = UnknownError; // ### ? | |
break; | |
case WSAEALREADY: | |
// ### ? | |
break; | |
case WSAEADDRNOTAVAIL: | |
e = ConnectionRefused; // ### ? | |
break; | |
case WSAEAFNOSUPPORT: | |
case WSAEFAULT: | |
e = InternalError; | |
break; | |
case WSAEINVAL: | |
break; | |
case WSAECONNREFUSED: | |
e = ConnectionRefused; | |
break; | |
case WSAEISCONN: | |
goto successful; | |
case WSAENETUNREACH: | |
case WSAETIMEDOUT: | |
e = NetworkFailure; | |
break; | |
case WSAENOTSOCK: | |
e = Impossible; | |
break; | |
case WSAEWOULDBLOCK: | |
break; | |
case WSAEACCES: | |
e = Inaccessible; | |
break; | |
case 10107: | |
// Workaround for a problem with the WinSock Proxy Server. See | |
// also support/arc-12/25557 for details on the problem. | |
goto successful; | |
default: | |
e = UnknownError; | |
break; | |
} | |
return false; | |
} | |
successful: | |
fetchConnectionParameters(); | |
return true; | |
} | |
bool Q3SocketDevice::bind( const QHostAddress &address, quint16 port ) | |
{ | |
if ( !isValid() ) | |
return false; | |
int r; | |
struct sockaddr_in a4; | |
#if !defined(QT_NO_IPV6) | |
qt_sockaddr_in6 a6; | |
if ( initialized >= 0x20 && address.isIPv6Address() ) { | |
memset( &a6, 0, sizeof(a6) ); | |
a6.sin6_family = AF_INET6; | |
a6.sin6_port = htons( port ); | |
Q_IPV6ADDR tmp = address.toIPv6Address(); | |
memcpy( &a6.sin6_addr.qt_s6_addr, &tmp, sizeof(tmp) ); | |
r = ::bind( fd, (struct sockaddr *)&a6, sizeof(struct qt_sockaddr_storage) ); | |
} else | |
#endif | |
if ( address.isIPv4Address() ) { | |
memset( &a4, 0, sizeof(a4) ); | |
a4.sin_family = AF_INET; | |
a4.sin_port = htons( port ); | |
a4.sin_addr.s_addr = htonl( address.toIPv4Address() ); | |
r = ::bind( fd, (struct sockaddr*)&a4, sizeof(struct sockaddr_in) ); | |
} else { | |
e = Impossible; | |
return false; | |
} | |
if ( r == SOCKET_ERROR ) { | |
switch( WSAGetLastError() ) { | |
case WSANOTINITIALISED: | |
e = Impossible; | |
break; | |
case WSAENETDOWN: | |
e = NetworkFailure; | |
break; | |
case WSAEACCES: | |
e = Inaccessible; | |
break; | |
case WSAEADDRNOTAVAIL: | |
e = Inaccessible; | |
break; | |
case WSAEFAULT: | |
e = InternalError; | |
break; | |
case WSAEINPROGRESS: | |
case WSAENOBUFS: | |
e = NoResources; | |
break; | |
case WSAEADDRINUSE: | |
case WSAEINVAL: | |
e = AlreadyBound; | |
break; | |
case WSAENOTSOCK: | |
e = Impossible; | |
break; | |
default: | |
e = UnknownError; | |
break; | |
} | |
return false; | |
} | |
fetchConnectionParameters(); | |
return true; | |
} | |
bool Q3SocketDevice::listen( int backlog ) | |
{ | |
if ( !isValid() ) | |
return false; | |
if ( ::listen( fd, backlog ) >= 0 ) | |
return true; | |
if ( !e ) | |
e = Impossible; | |
return false; | |
} | |
int Q3SocketDevice::accept() | |
{ | |
if ( !isValid() ) | |
return -1; | |
#if !defined(QT_NO_IPV6) | |
struct qt_sockaddr_storage a; | |
#else | |
struct sockaddr a; | |
#endif | |
SOCKLEN_T l = sizeof(a); | |
bool done; | |
SOCKET s; | |
do { | |
s = ::accept( fd, (struct sockaddr*)&a, &l ); | |
// we'll blithely throw away the stuff accept() wrote to a | |
done = true; | |
if ( s == INVALID_SOCKET && e == NoError ) { | |
switch( WSAGetLastError() ) { | |
case WSAEINTR: | |
done = false; | |
break; | |
case WSANOTINITIALISED: | |
e = Impossible; | |
break; | |
case WSAENETDOWN: | |
case WSAEOPNOTSUPP: | |
// in all these cases, an error happened during connection | |
// setup. we're not interested in what happened, so we | |
// just treat it like the client-closed-quickly case. | |
break; | |
case WSAEFAULT: | |
e = InternalError; | |
break; | |
case WSAEMFILE: | |
case WSAEINPROGRESS: | |
case WSAENOBUFS: | |
e = NoResources; | |
break; | |
case WSAEINVAL: | |
case WSAENOTSOCK: | |
e = Impossible; | |
break; | |
case WSAEWOULDBLOCK: | |
break; | |
default: | |
e = UnknownError; | |
break; | |
} | |
} | |
} while (!done); | |
return s; | |
} | |
qint64 Q3SocketDevice::bytesAvailable() const | |
{ | |
if ( !isValid() ) | |
return -1; | |
u_long nbytes = 0; | |
if ( ::ioctlsocket(fd, FIONREAD, &nbytes) < 0 ) | |
return -1; | |
// ioctlsocket sometimes reports 1 byte available for datagrams | |
// while the following recvfrom returns -1 and claims connection | |
// was reset (udp is connectionless). so we peek one byte to | |
// catch this case and return 0 bytes available if recvfrom | |
// fails. | |
if (nbytes == 1 && t == Datagram) { | |
char c; | |
if (::recvfrom(fd, &c, sizeof(c), MSG_PEEK, 0, 0) == SOCKET_ERROR) | |
return 0; | |
} | |
return nbytes; | |
} | |
Q_LONG Q3SocketDevice::waitForMore( int msecs, bool *timeout ) const | |
{ | |
if ( !isValid() ) | |
return -1; | |
fd_set fds; | |
memset(&fds, 0, sizeof(fd_set)); | |
fds.fd_count = 1; | |
fds.fd_array[0] = fd; | |
struct timeval tv; | |
tv.tv_sec = msecs / 1000; | |
tv.tv_usec = (msecs % 1000) * 1000; | |
int rv = select( fd+1, &fds, 0, 0, msecs < 0 ? 0 : &tv ); | |
if ( rv < 0 ) | |
return -1; | |
if ( timeout ) { | |
if ( rv == 0 ) | |
*timeout = true; | |
else | |
*timeout = false; | |
} | |
return bytesAvailable(); | |
} | |
qint64 Q3SocketDevice::readData( char *data, qint64 maxlen ) | |
{ | |
#if defined(QT_CHECK_NULL) | |
if ( data == 0 && maxlen != 0 ) { | |
qWarning( "Q3SocketDevice::readBlock: Null pointer error" ); | |
} | |
#endif | |
#if defined(QT_CHECK_STATE) | |
if ( !isValid() ) { | |
qWarning( "Q3SocketDevice::readBlock: Invalid socket" ); | |
return -1; | |
} | |
if ( !isOpen() ) { | |
qWarning( "Q3SocketDevice::readBlock: Device is not open" ); | |
return -1; | |
} | |
if ( !isReadable() ) { | |
qWarning( "Q3SocketDevice::readBlock: Read operation not permitted" ); | |
return -1; | |
} | |
#endif | |
qint64 r = 0; | |
if ( t == Datagram ) { | |
#if !defined(QT_NO_IPV6) | |
// With IPv6 support, we must be prepared to receive both IPv4 | |
// and IPv6 packets. The generic SOCKADDR_STORAGE (struct | |
// sockaddr_storage on unix) replaces struct sockaddr. | |
struct qt_sockaddr_storage a; | |
#else | |
struct sockaddr_in a; | |
#endif | |
memset( &a, 0, sizeof(a) ); | |
SOCKLEN_T sz; | |
sz = sizeof( a ); | |
r = ::recvfrom( fd, data, maxlen, 0, (struct sockaddr *)&a, &sz ); | |
qt_socket_getportaddr( (struct sockaddr *)(&a), &pp, &pa ); | |
} else { | |
r = ::recv( fd, data, maxlen, 0 ); | |
} | |
if ( r == 0 && t == Stream && maxlen > 0 ) { | |
if ( WSAGetLastError() != WSAEWOULDBLOCK ) { | |
// connection closed | |
close(); | |
} | |
} else if ( r == SOCKET_ERROR && e == NoError ) { | |
switch( WSAGetLastError() ) { | |
case WSANOTINITIALISED: | |
e = Impossible; | |
break; | |
case WSAECONNABORTED: | |
close(); | |
r = 0; | |
break; | |
case WSAETIMEDOUT: | |
case WSAECONNRESET: | |
/* | |
From msdn doc: | |
On a UDP datagram socket this error would indicate that a previous | |
send operation resulted in an ICMP "Port Unreachable" message. | |
So we should not close this socket just because one sendto failed. | |
*/ | |
if ( t != Datagram ) | |
close(); // connection closed | |
r = 0; | |
break; | |
case WSAENETDOWN: | |
case WSAENETRESET: | |
e = NetworkFailure; | |
break; | |
case WSAEFAULT: | |
case WSAENOTCONN: | |
case WSAESHUTDOWN: | |
case WSAEINVAL: | |
e = Impossible; | |
break; | |
case WSAEINTR: | |
// ### ? | |
r = 0; | |
break; | |
case WSAEINPROGRESS: | |
e = NoResources; | |
break; | |
case WSAENOTSOCK: | |
e = Impossible; | |
break; | |
case WSAEOPNOTSUPP: | |
e = InternalError; // ### ? | |
break; | |
case WSAEWOULDBLOCK: | |
break; | |
case WSAEMSGSIZE: | |
e = NoResources; // ### ? | |
break; | |
case WSAEISCONN: | |
// ### ? | |
r = 0; | |
break; | |
default: | |
e = UnknownError; | |
break; | |
} | |
} | |
return r; | |
} | |
qint64 Q3SocketDevice::writeData( const char *data, qint64 len ) | |
{ | |
if ( data == 0 && len != 0 ) { | |
#if defined(QT_CHECK_NULL) || defined(QSOCKETDEVICE_DEBUG) | |
qWarning( "Q3SocketDevice::writeBlock: Null pointer error" ); | |
#endif | |
return -1; | |
} | |
if ( !isValid() ) { | |
#if defined(QT_CHECK_STATE) || defined(QSOCKETDEVICE_DEBUG) | |
qWarning( "Q3SocketDevice::writeBlock: Invalid socket" ); | |
#endif | |
return -1; | |
} | |
if ( !isOpen() ) { | |
#if defined(QT_CHECK_STATE) || defined(QSOCKETDEVICE_DEBUG) | |
qWarning( "Q3SocketDevice::writeBlock: Device is not open" ); | |
#endif | |
return -1; | |
} | |
if ( !isWritable() ) { | |
#if defined(QT_CHECK_STATE) || defined(QSOCKETDEVICE_DEBUG) | |
qWarning( "Q3SocketDevice::writeBlock: Write operation not permitted" ); | |
#endif | |
return -1; | |
} | |
bool done = false; | |
qint64 r = 0; | |
while ( !done ) { | |
// Don't write more than 64K (see Knowledge Base Q201213). | |
r = ::send( fd, data, ( len>64*1024 ? 64*1024 : len ), 0 ); | |
done = true; | |
if ( r == SOCKET_ERROR && e == NoError ) {//&& errno != WSAEAGAIN ) { | |
switch( WSAGetLastError() ) { | |
case WSANOTINITIALISED: | |
e = Impossible; | |
break; | |
case WSAENETDOWN: | |
case WSAEACCES: | |
case WSAENETRESET: | |
case WSAESHUTDOWN: | |
case WSAEHOSTUNREACH: | |
e = NetworkFailure; | |
break; | |
case WSAECONNABORTED: | |
case WSAECONNRESET: | |
// connection closed | |
close(); | |
r = 0; | |
break; | |
case WSAEINTR: | |
done = false; | |
break; | |
case WSAEINPROGRESS: | |
e = NoResources; | |
// ### perhaps try it later? | |
break; | |
case WSAEFAULT: | |
case WSAEOPNOTSUPP: | |
e = InternalError; | |
break; | |
case WSAENOBUFS: | |
// ### try later? | |
break; | |
case WSAEMSGSIZE: | |
e = NoResources; | |
break; | |
case WSAENOTCONN: | |
case WSAENOTSOCK: | |
case WSAEINVAL: | |
e = Impossible; | |
break; | |
case WSAEWOULDBLOCK: | |
r = 0; | |
break; | |
default: | |
e = UnknownError; | |
break; | |
} | |
} | |
} | |
return r; | |
} | |
Q_LONG Q3SocketDevice::writeBlock( const char * data, Q_ULONG len, | |
const QHostAddress & host, quint16 port ) | |
{ | |
if ( t != Datagram ) { | |
#if defined(QT_CHECK_STATE) || defined(QSOCKETDEVICE_DEBUG) | |
qWarning( "Q3SocketDevice::sendBlock: Not datagram" ); | |
#endif | |
return -1; // for now - later we can do t/tcp | |
} | |
if ( data == 0 && len != 0 ) { | |
#if defined(QT_CHECK_NULL) || defined(QSOCKETDEVICE_DEBUG) | |
qWarning( "Q3SocketDevice::sendBlock: Null pointer error" ); | |
#endif | |
return -1; | |
} | |
if ( !isValid() ) { | |
#if defined(QT_CHECK_STATE) || defined(QSOCKETDEVICE_DEBUG) | |
qWarning( "Q3SocketDevice::sendBlock: Invalid socket" ); | |
#endif | |
return -1; | |
} | |
if ( !isOpen() ) { | |
#if defined(QT_CHECK_STATE) || defined(QSOCKETDEVICE_DEBUG) | |
qWarning( "Q3SocketDevice::sendBlock: Device is not open" ); | |
#endif | |
return -1; | |
} | |
if ( !isWritable() ) { | |
#if defined(QT_CHECK_STATE) || defined(QSOCKETDEVICE_DEBUG) | |
qWarning( "Q3SocketDevice::sendBlock: Write operation not permitted" ); | |
#endif | |
return -1; | |
} | |
struct sockaddr_in a4; | |
struct sockaddr *aa; | |
SOCKLEN_T slen; | |
#if !defined(QT_NO_IPV6) | |
qt_sockaddr_in6 a6; | |
if ( initialized >= 0x20 && host.isIPv6Address() ) { | |
memset( &a6, 0, sizeof(a6) ); | |
a6.sin6_family = AF_INET6; | |
a6.sin6_port = htons( port ); | |
Q_IPV6ADDR tmp = host.toIPv6Address(); | |
memcpy( &a6.sin6_addr.qt_s6_addr, &tmp, sizeof(tmp) ); | |
slen = sizeof( a6 ); | |
aa = (struct sockaddr *)&a6; | |
} else | |
#endif | |
if ( host.isIPv4Address() ) { | |
memset( &a4, 0, sizeof(a4) ); | |
a4.sin_family = AF_INET; | |
a4.sin_port = htons( port ); | |
a4.sin_addr.s_addr = htonl( host.toIPv4Address() ); | |
slen = sizeof(a4); | |
aa = (struct sockaddr *)&a4; | |
} else { | |
e = Impossible; | |
return -1; | |
} | |
// we'd use MSG_DONTWAIT + MSG_NOSIGNAL if Stevens were right. | |
// but apparently Stevens and most implementors disagree | |
bool done = false; | |
qint64 r = 0; | |
while ( !done ) { | |
r = ::sendto( fd, data, len, 0, aa, slen ); | |
done = true; | |
if ( r == SOCKET_ERROR && e == NoError ) {//&& e != EAGAIN ) { | |
switch( WSAGetLastError() ) { | |
case WSANOTINITIALISED: | |
e = Impossible; | |
break; | |
case WSAENETDOWN: | |
case WSAEACCES: | |
case WSAENETRESET: | |
case WSAESHUTDOWN: | |
case WSAEHOSTUNREACH: | |
case WSAECONNABORTED: | |
case WSAECONNRESET: | |
case WSAEADDRNOTAVAIL: | |
case WSAENETUNREACH: | |
case WSAETIMEDOUT: | |
e = NetworkFailure; | |
break; | |
case WSAEINTR: | |
done = false; | |
break; | |
case WSAEINPROGRESS: | |
e = NoResources; | |
// ### perhaps try it later? | |
break; | |
case WSAEFAULT: | |
case WSAEOPNOTSUPP: | |
case WSAEAFNOSUPPORT: | |
e = InternalError; | |
break; | |
case WSAENOBUFS: | |
case WSAEMSGSIZE: | |
e = NoResources; | |
break; | |
case WSAENOTCONN: | |
case WSAENOTSOCK: | |
case WSAEINVAL: | |
case WSAEDESTADDRREQ: | |
e = Impossible; | |
break; | |
case WSAEWOULDBLOCK: | |
r = 0; | |
break; | |
default: | |
e = UnknownError; | |
break; | |
} | |
} | |
} | |
return r; | |
} | |
void Q3SocketDevice::fetchConnectionParameters() | |
{ | |
if ( !isValid() ) { | |
p = 0; | |
a = QHostAddress(); | |
pp = 0; | |
pa = QHostAddress(); | |
return; | |
} | |
#if !defined (QT_NO_IPV6) | |
struct qt_sockaddr_storage sa; | |
#else | |
struct sockaddr_in sa; | |
#endif | |
memset( &sa, 0, sizeof(sa) ); | |
SOCKLEN_T sz; | |
sz = sizeof( sa ); | |
if ( !::getsockname( fd, (struct sockaddr *)(&sa), &sz ) ) | |
qt_socket_getportaddr( (struct sockaddr *)(&sa), &p, &a ); | |
pp = 0; | |
pa = QHostAddress(); | |
} | |
void Q3SocketDevice::fetchPeerConnectionParameters() | |
{ | |
// do the getpeername() lazy on Windows (sales/arc-18/37759 claims that | |
// there will be problems otherwise if you use MS Proxy server) | |
#if !defined (QT_NO_IPV6) | |
struct qt_sockaddr_storage sa; | |
#else | |
struct sockaddr_in sa; | |
#endif | |
memset( &sa, 0, sizeof(sa) ); | |
SOCKLEN_T sz; | |
sz = sizeof( sa ); | |
if ( !::getpeername( fd, (struct sockaddr *)(&sa), &sz ) ) | |
qt_socket_getportaddr( (struct sockaddr *)(&sa), &pp, &pa ); | |
} | |
quint16 Q3SocketDevice::peerPort() const | |
{ | |
if ( pp==0 && isValid() ) { | |
Q3SocketDevice *that = (Q3SocketDevice*)this; // mutable | |
that->fetchPeerConnectionParameters(); | |
} | |
return pp; | |
} | |
QHostAddress Q3SocketDevice::peerAddress() const | |
{ | |
if ( pp==0 && isValid() ) { | |
Q3SocketDevice *that = (Q3SocketDevice*)this; // mutable | |
that->fetchPeerConnectionParameters(); | |
} | |
return pa; | |
} | |
QT_END_NAMESPACE |