diff options
Diffstat (limited to 'tqtinterface/qt4/src/network/tqsocketdevice_unix.cpp')
-rw-r--r-- | tqtinterface/qt4/src/network/tqsocketdevice_unix.cpp | 1269 |
1 files changed, 1269 insertions, 0 deletions
diff --git a/tqtinterface/qt4/src/network/tqsocketdevice_unix.cpp b/tqtinterface/qt4/src/network/tqsocketdevice_unix.cpp new file mode 100644 index 0000000..a0bf65d --- /dev/null +++ b/tqtinterface/qt4/src/network/tqsocketdevice_unix.cpp @@ -0,0 +1,1269 @@ +/**************************************************************************** +** +** Implementation of TQSocketDevice class. +** +** Created : 970521 +** +** Copyright (C) 2010 Timothy Pearson and (C) 1992-2008 Trolltech ASA. +** +** This file is part of the network module of the TQt GUI Toolkit. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing requirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.TQPL +** included in the packaging of this file. Licensees holding valid TQt +** Commercial licenses may use this file in accordance with the TQt +** Commercial License Agreement provided with the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#include "tqplatformdefs.h" + +// Almost always the same. If not, specify in qplatformdefs.h. +#if !defined(TQT_SOCKOPTLEN_T) +# define TQT_SOCKOPTLEN_T TQT_SOCKLEN_T +#endif + +// Tru64 redefines accept -> _accept with _XOPEN_SOURCE_EXTENDED +static inline int qt_socket_accept(int s, struct sockaddr *addr, TQT_SOCKLEN_T *addrlen) +{ return ::accept(s, addr, addrlen); } +#if defined(accept) +# undef accept +#endif + +// Solaris redefines bind -> __xnet_bind with _XOPEN_SOURCE_EXTENDED +static inline int qt_socket_bind(int s, struct sockaddr *addr, TQT_SOCKLEN_T addrlen) +{ return ::bind(s, addr, addrlen); } +#if defined(bind) +# undef bind +#endif + +// Solaris redefines connect -> __xnet_connect with _XOPEN_SOURCE_EXTENDED +static inline int qt_socket_connect(int s, struct sockaddr *addr, TQT_SOCKLEN_T addrlen) +{ return ::connect(s, addr, addrlen); } +#if defined(connect) +# undef connect +#endif + +// UnixWare 7 redefines listen -> _listen +static inline int qt_socket_listen(int s, int backlog) +{ return ::listen(s, backlog); } +#if defined(listen) +# undef listen +#endif + +// UnixWare 7 redefines socket -> _socket +static inline int qt_socket_socket(int domain, int type, int protocol) +{ return ::socket(domain, type, protocol); } +#if defined(socket) +# undef socket +#endif + +#include "tqsocketdevice.h" + +#ifndef TQT_NO_NETWORK + +#include "tqwindowdefs.h" + +#include <errno.h> +#include <sys/types.h> + + +static inline void qt_socket_getportaddr( struct sockaddr *sa, + TQ_UINT16 *port, TQHostAddress *addr ) +{ +#if !defined(TQT_NO_IPV6) + if ( sa->sa_family == AF_INET6 ) { + struct sockaddr_in6 *sa6 = ( struct sockaddr_in6 * )sa; + TQ_IPV6ADDR tmp; + memcpy( &tmp, &sa6->sin6_addr.s6_addr, sizeof(tmp) ); + TQHostAddress a( tmp ); + *addr = a; + *port = ntohs( sa6->sin6_port ); + return; + } +#endif + struct sockaddr_in *sa4 = (struct sockaddr_in *)sa; + TQHostAddress a( ntohl( sa4->sin_addr.s_addr ) ); + *port = ntohs( sa4->sin_port ); + *addr = TQHostAddress( ntohl( sa4->sin_addr.s_addr ) ); + return; +} + +#ifdef USE_QT4 + +qint64 TQSocketDevice::readData( char *data, qint64 maxlen ) +{ +#if defined(QT_CHECK_NULL) + if ( data == 0 && maxlen != 0 ) { + qWarning( "TQSocketDevice::readBlock: Null pointer error" ); + } +#endif +#if defined(QT_CHECK_STATE) + if ( !isValid() ) { + qWarning( "TQSocketDevice::readBlock: Invalid socket" ); + return -1; + } + if ( !isOpen() ) { + qWarning( "TQSocketDevice::readBlock: Device is not open" ); + return -1; + } + if ( !isReadable() ) { + qWarning( "TQSocketDevice::readBlock: Read operation not permitted" ); + return -1; + } +#endif + bool done = false; + int r = 0; + while ( done == false ) { + if ( t == Datagram ) { +#if !defined(QT_NO_IPV6) + struct sockaddr_storage aa; +#else + struct sockaddr_in aa; +#endif + memset( &aa, 0, sizeof(aa) ); + QT_SOCKLEN_T sz; + sz = sizeof( aa ); + r = ::recvfrom( fd, data, maxlen, 0, + (struct sockaddr *)&aa, &sz ); + + qt_socket_getportaddr( (struct sockaddr *)&aa, &pp, &pa); + + } else { + r = ::read( fd, data, maxlen ); + } + done = true; + if ( r == 0 && t == Stream && maxlen > 0 ) { + // connection closed + close(); + } else if ( r >= 0 || errno == EAGAIN || errno == EWOULDBLOCK ) { + // nothing + } else if ( errno == EINTR ) { + done = false; + } else if ( e == NoError ) { + switch( errno ) { + case EIO: + case EISDIR: + case EBADF: + case EINVAL: + case EFAULT: + case ENOTCONN: + case ENOTSOCK: + e = Impossible; + break; +#if defined(ENONET) + case ENONET: +#endif + case EHOSTUNREACH: + case ENETDOWN: + case ENETUNREACH: + case ETIMEDOUT: + e = NetworkFailure; + break; + case EPIPE: + case ECONNRESET: + // connection closed + close(); + r = 0; + break; + default: + e = UnknownError; + break; + } + } + } + return r; +} + + +qint64 TQSocketDevice::writeData( const char *data, qint64 len ) +{ + if ( data == 0 && len != 0 ) { +#if defined(QT_CHECK_NULL) || defined(QSOCKETDEVICE_DEBUG) + qWarning( "TQSocketDevice::writeBlock: Null pointer error" ); +#endif + return -1; + } + if ( !isValid() ) { +#if defined(QT_CHECK_STATE) || defined(QSOCKETDEVICE_DEBUG) + qWarning( "TQSocketDevice::writeBlock: Invalid socket" ); +#endif + return -1; + } + if ( !isOpen() ) { +#if defined(QT_CHECK_STATE) || defined(QSOCKETDEVICE_DEBUG) + qWarning( "TQSocketDevice::writeBlock: Device is not open" ); +#endif + return -1; + } + if ( !isWritable() ) { +#if defined(QT_CHECK_STATE) || defined(QSOCKETDEVICE_DEBUG) + qWarning( "TQSocketDevice::writeBlock: Write operation not permitted" ); +#endif + return -1; + } + bool done = false; + int r = 0; + bool timeout; + while ( !done ) { + r = ::write( fd, data, len ); + done = true; + if ( r < 0 && e == NoError && + errno != EAGAIN && errno != EWOULDBLOCK ) { + switch( errno ) { + case EINTR: // signal - call read() or whatever again + done = false; + break; + case EPIPE: + case ECONNRESET: + // connection closed + close(); + r = 0; + break; + case ENOSPC: + case EIO: + case EISDIR: + case EBADF: + case EINVAL: + case EFAULT: + case ENOTCONN: + case ENOTSOCK: + e = Impossible; + break; +#if defined(ENONET) + case ENONET: +#endif + case EHOSTUNREACH: + case ENETDOWN: + case ENETUNREACH: + case ETIMEDOUT: + e = NetworkFailure; + break; + default: + e = UnknownError; + break; + } + } else if ( waitForMore( 0, &timeout ) == 0 ) { + if ( !timeout ) { + // connection closed + close(); + } + } + } + return r; +} + +#endif // USE_QT4 + + +//#define TQSOCKETDEVICE_DEBUG + +// internal +void TQSocketDevice::init() +{ +} + + +TQSocketDevice::Protocol TQSocketDevice::getProtocol() const +{ + if ( isValid() ) { +#if !defined (TQT_NO_IPV6) + struct sockaddr_storage sa; +#else + struct sockaddr sa; +#endif + memset( &sa, 0, sizeof(sa) ); + TQT_SOCKLEN_T sz = sizeof( sa ); +#if !defined (TQT_NO_IPV6) + struct sockaddr *sap = reinterpret_cast<struct sockaddr *>(&sa); + if ( !::getsockname(fd, sap, &sz) ) { + switch ( sap->sa_family ) { + case AF_INET: + return IPv4; + case AF_INET6: + return IPv6; + default: + return Unknown; + } + } +#else + if ( !::getsockname(fd, &sa, &sz) ) { + switch ( sa.sa_family ) { + case AF_INET: + return IPv4; + default: + return Unknown; + } + } +#endif + } + return Unknown; +} + +/*! + Creates a new socket identifier. Returns -1 if there is a failure + to create the new identifier; error() explains why. + + \sa setSocket() +*/ + +int TQSocketDevice::createNewSocket() +{ +#if !defined(TQT_NO_IPV6) + int s = qt_socket_socket( protocol() == IPv6 ? AF_INET6 : AF_INET, + t == Datagram ? SOCK_DGRAM : SOCK_STREAM, 0 ); +#else + int s = qt_socket_socket( AF_INET, t==Datagram?SOCK_DGRAM:SOCK_STREAM, 0 ); +#endif + if ( s < 0 ) { + switch( errno ) { + case EPROTONOSUPPORT: + e = InternalError; // 0 is supposed to work for both types + break; + case ENFILE: + e = NoFiles; // special case for this + break; + case EACCES: + e = Inaccessible; + break; + case ENOBUFS: + case ENOMEM: + e = NoResources; + break; + case EINVAL: + e = Impossible; + break; + default: + e = UnknownError; + break; + } + } else { + // ensure that the socket is closed on exec..() after being dup()'ed by + // fork() in TQProcess. + ::fcntl(s, F_SETFD, FD_CLOEXEC); + return s; + } + return -1; +} + +/*! + \reimp + + Closes the socket and sets the socket identifier to -1 (invalid). + + (This function ignores errors; if there are any then a file + descriptor leakage might result. As far as we know, the only error + that can arise is EBADF, and that would of course not cause + leakage. There may be OS-specfic errors that we haven't come + across, however.) + + \sa open() +*/ +void TQSocketDevice::close() +{ + if ( fd == -1 || !isOpen() ) // already closed + return; + setFlags( IO_Sequential ); + resetqStatus(); + setState( 0 ); + ::close( fd ); +#if defined(TQSOCKETDEVICE_DEBUG) + qDebug( "TQSocketDevice::close: Closed socket %x", fd ); +#endif + fd = -1; + fetchConnectionParameters(); +} + + +/*! + Returns TRUE if the socket is valid and in blocking mode; + otherwise returns FALSE. + + Note that this function does not set error(). + + \warning On Windows, this function always returns TRUE since the + ioctlsocket() function is broken. + + \sa setBlocking(), isValid() +*/ +bool TQSocketDevice::blocking() const +{ + if ( !isValid() ) + return TRUE; + int s = fcntl(fd, F_GETFL, 0); + return !(s >= 0 && ((s & O_NDELAY) != 0)); +} + + +/*! + Makes the socket blocking if \a enable is TRUE or nonblocking if + \a enable is FALSE. + + Sockets are blocking by default, but we recommend using + nonblocking socket operations, especially for GUI programs that + need to be responsive. + + \warning On Windows, this function should be used with care since + whenever you use a TQSocketNotifier on Windows, the socket is + immediately made nonblocking. + + \sa blocking(), isValid() +*/ +void TQSocketDevice::setBlocking( bool enable ) +{ +#if defined(TQSOCKETDEVICE_DEBUG) + qDebug( "TQSocketDevice::setBlocking( %d )", enable ); +#endif + if ( !isValid() ) + return; + int tmp = ::fcntl(fd, F_GETFL, 0); + if ( tmp >= 0 ) + tmp = ::fcntl( fd, F_SETFL, enable ? (tmp&~O_NDELAY) : (tmp|O_NDELAY) ); + if ( tmp >= 0 ) + return; + if ( e ) + return; + switch( errno ) { + case EACCES: + case EBADF: + e = Impossible; + break; + case EFAULT: + case EAGAIN: +#if EAGAIN != EWOULDBLOCK + case EWOULDBLOCK: +#endif + case EDEADLK: + case EINTR: + case EINVAL: + case EMFILE: + case ENOLCK: + case EPERM: + default: + e = UnknownError; + } +} + + +/*! + Returns the value of the socket option \a opt. +*/ +int TQSocketDevice::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 ) { + TQT_SOCKOPTLEN_T len; + len = sizeof(v); + int r = ::getsockopt( fd, SOL_SOCKET, n, (char*)&v, &len ); + if ( r >= 0 ) + return v; + if ( !e ) { + TQSocketDevice *that = (TQSocketDevice*)this; // mutable function + switch( errno ) { + case EBADF: + case ENOTSOCK: + that->e = Impossible; + break; + case EFAULT: + that->e = InternalError; + break; + default: + that->e = UnknownError; + break; + } + } + return -1; + } + return v; +} + + +/*! + Sets the socket option \a opt to \a v. +*/ +void TQSocketDevice::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; + } + if ( ::setsockopt( fd, SOL_SOCKET, n, (char*)&v, sizeof(v)) < 0 && + e == NoError ) { + switch( errno ) { + case EBADF: + case ENOTSOCK: + e = Impossible; + break; + case EFAULT: + e = InternalError; + break; + default: + e = UnknownError; + break; + } + } +} + + +/*! + Connects to the IP address and port specified by \a addr and \a + port. Returns TRUE if it establishes a connection; otherwise returns FALSE. + If it returns FALSE, error() explains why. + + Note that error() commonly returns NoError for non-blocking + sockets; this just means that you can call connect() again in a + little while and it'll probably succeed. +*/ +bool TQSocketDevice::connect( const TQHostAddress &addr, TQ_UINT16 port ) +{ + if ( !isValid() ) + return FALSE; + + pa = addr; + pp = port; + + struct sockaddr_in a4; + struct sockaddr *aa; + TQT_SOCKLEN_T aalen; + +#if !defined(TQT_NO_IPV6) + struct sockaddr_in6 a6; + + if ( addr.isIPv6Address() ) { + memset( &a6, 0, sizeof(a6) ); + a6.sin6_family = AF_INET6; + a6.sin6_port = htons( port ); + TQ_IPV6ADDR ip6 = addr.toIPv6Address(); + memcpy( &a6.sin6_addr.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 = qt_socket_connect( fd, aa, aalen ); + if ( r == 0 ) { + fetchConnectionParameters(); + return TRUE; + } + if ( errno == EISCONN || errno == EALREADY || errno == EINPROGRESS ) { + fetchConnectionParameters(); + return TRUE; + } + if ( e != NoError || errno == EAGAIN || errno == EWOULDBLOCK ) { + return FALSE; + } + switch( errno ) { + case EBADF: + case ENOTSOCK: + e = Impossible; + break; + case EFAULT: + case EAFNOSUPPORT: + e = InternalError; + break; + case ECONNREFUSED: + e = ConnectionRefused; + break; + case ETIMEDOUT: + case ENETUNREACH: + e = NetworkFailure; + break; + case EADDRINUSE: + e = NoResources; + break; + case EACCES: + case EPERM: + e = Inaccessible; + break; + default: + e = UnknownError; + break; + } + return FALSE; +} + + +/*! + Assigns a name to an unnamed socket. The name is the host address + \a address and the port number \a port. If the operation succeeds, + bind() returns TRUE; otherwise it returns FALSE without changing + what port() and address() return. + + bind() is used by servers for setting up incoming connections. + Call bind() before listen(). +*/ +bool TQSocketDevice::bind( const TQHostAddress &address, TQ_UINT16 port ) +{ + if ( !isValid() ) + return FALSE; + int r; + struct sockaddr_in a4; +#if !defined(TQT_NO_IPV6) + struct sockaddr_in6 a6; + + if ( address.isIPv6Address() ) { + memset( &a6, 0, sizeof(a6) ); + a6.sin6_family = AF_INET6; + a6.sin6_port = htons( port ); + TQ_IPV6ADDR tmp = address.toIPv6Address(); + memcpy( &a6.sin6_addr.s6_addr, &tmp, sizeof(tmp) ); + + r = qt_socket_bind( fd, (struct sockaddr *)&a6, sizeof(a6) ); + } 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 = qt_socket_bind( fd, (struct sockaddr*)&a4, sizeof(a4) ); + } else { + e = Impossible; + return FALSE; + } + + if ( r < 0 ) { + switch( errno ) { + case EINVAL: + e = AlreadyBound; + break; + case EACCES: + e = Inaccessible; + break; + case ENOMEM: + e = NoResources; + break; + case EFAULT: // a was illegal + case ENAMETOOLONG: // sz was wrong + e = InternalError; + break; + case EBADF: // AF_UNIX only + case ENOTSOCK: // AF_UNIX only + case EROFS: // AF_UNIX only + case ENOENT: // AF_UNIX only + case ENOTDIR: // AF_UNIX only + case ELOOP: // AF_UNIX only + e = Impossible; + break; + default: + e = UnknownError; + break; + } + return FALSE; + } + fetchConnectionParameters(); + return TRUE; +} + + +/*! + Specifies how many pending connections a server socket can have. + Returns TRUE if the operation was successful; otherwise returns + FALSE. A \a backlog value of 50 is quite common. + + The listen() call only applies to sockets where type() is \c + Stream, i.e. not to \c Datagram sockets. listen() must not be + called before bind() or after accept(). + + \sa bind(), accept() +*/ +bool TQSocketDevice::listen( int backlog ) +{ + if ( !isValid() ) + return FALSE; + if ( qt_socket_listen( fd, backlog ) >= 0 ) + return TRUE; + if ( !e ) + e = Impossible; + return FALSE; +} + + +/*! + Extracts the first connection from the queue of pending + connections for this socket and returns a new socket identifier. + Returns -1 if the operation failed. + + \sa bind(), listen() +*/ +int TQSocketDevice::accept() +{ + if ( !isValid() ) + return -1; + +#if !defined (TQT_NO_IPV6) + struct sockaddr_storage aa; +#else + struct sockaddr aa; +#endif + TQT_SOCKLEN_T l = sizeof( aa ); + bool done; + int s; + do { + s = qt_socket_accept( fd, (struct sockaddr*)&aa, &l ); + // we'll blithely throw away the stuff accept() wrote to aa + done = TRUE; + if ( s < 0 && e == NoError ) { + switch( errno ) { + case EINTR: + done = FALSE; + break; +#if defined(EPROTO) + case EPROTO: +#endif +#if defined(ENONET) + case ENONET: +#endif + case ENOPROTOOPT: + case EHOSTDOWN: + case EOPNOTSUPP: + case EHOSTUNREACH: + case ENETDOWN: + case ENETUNREACH: + case ETIMEDOUT: + // 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. + case EPERM: + // firewalling wouldn't let us accept. we treat it like + // the client-closed-quickly case. + case EAGAIN: +#if EAGAIN != EWOULDBLOCK + case EWOULDBLOCK: +#endif + // the client closed the connection before we got around + // to accept()ing it. + break; + case EBADF: + case ENOTSOCK: + e = Impossible; + break; + case EFAULT: + e = InternalError; + break; + case ENOMEM: + case ENOBUFS: + e = NoResources; + break; + default: + e = UnknownError; + break; + } + } + } while (!done); + // ensure that the socket is closed on exec..() after being dup()'ed by + // fork() in TQProcess. + ::fcntl(s, F_SETFD, FD_CLOEXEC); + return s; +} + + +/*! + Returns the number of bytes available for reading, or -1 if an + error occurred. + + \warning On Microsoft Windows, we use the ioctlsocket() function + to determine the number of bytes queued on the socket. According + to Microsoft (KB Q125486), ioctlsocket() sometimes returns an + incorrect number. The only safe way to determine the amount of + data on the socket is to read it using readBlock(). TQSocket has + workarounds to deal with this problem. +*/ +#ifdef USE_QT4 +qint64 TQSocketDevice::bytesAvailable() const +#else // USE_QT4 +TQ_LONG TQSocketDevice::bytesAvailable() const +#endif // USE_QT4 +{ + if ( !isValid() ) + return -1; + + /* + Apparently, there is not consistency among different operating + systems on how to use FIONREAD. + + FreeBSD, Linux and Solaris all expect the 3rd argument to + ioctl() to be an int, which is normally 32-bit even on 64-bit + machines. + + IRIX, on the other hand, expects a size_t, which is 64-bit on + 64-bit machines. + + So, the solution is to use size_t initialized to zero to make + sure all bits are set to zero, preventing underflow with the + FreeBSD/Linux/Solaris ioctls. + */ + size_t nbytes = 0; + // gives shorter than true amounts on Unix domain sockets. + if ( ::ioctl(fd, FIONREAD, (char*)&nbytes) < 0 ) + return -1; + return (TQ_LONG) *((int *) &nbytes); +} + + +/*! + Wait up to \a msecs milliseconds for more data to be available. If + \a msecs is -1 the call will block indefinitely. + + Returns the number of bytes available for reading, or -1 if an + error occurred. + + If \a timeout is non-null and no error occurred (i.e. it does not + return -1): this function sets \a *timeout to TRUE, if the reason + for returning was that the timeout was reached; otherwise it sets + \a *timeout to FALSE. This is useful to tqfind out if the peer + closed the connection. + + \warning This is a blocking call and should be avoided in event + driven applications. + + \sa bytesAvailable() +*/ +TQ_LONG TQSocketDevice::waitForMore( int msecs, bool *timeout ) const +{ + if ( !isValid() ) + return -1; + if ( fd >= FD_SETSIZE ) + return -1; + + fd_set fds; + struct timeval tv; + + FD_ZERO( &fds ); + FD_SET( fd, &fds ); + + 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(); +} + + +/*! + Reads \a maxlen bytes from the socket into \a data and returns the + number of bytes read. Returns -1 if an error occurred. Returning 0 + is not an error. For Stream sockets, 0 is returned when the remote + host closes the connection. For Datagram sockets, 0 is a valid + datagram size. +*/ +TQ_LONG TQSocketDevice::readBlock( char *data, TQ_ULONG maxlen ) +{ +#if defined(TQT_CHECK_NULL) + if ( data == 0 && maxlen != 0 ) { + qWarning( "TQSocketDevice::readBlock: Null pointer error" ); + } +#endif +#if defined(TQT_CHECK_STATE) + if ( !isValid() ) { + qWarning( "TQSocketDevice::readBlock: Invalid socket" ); + return -1; + } + if ( !isOpen() ) { + qWarning( "TQSocketDevice::readBlock: Device is not open" ); + return -1; + } + if ( !isReadable() ) { + qWarning( "TQSocketDevice::readBlock: Read operation not permitted" ); + return -1; + } +#endif + bool done = FALSE; + int r = 0; + while ( done == FALSE ) { + if ( t == Datagram ) { +#if !defined(TQT_NO_IPV6) + struct sockaddr_storage aa; +#else + struct sockaddr_in aa; +#endif + memset( &aa, 0, sizeof(aa) ); + TQT_SOCKLEN_T sz; + sz = sizeof( aa ); + r = ::recvfrom( fd, data, maxlen, 0, + (struct sockaddr *)&aa, &sz ); + + qt_socket_getportaddr( (struct sockaddr *)&aa, &pp, &pa); + + } else { + r = ::read( fd, data, maxlen ); + } + done = TRUE; + if ( r == 0 && t == Stream && maxlen > 0 ) { + // connection closed + close(); + } else if ( r >= 0 || errno == EAGAIN || errno == EWOULDBLOCK ) { + // nothing + } else if ( errno == EINTR ) { + done = FALSE; + } else if ( e == NoError ) { + switch( errno ) { + case EIO: + case EISDIR: + case EBADF: + case EINVAL: + case EFAULT: + case ENOTCONN: + case ENOTSOCK: + e = Impossible; + break; +#if defined(ENONET) + case ENONET: +#endif + case EHOSTUNREACH: + case ENETDOWN: + case ENETUNREACH: + case ETIMEDOUT: + e = NetworkFailure; + break; + case EPIPE: + case ECONNRESET: + // connection closed + close(); + r = 0; + break; + default: + e = UnknownError; + break; + } + } + } + return r; +} + + +/*! + Writes \a len bytes to the socket from \a data and returns the + number of bytes written. Returns -1 if an error occurred. + + This is used for \c TQSocketDevice::Stream sockets. +*/ +TQ_LONG TQSocketDevice::writeBlock( const char *data, TQ_ULONG len ) +{ + if ( data == 0 && len != 0 ) { +#if defined(TQT_CHECK_NULL) || defined(TQSOCKETDEVICE_DEBUG) + qWarning( "TQSocketDevice::writeBlock: Null pointer error" ); +#endif + return -1; + } + if ( !isValid() ) { +#if defined(TQT_CHECK_STATE) || defined(TQSOCKETDEVICE_DEBUG) + qWarning( "TQSocketDevice::writeBlock: Invalid socket" ); +#endif + return -1; + } + if ( !isOpen() ) { +#if defined(TQT_CHECK_STATE) || defined(TQSOCKETDEVICE_DEBUG) + qWarning( "TQSocketDevice::writeBlock: Device is not open" ); +#endif + return -1; + } + if ( !isWritable() ) { +#if defined(TQT_CHECK_STATE) || defined(TQSOCKETDEVICE_DEBUG) + qWarning( "TQSocketDevice::writeBlock: Write operation not permitted" ); +#endif + return -1; + } + bool done = FALSE; + int r = 0; + bool timeout; + while ( !done ) { + r = ::write( fd, data, len ); + done = TRUE; + if ( r < 0 && e == NoError && + errno != EAGAIN && errno != EWOULDBLOCK ) { + switch( errno ) { + case EINTR: // signal - call read() or whatever again + done = FALSE; + break; + case EPIPE: + case ECONNRESET: + // connection closed + close(); + r = 0; + break; + case ENOSPC: + case EIO: + case EISDIR: + case EBADF: + case EINVAL: + case EFAULT: + case ENOTCONN: + case ENOTSOCK: + e = Impossible; + break; +#if defined(ENONET) + case ENONET: +#endif + case EHOSTUNREACH: + case ENETDOWN: + case ENETUNREACH: + case ETIMEDOUT: + e = NetworkFailure; + break; + default: + e = UnknownError; + break; + } + } else if ( waitForMore( 0, &timeout ) == 0 ) { + if ( !timeout ) { + // connection closed + close(); + } + } + } + return r; +} + + +/*! + \overload + + Writes \a len bytes to the socket from \a data and returns the + number of bytes written. Returns -1 if an error occurred. + + This is used for \c TQSocketDevice::Datagram sockets. You must + specify the \a host and \a port of the destination of the data. +*/ +TQ_LONG TQSocketDevice::writeBlock( const char * data, TQ_ULONG len, + const TQHostAddress & host, TQ_UINT16 port ) +{ + if ( t != Datagram ) { +#if defined(TQT_CHECK_STATE) || defined(TQSOCKETDEVICE_DEBUG) + qWarning( "TQSocketDevice::sendBlock: Not datagram" ); +#endif + return -1; // for now - later we can do t/tcp + } + + if ( data == 0 && len != 0 ) { +#if defined(TQT_CHECK_NULL) || defined(TQSOCKETDEVICE_DEBUG) + qWarning( "TQSocketDevice::sendBlock: Null pointer error" ); +#endif + return -1; + } + if ( !isValid() ) { +#if defined(TQT_CHECK_STATE) || defined(TQSOCKETDEVICE_DEBUG) + qWarning( "TQSocketDevice::sendBlock: Invalid socket" ); +#endif + return -1; + } + if ( !isOpen() ) { +#if defined(TQT_CHECK_STATE) || defined(TQSOCKETDEVICE_DEBUG) + qWarning( "TQSocketDevice::sendBlock: Device is not open" ); +#endif + return -1; + } + if ( !isWritable() ) { +#if defined(TQT_CHECK_STATE) || defined(TQSOCKETDEVICE_DEBUG) + qWarning( "TQSocketDevice::sendBlock: Write operation not permitted" ); +#endif + return -1; + } + struct sockaddr_in a4; + struct sockaddr *aa; + TQT_SOCKLEN_T slen; +#if !defined(TQT_NO_IPV6) + struct sockaddr_in6 a6; + if ( host.isIPv6Address() ) { + memset( &a6, 0, sizeof(a6) ); + a6.sin6_family = AF_INET6; + a6.sin6_port = htons( port ); + + TQ_IPV6ADDR tmp = host.toIPv6Address(); + memcpy( &a6.sin6_addr.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_NOTQT_SIGNAL if Stevens were right. + // but apparently Stevens and most implementors disagree + bool done = FALSE; + int r = 0; + while ( !done ) { + r = ::sendto( fd, data, len, 0, aa, slen); + done = TRUE; + if ( r < 0 && e == NoError && + errno != EAGAIN && errno != EWOULDBLOCK ) { + switch( errno ) { + case EINTR: // signal - call read() or whatever again + done = FALSE; + break; + case ENOSPC: + case EPIPE: + case EIO: + case EISDIR: + case EBADF: + case EINVAL: + case EFAULT: + case ENOTCONN: + case ENOTSOCK: + e = Impossible; + break; +#if defined(ENONET) + case ENONET: +#endif + case EHOSTUNREACH: + case ENETDOWN: + case ENETUNREACH: + case ETIMEDOUT: + e = NetworkFailure; + break; + default: + e = UnknownError; + break; + } + } + } + return r; +} + + +/*! + Fetches information about both ends of the connection: whatever is + available. +*/ +void TQSocketDevice::fetchConnectionParameters() +{ + if ( !isValid() ) { + p = 0; + a = TQHostAddress(); + pp = 0; + pa = TQHostAddress(); + return; + } +#if !defined(TQT_NO_IPV6) + struct sockaddr_storage sa; +#else + struct sockaddr_in sa; +#endif + memset( &sa, 0, sizeof(sa) ); + TQT_SOCKLEN_T sz; + sz = sizeof( sa ); + if ( !::getsockname( fd, (struct sockaddr *)(&sa), &sz ) ) + qt_socket_getportaddr( (struct sockaddr *)&sa, &p, &a ); + + sz = sizeof( sa ); + if ( !::getpeername( fd, (struct sockaddr *)(&sa), &sz ) ) + qt_socket_getportaddr( (struct sockaddr *)&sa, &pp, &pa ); +} + + +/*! + Returns the port number of the port this socket tqdevice is + connected to. This may be 0 for a while, but is set to something + sensible as soon as a sensible value is available. + + Note that for Datagram sockets, this is the source port of the + last packet received, and that it is in native byte order. +*/ +TQ_UINT16 TQSocketDevice::peerPort() const +{ + return pp; +} + + +/*! + Returns the address of the port this socket tqdevice is connected + to. This may be 0.0.0.0 for a while, but is set to something + sensible as soon as a sensible value is available. + + Note that for Datagram sockets, this is the source port of the + last packet received. +*/ +TQHostAddress TQSocketDevice::peerAddress() const +{ + return pa; +} + +#endif //TQT_NO_NETWORK |