summaryrefslogtreecommitdiffstats
path: root/src/qt3support/network/q3socketdevice_win.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/qt3support/network/q3socketdevice_win.cpp')
-rw-r--r--src/qt3support/network/q3socketdevice_win.cpp1068
1 files changed, 1068 insertions, 0 deletions
diff --git a/src/qt3support/network/q3socketdevice_win.cpp b/src/qt3support/network/q3socketdevice_win.cpp
new file mode 100644
index 0000000..f2d034b
--- /dev/null
+++ b/src/qt3support/network/q3socketdevice_win.cpp
@@ -0,0 +1,1068 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.0, 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.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "q3socketdevice.h"
+#include "qwindowdefs.h"
+#include "qdatetime.h"
+
+#include <qcoreapplication.h>
+
+#include <string.h>
+
+#if defined (QT_NO_IPV6)
+# include <windows.h>
+# include <winsock.h>
+#else
+# if defined (Q_CC_BOR) || defined (Q_CC_GNU)
+# include <winsock2.h>
+# elif defined (Q_CC_INTEL)
+# include <winsock.h>
+# else
+# include <windows.h>
+# if defined(Q_OS_WINCE)
+# include <winsock.h>
+# endif
+# 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