diff options
Diffstat (limited to 'src/network/socket')
24 files changed, 3073 insertions, 293 deletions
diff --git a/src/network/socket/qabstractsocket.cpp b/src/network/socket/qabstractsocket.cpp index bd4c9e4..5316626 100644 --- a/src/network/socket/qabstractsocket.cpp +++ b/src/network/socket/qabstractsocket.cpp @@ -356,6 +356,10 @@ to enable. \value KeepAliveOption Set this to 1 to enable the SO_KEEPALIVE socket option + \value MulticastTtlOption Set this to an integer value to set IP_MULTICAST_TTL (TTL for multicast datagrams) socket option. + + \value MulticastLoopbackOption Set this to 1 to enable the IP_MULTICAST_LOOP (multicast loopback) socket option. + \sa QAbstractSocket::setSocketOption(), QAbstractSocket::socketOption() */ @@ -363,6 +367,7 @@ #include "qabstractsocket_p.h" #include "private/qhostinfo_p.h" +#include "private/qnetworksession_p.h" #include <qabstracteventdispatcher.h> #include <qhostaddress.h> @@ -371,6 +376,7 @@ #include <qpointer.h> #include <qtimer.h> #include <qelapsedtimer.h> +#include <qscopedvaluerollback.h> #ifndef QT_NO_OPENSSL #include <QtNetwork/qsslsocket.h> @@ -466,9 +472,6 @@ QAbstractSocketPrivate::QAbstractSocketPrivate() peerPort(0), socketEngine(0), cachedSocketDescriptor(-1), -#ifdef Q_OS_LINUX - addToBytesAvailable(0), -#endif readBufferMaxSize(0), readBuffer(QABSTRACTSOCKET_BUFFERSIZE), writeBuffer(QABSTRACTSOCKET_BUFFERSIZE), @@ -549,6 +552,10 @@ bool QAbstractSocketPrivate::initSocketLayer(QAbstractSocket::NetworkLayerProtoc q->setErrorString(QAbstractSocket::tr("Operation on socket is not supported")); return false; } +#ifndef QT_NO_BEARERMANAGEMENT + //copy network session down to the socket engine (if it has been set) + socketEngine->setProperty("_q_networksession", q->property("_q_networksession")); +#endif #ifndef QT_NO_NETWORKPROXY //copy user agent to socket engine (if it has been set) socketEngine->setProperty("_q_user-agent", q->property("_q_user-agent")); @@ -595,6 +602,7 @@ bool QAbstractSocketPrivate::canReadNotification() socketEngine->setReadNotificationEnabled(false); } } + QScopedValueRollback<bool> rsncrollback(readSocketNotifierCalled); readSocketNotifierCalled = true; if (!isBuffered) @@ -608,7 +616,6 @@ bool QAbstractSocketPrivate::canReadNotification() #if defined (QABSTRACTSOCKET_DEBUG) qDebug("QAbstractSocketPrivate::canReadNotification() buffer is full"); #endif - readSocketNotifierCalled = false; return false; } @@ -620,7 +627,6 @@ bool QAbstractSocketPrivate::canReadNotification() qDebug("QAbstractSocketPrivate::canReadNotification() disconnecting socket"); #endif q->disconnectFromHost(); - readSocketNotifierCalled = false; return false; } newBytes = readBuffer.size() - newBytes; @@ -634,14 +640,15 @@ bool QAbstractSocketPrivate::canReadNotification() // only emit readyRead() when not recursing, and only if there is data available bool hasData = newBytes > 0 #ifndef QT_NO_UDPSOCKET - || (!isBuffered && socketEngine && socketEngine->hasPendingDatagrams()) + || (!isBuffered && socketType != QAbstractSocket::TcpSocket && socketEngine && socketEngine->hasPendingDatagrams()) #endif + || (!isBuffered && socketType == QAbstractSocket::TcpSocket && socketEngine) ; if (!emittedReadyRead && hasData) { + QScopedValueRollback<bool> r(emittedReadyRead); emittedReadyRead = true; emit q->readyRead(); - emittedReadyRead = false; } // If we were closed as a result of the readyRead() signal, @@ -650,7 +657,6 @@ bool QAbstractSocketPrivate::canReadNotification() #if defined (QABSTRACTSOCKET_DEBUG) qDebug("QAbstractSocketPrivate::canReadNotification() socket is closing - returning"); #endif - readSocketNotifierCalled = false; return true; } @@ -664,7 +670,6 @@ bool QAbstractSocketPrivate::canReadNotification() socketEngine->setReadNotificationEnabled(readSocketNotifierState); readSocketNotifierStateSet = false; } - readSocketNotifierCalled = false; return true; } @@ -751,11 +756,11 @@ bool QAbstractSocketPrivate::flush() if (written < 0) { socketError = socketEngine->error(); q->setErrorString(socketEngine->errorString()); - emit q->error(socketError); - // an unexpected error so close the socket. #if defined (QABSTRACTSOCKET_DEBUG) qDebug() << "QAbstractSocketPrivate::flush() write error, aborting." << socketEngine->errorString(); #endif + emit q->error(socketError); + // an unexpected error so close the socket. q->abort(); return false; } @@ -770,9 +775,9 @@ bool QAbstractSocketPrivate::flush() if (written > 0) { // Don't emit bytesWritten() recursively. if (!emittedBytesWritten) { + QScopedValueRollback<bool> r(emittedBytesWritten); emittedBytesWritten = true; emit q->bytesWritten(written); - emittedBytesWritten = false; } } @@ -1136,10 +1141,6 @@ bool QAbstractSocketPrivate::readFromSocket() Q_Q(QAbstractSocket); // Find how many bytes we can read from the socket layer. qint64 bytesToRead = socketEngine->bytesAvailable(); -#ifdef Q_OS_LINUX - if (bytesToRead > 0) // ### See setSocketDescriptor() - bytesToRead += addToBytesAvailable; -#endif if (bytesToRead == 0) { // Under heavy load, certain conditions can trigger read notifications // for socket notifiers on which there is no activity. If we continue @@ -1374,10 +1375,6 @@ void QAbstractSocket::connectToHostImplementation(const QString &hostName, quint d->localAddress.clear(); d->peerAddress.clear(); d->peerName = hostName; -#ifdef Q_OS_LINUX - // ### See setSocketDescriptor(). - d->addToBytesAvailable = 0; -#endif if (d->hostLookupId != -1) { QHostInfo::abortHostLookup(d->hostLookupId); d->hostLookupId = -1; @@ -1395,8 +1392,11 @@ void QAbstractSocket::connectToHostImplementation(const QString &hostName, quint } #endif - if (!d_func()->isBuffered) - openMode |= QAbstractSocket::Unbuffered; + if (openMode & QIODevice::Unbuffered) + d->isBuffered = false; // Unbuffered QTcpSocket + else if (!d_func()->isBuffered) + openMode |= QAbstractSocket::Unbuffered; // QUdpSocket + QIODevice::open(openMode); d->state = HostLookupState; emit stateChanged(d->state); @@ -1476,10 +1476,12 @@ qint64 QAbstractSocket::bytesAvailable() const { Q_D(const QAbstractSocket); qint64 available = QIODevice::bytesAvailable(); - if (d->isBuffered) - available += (qint64) d->readBuffer.size(); - else if (d->socketEngine && d->socketEngine->isValid()) + + available += (qint64) d->readBuffer.size(); + + if (!d->isBuffered && d->socketEngine && d->socketEngine->isValid()) available += d->socketEngine->bytesAvailable(); + #if defined(QABSTRACTSOCKET_DEBUG) qDebug("QAbstractSocket::bytesAvailable() == %llu", available); #endif @@ -1612,6 +1614,10 @@ bool QAbstractSocket::setSocketDescriptor(int socketDescriptor, SocketState sock setErrorString(tr("Operation on socket is not supported")); return false; } +#ifndef QT_NO_BEARERMANAGEMENT + //copy network session down to the socket engine (if it has been set) + d->socketEngine->setProperty("_q_networksession", property("_q_networksession")); +#endif bool result = d->socketEngine->initialize(socketDescriptor, socketState); if (!result) { d->socketError = d->socketEngine->error(); @@ -1637,17 +1643,6 @@ bool QAbstractSocket::setSocketDescriptor(int socketDescriptor, SocketState sock d->peerAddress = d->socketEngine->peerAddress(); d->cachedSocketDescriptor = socketDescriptor; -#ifdef Q_OS_LINUX - // ### This is a workaround for certain broken Linux kernels, when using - // QTcpSocket with a Unix domain socket. It was introduced around 2.6.9, - // and fixed at some point after that. - // http://archive.linux-usenet.com/index-t-73300.html - // We can provide a better workaround for this: readFromSocket() can loop - // while reading, but this must happen without triggering an implicit - // close because of reading after the socket has closed. - d->addToBytesAvailable = 4096; -#endif - return true; } @@ -1677,6 +1672,14 @@ void QAbstractSocket::setSocketOption(QAbstractSocket::SocketOption option, cons case KeepAliveOption: d_func()->socketEngine->setOption(QAbstractSocketEngine::KeepAliveOption, value.toInt()); break; + + case MulticastTtlOption: + d_func()->socketEngine->setOption(QAbstractSocketEngine::MulticastTtlOption, value.toInt()); + break; + + case MulticastLoopbackOption: + d_func()->socketEngine->setOption(QAbstractSocketEngine::MulticastLoopbackOption, value.toInt()); + break; } } @@ -1706,6 +1709,13 @@ QVariant QAbstractSocket::socketOption(QAbstractSocket::SocketOption option) case KeepAliveOption: ret = d_func()->socketEngine->option(QAbstractSocketEngine::KeepAliveOption); break; + + case MulticastTtlOption: + ret = d_func()->socketEngine->option(QAbstractSocketEngine::MulticastTtlOption); + break; + case MulticastLoopbackOption: + ret = d_func()->socketEngine->option(QAbstractSocketEngine::MulticastLoopbackOption); + break; } if (ret == -1) return QVariant(); @@ -1781,6 +1791,14 @@ bool QAbstractSocket::waitForConnected(int msecs) #endif QHostInfo::abortHostLookup(d->hostLookupId); d->hostLookupId = -1; +#ifndef QT_NO_BEARERMANAGEMENT + QSharedPointer<QNetworkSession> networkSession; + QVariant v(property("_q_networksession")); + if (v.isValid()) { + networkSession = qvariant_cast< QSharedPointer<QNetworkSession> >(v); + d->_q_startConnecting(QHostInfoPrivate::fromName(d->hostName, networkSession)); + } else +#endif d->_q_startConnecting(QHostInfo::fromName(d->hostName)); } if (state() == UnconnectedState) @@ -1863,7 +1881,7 @@ bool QAbstractSocket::waitForReadyRead(int msecs) } Q_ASSERT(d->socketEngine); - forever { + do { bool readyToRead = false; bool readyToWrite = false; if (!d->socketEngine->waitForReadOrWrite(&readyToRead, &readyToWrite, true, !d->writeBuffer.isEmpty(), @@ -1890,7 +1908,7 @@ bool QAbstractSocket::waitForReadyRead(int msecs) if (state() != ConnectedState) return false; - } + } while (msecs == -1 || qt_timeout_value(msecs, stopWatch.elapsed()) > 0); return false; } @@ -2123,42 +2141,51 @@ bool QAbstractSocket::flush() qint64 QAbstractSocket::readData(char *data, qint64 maxSize) { Q_D(QAbstractSocket); - if (d->socketEngine && !d->socketEngine->isReadNotificationEnabled() && d->socketEngine->isValid()) - d->socketEngine->setReadNotificationEnabled(true); - - if (!d->isBuffered) { - if (!d->socketEngine) - return -1; // no socket engine is probably EOF - qint64 readBytes = d->socketEngine->read(data, maxSize); - if (readBytes < 0) { - d->socketError = d->socketEngine->error(); - setErrorString(d->socketEngine->errorString()); - } - if (!d->socketEngine->isReadNotificationEnabled()) - d->socketEngine->setReadNotificationEnabled(true); -#if defined (QABSTRACTSOCKET_DEBUG) - qDebug("QAbstractSocket::readData(%p \"%s\", %lli) == %lld", - data, qt_prettyDebug(data, 32, readBytes).data(), maxSize, - readBytes); -#endif - return readBytes; - } - if (d->readBuffer.isEmpty()) + // This is for a buffered QTcpSocket + if (d->isBuffered && d->readBuffer.isEmpty()) // if we're still connected, return 0 indicating there may be more data in the future // if we're not connected, return -1 indicating EOF return d->state == QAbstractSocket::ConnectedState ? qint64(0) : qint64(-1); - // If readFromSocket() read data, copy it to its destination. - if (maxSize == 1) { + // short cut for a char read if we have something in the buffer + if (maxSize == 1 && !d->readBuffer.isEmpty()) { *data = d->readBuffer.getChar(); #if defined (QABSTRACTSOCKET_DEBUG) - qDebug("QAbstractSocket::readData(%p '%c (0x%.2x)', 1) == 1", + qDebug("QAbstractSocket::readData(%p '%c (0x%.2x)', 1) == 1 [char buffer]", data, isprint(int(uchar(*data))) ? *data : '?', *data); #endif + if (d->readBuffer.isEmpty() && d->socketEngine && d->socketEngine->isValid()) + d->socketEngine->setReadNotificationEnabled(true); return 1; } + // Special case for an Unbuffered QTcpSocket + // Re-filling the buffer. + if (d->socketType == TcpSocket + && !d->isBuffered + && d->readBuffer.size() < maxSize + && d->readBufferMaxSize > 0 + && maxSize < d->readBufferMaxSize + && d->socketEngine + && d->socketEngine->isValid()) { + // Our buffer is empty and a read() was requested for a byte amount that is smaller + // than the readBufferMaxSize. This means that we should fill our buffer since we want + // such small reads come from the buffer and not always go to the costly socket engine read() + qint64 bytesToRead = d->socketEngine->bytesAvailable(); + if (bytesToRead > 0) { + char *ptr = d->readBuffer.reserve(bytesToRead); + qint64 readBytes = d->socketEngine->read(ptr, bytesToRead); + if (readBytes == -2) { + // No bytes currently available for reading. + d->readBuffer.chop(bytesToRead); + } else { + d->readBuffer.chop(int(bytesToRead - (readBytes < 0 ? qint64(0) : readBytes))); + } + } + } + + // First try to satisfy the read from the buffer qint64 bytesToRead = qMin(qint64(d->readBuffer.size()), maxSize); qint64 readSoFar = 0; while (readSoFar < bytesToRead) { @@ -2170,8 +2197,56 @@ qint64 QAbstractSocket::readData(char *data, qint64 maxSize) d->readBuffer.free(bytesToReadFromThisBlock); } + if (d->socketEngine && !d->socketEngine->isReadNotificationEnabled() && d->socketEngine->isValid()) + d->socketEngine->setReadNotificationEnabled(true); + + if (readSoFar > 0) { + // we read some data from buffer. + // Just return, readyRead will be emitted again +#if defined (QABSTRACTSOCKET_DEBUG) + qDebug("QAbstractSocket::readData(%p '%c (0x%.2x)', %lli) == %lli [buffer]", + data, isprint(int(uchar(*data))) ? *data : '?', *data, maxSize, readSoFar); +#endif + + if (d->readBuffer.isEmpty() && d->socketEngine) + d->socketEngine->setReadNotificationEnabled(true); + return readSoFar; + } + + // This code path is for Unbuffered QTcpSocket or for connected UDP + + if (!d->isBuffered) { + if (!d->socketEngine) + return -1; // no socket engine is probably EOF + if (!d->socketEngine->isValid()) + return -1; // This is for unbuffered TCP when we already had been disconnected + if (d->state != QAbstractSocket::ConnectedState) + return -1; // This is for unbuffered TCP if we're not connected yet + qint64 readBytes = d->socketEngine->read(data, maxSize); + if (readBytes == -2) { + // -2 from the engine means no bytes available (EAGAIN) so read more later + return 0; + } else if (readBytes < 0) { + d->socketError = d->socketEngine->error(); + setErrorString(d->socketEngine->errorString()); + d->resetSocketLayer(); + d->state = QAbstractSocket::UnconnectedState; + } else if (!d->socketEngine->isReadNotificationEnabled()) { + // Only do this when there was no error + d->socketEngine->setReadNotificationEnabled(true); + } + +#if defined (QABSTRACTSOCKET_DEBUG) + qDebug("QAbstractSocket::readData(%p \"%s\", %lli) == %lld [engine]", + data, qt_prettyDebug(data, 32, readBytes).data(), maxSize, + readBytes); +#endif + return readBytes; + } + + #if defined (QABSTRACTSOCKET_DEBUG) - qDebug("QAbstractSocket::readData(%p \"%s\", %lli) == %lld", + qDebug("QAbstractSocket::readData(%p \"%s\", %lli) == %lld [unreachable]", data, qt_prettyDebug(data, qMin<qint64>(32, readSoFar), readSoFar).data(), maxSize, readSoFar); #endif @@ -2196,7 +2271,23 @@ qint64 QAbstractSocket::writeData(const char *data, qint64 size) return -1; } - if (!d->isBuffered) { + if (!d->isBuffered && d->socketType == TcpSocket && d->writeBuffer.isEmpty()) { + // This code is for the new Unbuffered QTcpSocket use case + qint64 written = d->socketEngine->write(data, size); + if (written < 0) { + d->socketError = d->socketEngine->error(); + setErrorString(d->socketEngine->errorString()); + return written; + } else if (written < size) { + // Buffer what was not written yet + char *ptr = d->writeBuffer.reserve(size - written); + memcpy(ptr, data + written, size - written); + if (d->socketEngine) + d->socketEngine->setWriteNotificationEnabled(true); + } + return size; // size=actually written + what has been buffered + } else if (!d->isBuffered && d->socketType != TcpSocket) { + // This is for a QUdpSocket that was connect()ed qint64 written = d->socketEngine->write(data, size); if (written < 0) { d->socketError = d->socketEngine->error(); @@ -2215,6 +2306,12 @@ qint64 QAbstractSocket::writeData(const char *data, qint64 size) return written; } + // This is the code path for normal buffered QTcpSocket or + // unbuffered QTcpSocket when there was already something in the + // write buffer and therefore we could not do a direct engine write. + // We just write to our write buffer and enable the write notifier + // The write notifier then flush()es the buffer. + char *ptr = d->writeBuffer.reserve(size); if (size == 1) *ptr = *data; @@ -2553,7 +2650,7 @@ void QAbstractSocket::setReadBufferSize(qint64 size) // ensure that the read notification is enabled if we've now got // room in the read buffer // but only if we're not inside canReadNotification -- that will take care on its own - if (size == 0 || d->readBuffer.size() < size) + if ((size == 0 || d->readBuffer.size() < size) && d->state == QAbstractSocket::ConnectedState) // Do not change the notifier unless we are connected. d->socketEngine->setReadNotificationEnabled(true); } } diff --git a/src/network/socket/qabstractsocket.h b/src/network/socket/qabstractsocket.h index 348824a..1959fab 100644 --- a/src/network/socket/qabstractsocket.h +++ b/src/network/socket/qabstractsocket.h @@ -64,6 +64,7 @@ class QAuthenticator; class Q_NETWORK_EXPORT QAbstractSocket : public QIODevice { Q_OBJECT + Q_ENUMS(SocketType NetworkLayerProtocol SocketError SocketState SocketOption) public: enum SocketType { TcpSocket, @@ -118,7 +119,9 @@ public: }; enum SocketOption { LowDelayOption, // TCP_NODELAY - KeepAliveOption // SO_KEEPALIVE + KeepAliveOption, // SO_KEEPALIVE + MulticastTtlOption, // IP_MULTICAST_TTL + MulticastLoopbackOption // IP_MULTICAST_LOOPBACK }; QAbstractSocket(SocketType socketType, QObject *parent); diff --git a/src/network/socket/qabstractsocket_p.h b/src/network/socket/qabstractsocket_p.h index b600b30..cf7d988 100644 --- a/src/network/socket/qabstractsocket_p.h +++ b/src/network/socket/qabstractsocket_p.h @@ -59,7 +59,7 @@ #include "QtCore/qtimer.h" #include "private/qringbuffer_p.h" #include "private/qiodevice_p.h" -#include "private/qnativesocketengine_p.h" +#include "private/qabstractsocketengine_p.h" #include "qnetworkproxy.h" QT_BEGIN_NAMESPACE @@ -138,9 +138,6 @@ public: void setupSocketNotifiers(); bool readFromSocket(); -#ifdef Q_OS_LINUX - qint64 addToBytesAvailable; -#endif qint64 readBufferMaxSize; QRingBuffer readBuffer; QRingBuffer writeBuffer; diff --git a/src/network/socket/qabstractsocketengine.cpp b/src/network/socket/qabstractsocketengine.cpp index 41bf5df..2c5b5dc 100644 --- a/src/network/socket/qabstractsocketengine.cpp +++ b/src/network/socket/qabstractsocketengine.cpp @@ -40,7 +40,13 @@ ****************************************************************************/ #include "qabstractsocketengine_p.h" + +#ifdef Q_OS_SYMBIAN +#include "qsymbiansocketengine_p.h" +#else #include "qnativesocketengine_p.h" +#endif + #include "qmutex.h" #include "qnetworkproxy.h" @@ -113,7 +119,11 @@ QAbstractSocketEngine *QAbstractSocketEngine::createSocketEngine(QAbstractSocket return 0; #endif +#ifdef Q_OS_SYMBIAN + return new QSymbianSocketEngine(parent); +#else return new QNativeSocketEngine(parent); +#endif } QAbstractSocketEngine *QAbstractSocketEngine::createSocketEngine(int socketDescripter, QObject *parent) @@ -123,7 +133,11 @@ QAbstractSocketEngine *QAbstractSocketEngine::createSocketEngine(int socketDescr if (QAbstractSocketEngine *ret = socketHandlers()->at(i)->createSocketEngine(socketDescripter, parent)) return ret; } +#ifdef Q_OS_SYMBIAN + return new QSymbianSocketEngine(parent); +#else return new QNativeSocketEngine(parent); +#endif } QAbstractSocket::SocketError QAbstractSocketEngine::error() const diff --git a/src/network/socket/qabstractsocketengine_p.h b/src/network/socket/qabstractsocketengine_p.h index 9ed007b..a8be98a 100644 --- a/src/network/socket/qabstractsocketengine_p.h +++ b/src/network/socket/qabstractsocketengine_p.h @@ -61,6 +61,9 @@ QT_BEGIN_NAMESPACE class QAuthenticator; class QAbstractSocketEnginePrivate; +#ifndef QT_NO_NETWORKINTERFACE +class QNetworkInterface; +#endif class QNetworkProxy; class QAbstractSocketEngineReceiver { @@ -94,7 +97,9 @@ public: BindExclusively, ReceiveOutOfBandData, LowDelayOption, - KeepAliveOption + KeepAliveOption, + MulticastTtlOption, + MulticastLoopbackOption }; virtual bool initialize(QAbstractSocket::SocketType type, QAbstractSocket::NetworkLayerProtocol protocol = QAbstractSocket::IPv4Protocol) = 0; @@ -118,13 +123,22 @@ public: virtual qint64 write(const char *data, qint64 len) = 0; #ifndef QT_NO_UDPSOCKET +#ifndef QT_NO_NETWORKINTERFACE + virtual bool joinMulticastGroup(const QHostAddress &groupAddress, + const QNetworkInterface &iface) = 0; + virtual bool leaveMulticastGroup(const QHostAddress &groupAddress, + const QNetworkInterface &iface) = 0; + virtual QNetworkInterface multicastInterface() const = 0; + virtual bool setMulticastInterface(const QNetworkInterface &iface) = 0; +#endif // QT_NO_NETWORKINTERFACE + virtual qint64 readDatagram(char *data, qint64 maxlen, QHostAddress *addr = 0, quint16 *port = 0) = 0; virtual qint64 writeDatagram(const char *data, qint64 len, const QHostAddress &addr, quint16 port) = 0; virtual bool hasPendingDatagrams() const = 0; virtual qint64 pendingDatagramSize() const = 0; -#endif +#endif // QT_NO_UDPSOCKET virtual qint64 bytesToWrite() const = 0; diff --git a/src/network/socket/qhttpsocketengine.cpp b/src/network/socket/qhttpsocketengine.cpp index 81a2c61..5f5db17 100644 --- a/src/network/socket/qhttpsocketengine.cpp +++ b/src/network/socket/qhttpsocketengine.cpp @@ -45,6 +45,7 @@ #include "qurl.h" #include "qhttp.h" #include "qelapsedtimer.h" +#include "qnetworkinterface.h" #if !defined(QT_NO_NETWORKPROXY) && !defined(QT_NO_HTTP) #include <qdebug.h> @@ -71,6 +72,9 @@ bool QHttpSocketEngine::initialize(QAbstractSocket::SocketType type, QAbstractSo setProtocol(protocol); setSocketType(type); d->socket = new QTcpSocket(this); +#ifndef QT_NO_BEARERMANAGEMENT + d->socket->setProperty("_q_networkSession", property("_q_networkSession")); +#endif // Explicitly disable proxying on the proxy socket itself to avoid // unwanted recursion. @@ -239,6 +243,36 @@ qint64 QHttpSocketEngine::write(const char *data, qint64 len) } #ifndef QT_NO_UDPSOCKET +#ifndef QT_NO_NETWORKINTERFACE +bool QHttpSocketEngine::joinMulticastGroup(const QHostAddress &, + const QNetworkInterface &) +{ + setError(QAbstractSocket::UnsupportedSocketOperationError, + QLatin1String("Operation on socket is not supported")); + return false; +} + +bool QHttpSocketEngine::leaveMulticastGroup(const QHostAddress &, + const QNetworkInterface &) +{ + setError(QAbstractSocket::UnsupportedSocketOperationError, + QLatin1String("Operation on socket is not supported")); + return false; +} + +QNetworkInterface QHttpSocketEngine::multicastInterface() const +{ + return QNetworkInterface(); +} + +bool QHttpSocketEngine::setMulticastInterface(const QNetworkInterface &) +{ + setError(QAbstractSocket::UnsupportedSocketOperationError, + QLatin1String("Operation on socket is not supported")); + return false; +} +#endif // QT_NO_NETWORKINTERFACE + qint64 QHttpSocketEngine::readDatagram(char *, qint64, QHostAddress *, quint16 *) { @@ -681,11 +715,10 @@ void QHttpSocketEngine::slotSocketError(QAbstractSocket::SocketError error) d->state = None; setError(error, d->socket->errorString()); - if (error == QAbstractSocket::RemoteHostClosedError) { - emitReadNotification(); - } else { + if (error != QAbstractSocket::RemoteHostClosedError) qDebug() << "QHttpSocketEngine::slotSocketError: got weird error =" << error; - } + //read notification needs to always be emitted, otherwise the higher layer doesn't get the disconnected signal + emitReadNotification(); } void QHttpSocketEngine::slotSocketStateChanged(QAbstractSocket::SocketState state) @@ -720,7 +753,10 @@ void QHttpSocketEngine::emitReadNotification() { Q_D(QHttpSocketEngine); d->readNotificationActivated = true; - if (d->readNotificationEnabled && !d->readNotificationPending) { + // if there is a connection notification pending we have to emit the readNotification + // incase there is connection error. This is only needed for Windows, but it does not + // hurt in other cases. + if ((d->readNotificationEnabled && !d->readNotificationPending) || d->connectionNotificationPending) { d->readNotificationPending = true; QMetaObject::invokeMethod(this, "emitPendingReadNotification", Qt::QueuedConnection); } diff --git a/src/network/socket/qhttpsocketengine_p.h b/src/network/socket/qhttpsocketengine_p.h index 00744f3..d7cc7c1 100644 --- a/src/network/socket/qhttpsocketengine_p.h +++ b/src/network/socket/qhttpsocketengine_p.h @@ -102,6 +102,15 @@ public: qint64 write(const char *data, qint64 len); #ifndef QT_NO_UDPSOCKET +#ifndef QT_NO_NETWORKINTERFACE + bool joinMulticastGroup(const QHostAddress &groupAddress, + const QNetworkInterface &interface); + bool leaveMulticastGroup(const QHostAddress &groupAddress, + const QNetworkInterface &interface); + QNetworkInterface multicastInterface() const; + bool setMulticastInterface(const QNetworkInterface &iface); +#endif // QT_NO_NETWORKINTERFACE + qint64 readDatagram(char *data, qint64 maxlen, QHostAddress *addr = 0, quint16 *port = 0); qint64 writeDatagram(const char *data, qint64 len, const QHostAddress &addr, diff --git a/src/network/socket/qlocalserver.cpp b/src/network/socket/qlocalserver.cpp index f322e23..d4f847a 100644 --- a/src/network/socket/qlocalserver.cpp +++ b/src/network/socket/qlocalserver.cpp @@ -274,11 +274,11 @@ QLocalSocket *QLocalServer::nextPendingConnection() if (d->pendingConnections.isEmpty()) return 0; QLocalSocket *nextSocket = d->pendingConnections.dequeue(); +#ifndef QT_LOCALSOCKET_TCP #ifdef Q_OS_SYMBIAN if(!d->socketNotifier) return nextSocket; #endif -#ifndef QT_LOCALSOCKET_TCP if (d->pendingConnections.size() <= d->maxPendingConnections) #ifndef Q_OS_WIN d->socketNotifier->setEnabled(true); diff --git a/src/network/socket/qlocalserver_p.h b/src/network/socket/qlocalserver_p.h index 699fb2c..67b7000 100644 --- a/src/network/socket/qlocalserver_p.h +++ b/src/network/socket/qlocalserver_p.h @@ -65,7 +65,7 @@ # include <qt_windows.h> # include <private/qwineventnotifier_p.h> #else -# include <private/qnativesocketengine_p.h> +# include <private/qabstractsocketengine_p.h> # include <qsocketnotifier.h> #endif diff --git a/src/network/socket/qlocalsocket.cpp b/src/network/socket/qlocalsocket.cpp index 029af79..219d2aa 100644 --- a/src/network/socket/qlocalsocket.cpp +++ b/src/network/socket/qlocalsocket.cpp @@ -346,7 +346,7 @@ QLocalSocket::QLocalSocket(QObject * parent) QLocalSocket::~QLocalSocket() { close(); -#ifndef Q_OS_WIN +#if !defined(Q_OS_WIN) && !defined(QT_LOCALSOCKET_TCP) Q_D(QLocalSocket); d->unixSocket.setParent(0); #endif diff --git a/src/network/socket/qlocalsocket_p.h b/src/network/socket/qlocalsocket_p.h index aaef998..2671258 100644 --- a/src/network/socket/qlocalsocket_p.h +++ b/src/network/socket/qlocalsocket_p.h @@ -67,7 +67,7 @@ # include "private/qringbuffer_p.h" # include <private/qwineventnotifier_p.h> #else -# include "private/qnativesocketengine_p.h" +# include "private/qabstractsocketengine_p.h" # include <qtcpsocket.h> # include <qsocketnotifier.h> # include <errno.h> diff --git a/src/network/socket/qlocalsocket_win.cpp b/src/network/socket/qlocalsocket_win.cpp index b5dcb05..f595ba7 100644 --- a/src/network/socket/qlocalsocket_win.cpp +++ b/src/network/socket/qlocalsocket_win.cpp @@ -79,6 +79,11 @@ void QLocalSocketPrivate::setErrorString(const QString &function) errorString = QLocalSocket::tr("%1: Invalid name").arg(function); state = QLocalSocket::UnconnectedState; break; + case ERROR_ACCESS_DENIED: + error = QLocalSocket::SocketAccessError; + errorString = QLocalSocket::tr("%1: Access denied").arg(function); + state = QLocalSocket::UnconnectedState; + break; default: error = QLocalSocket::UnknownSocketError; errorString = QLocalSocket::tr("%1: Unknown error %2").arg(function).arg(windowsError); @@ -348,6 +353,11 @@ qint64 QLocalSocket::writeData(const char *data, qint64 maxSize) void QLocalSocket::abort() { + Q_D(QLocalSocket); + if (d->pipeWriter) { + delete d->pipeWriter; + d->pipeWriter = 0; + } close(); } @@ -569,10 +579,7 @@ bool QLocalSocket::waitForDisconnected(int msecs) bool QLocalSocket::isValid() const { Q_D(const QLocalSocket); - if (d->handle == INVALID_HANDLE_VALUE) - return false; - - return PeekNamedPipe(d->handle, NULL, 0, NULL, NULL, NULL); + return d->handle != INVALID_HANDLE_VALUE; } bool QLocalSocket::waitForReadyRead(int msecs) diff --git a/src/network/socket/qnativesocketengine.cpp b/src/network/socket/qnativesocketengine.cpp index eb2024d..8758522 100644 --- a/src/network/socket/qnativesocketengine.cpp +++ b/src/network/socket/qnativesocketengine.cpp @@ -98,6 +98,7 @@ #include <qabstracteventdispatcher.h> #include <qsocketnotifier.h> +#include <qnetworkinterface.h> #include "qnativesocketengine_p.h" #include <private/qthread_p.h> @@ -157,12 +158,12 @@ QT_BEGIN_NAMESPACE concurrent QNativeSocketEngine. This is safe, because WSAStartup and WSACleanup are reference counted. */ -QNativeSocketEnginePrivate::QNativeSocketEnginePrivate() +QNativeSocketEnginePrivate::QNativeSocketEnginePrivate() : + socketDescriptor(-1), + readNotifier(0), + writeNotifier(0), + exceptNotifier(0) { - socketDescriptor = -1; - readNotifier = 0; - writeNotifier = 0; - exceptNotifier = 0; } /*! \internal @@ -386,7 +387,6 @@ bool QNativeSocketEngine::initialize(QAbstractSocket::SocketType socketType, QAb // Make sure we receive out-of-band data - // On Symbian OS this works only with native IP stack, not with WinSock if (socketType == QAbstractSocket::TcpSocket && !setOption(ReceiveOutOfBandData, 1)) { qWarning("QNativeSocketEngine::initialize unable to inline out-of-band data"); @@ -646,6 +646,54 @@ int QNativeSocketEngine::accept() return d->nativeAccept(); } +#ifndef QT_NO_NETWORKINTERFACE + +/*! + \since 4.8 +*/ +bool QNativeSocketEngine::joinMulticastGroup(const QHostAddress &groupAddress, + const QNetworkInterface &iface) +{ + Q_D(QNativeSocketEngine); + Q_CHECK_VALID_SOCKETLAYER(QNativeSocketEngine::joinMulticastGroup(), false); + Q_CHECK_STATE(QNativeSocketEngine::joinMulticastGroup(), QAbstractSocket::BoundState, false); + Q_CHECK_TYPE(QNativeSocketEngine::joinMulticastGroup(), QAbstractSocket::UdpSocket, false); + return d->nativeJoinMulticastGroup(groupAddress, iface); +} + +/*! + \since 4.8 +*/ +bool QNativeSocketEngine::leaveMulticastGroup(const QHostAddress &groupAddress, + const QNetworkInterface &iface) +{ + Q_D(QNativeSocketEngine); + Q_CHECK_VALID_SOCKETLAYER(QNativeSocketEngine::leaveMulticastGroup(), false); + Q_CHECK_STATE(QNativeSocketEngine::leaveMulticastGroup(), QAbstractSocket::BoundState, false); + Q_CHECK_TYPE(QNativeSocketEngine::leaveMulticastGroup(), QAbstractSocket::UdpSocket, false); + return d->nativeLeaveMulticastGroup(groupAddress, iface); +} + +/*! \since 4.8 */ +QNetworkInterface QNativeSocketEngine::multicastInterface() const +{ + Q_D(const QNativeSocketEngine); + Q_CHECK_VALID_SOCKETLAYER(QNativeSocketEngine::multicastInterface(), QNetworkInterface()); + Q_CHECK_TYPE(QNativeSocketEngine::multicastInterface(), QAbstractSocket::UdpSocket, QNetworkInterface()); + return d->nativeMulticastInterface(); +} + +/*! \since 4.8 */ +bool QNativeSocketEngine::setMulticastInterface(const QNetworkInterface &iface) +{ + Q_D(QNativeSocketEngine); + Q_CHECK_VALID_SOCKETLAYER(QNativeSocketEngine::setMulticastInterface(), false); + Q_CHECK_TYPE(QNativeSocketEngine::setMulticastInterface(), QAbstractSocket::UdpSocket, false); + return d->nativeSetMulticastInterface(iface); +} + +#endif // QT_NO_NETWORKINTERFACE + /*! Returns the number of bytes that are currently available for reading. On error, -1 is returned. diff --git a/src/network/socket/qnativesocketengine_p.h b/src/network/socket/qnativesocketengine_p.h index fa52816..c5c4b32 100644 --- a/src/network/socket/qnativesocketengine_p.h +++ b/src/network/socket/qnativesocketengine_p.h @@ -60,11 +60,6 @@ # include <winsock2.h> #endif -#ifdef Q_OS_SYMBIAN -#include <private/qeventdispatcher_symbian_p.h> -#include <unistd.h> -#endif - QT_BEGIN_NAMESPACE // Use our own defines and structs which we know are correct @@ -101,6 +96,9 @@ union qt_sockaddr { }; class QNativeSocketEnginePrivate; +#ifndef QT_NO_NETWORKINTERFACE +class QNetworkInterface; +#endif class Q_AUTOTEST_EXPORT QNativeSocketEngine : public QAbstractSocketEngine { @@ -123,6 +121,15 @@ public: int accept(); void close(); +#ifndef QT_NO_NETWORKINTERFACE + bool joinMulticastGroup(const QHostAddress &groupAddress, + const QNetworkInterface &iface); + bool leaveMulticastGroup(const QHostAddress &groupAddress, + const QNetworkInterface &iface); + QNetworkInterface multicastInterface() const; + bool setMulticastInterface(const QNetworkInterface &iface); +#endif + qint64 bytesAvailable() const; qint64 read(char *data, qint64 maxlen); @@ -237,6 +244,14 @@ public: bool nativeBind(const QHostAddress &address, quint16 port); bool nativeListen(int backlog); int nativeAccept(); +#ifndef QT_NO_NETWORKINTERFACE + bool nativeJoinMulticastGroup(const QHostAddress &groupAddress, + const QNetworkInterface &iface); + bool nativeLeaveMulticastGroup(const QHostAddress &groupAddress, + const QNetworkInterface &iface); + QNetworkInterface nativeMulticastInterface() const; + bool nativeSetMulticastInterface(const QNetworkInterface &iface); +#endif qint64 nativeBytesAvailable() const; bool nativeHasPendingDatagrams() const; diff --git a/src/network/socket/qnativesocketengine_unix.cpp b/src/network/socket/qnativesocketengine_unix.cpp index 5cf27ce..39570c8 100644 --- a/src/network/socket/qnativesocketengine_unix.cpp +++ b/src/network/socket/qnativesocketengine_unix.cpp @@ -46,6 +46,7 @@ #include "qhostaddress.h" #include "qelapsedtimer.h" #include "qvarlengtharray.h" +#include "qnetworkinterface.h" #include <time.h> #include <errno.h> #include <fcntl.h> @@ -64,12 +65,7 @@ #include <ctype.h> #endif -#ifdef Q_OS_SYMBIAN // ### TODO: Are these headers right? -#include <sys/socket.h> -#include <netinet/in.h> -#else #include <netinet/tcp.h> -#endif QT_BEGIN_NAMESPACE @@ -173,11 +169,8 @@ bool QNativeSocketEnginePrivate::createNewSocket(QAbstractSocket::SocketType soc int protocol = AF_INET; #endif int type = (socketType == QAbstractSocket::UdpSocket) ? SOCK_DGRAM : SOCK_STREAM; -#ifdef Q_OS_SYMBIAN - int socket = ::socket(protocol, type, 0); -#else + int socket = qt_safe_socket(protocol, type, 0); -#endif if (socket <= 0) { switch (errno) { @@ -244,6 +237,30 @@ int QNativeSocketEnginePrivate::option(QNativeSocketEngine::SocketOption opt) co case QNativeSocketEngine::KeepAliveOption: n = SO_KEEPALIVE; break; + case QNativeSocketEngine::MulticastTtlOption: +#ifndef QT_NO_IPV6 + if (socketProtocol == QAbstractSocket::IPv6Protocol) { + level = IPPROTO_IPV6; + n = IPV6_MULTICAST_HOPS; + } else +#endif + { + level = IPPROTO_IP; + n = IP_MULTICAST_TTL; + } + break; + case QNativeSocketEngine::MulticastLoopbackOption: +#ifndef QT_NO_IPV6 + if (socketProtocol == QAbstractSocket::IPv6Protocol) { + level = IPPROTO_IPV6; + n = IPV6_MULTICAST_LOOP; + } else +#endif + { + level = IPPROTO_IP; + n = IP_MULTICAST_LOOP; + } + break; } int v = -1; @@ -295,11 +312,9 @@ bool QNativeSocketEnginePrivate::setOption(QNativeSocketEngine::SocketOption opt } #else // Q_OS_VXWORKS int onoff = 1; -#ifdef Q_OS_SYMBIAN - if (::ioctl(socketDescriptor, FIONBIO, &onoff) < 0) { -#else + if (qt_safe_ioctl(socketDescriptor, FIONBIO, &onoff) < 0) { -#endif + #ifdef QNATIVESOCKETENGINE_DEBUG perror("QNativeSocketEnginePrivate::setOption(): ioctl(FIONBIO, 1) failed"); #endif @@ -309,7 +324,7 @@ bool QNativeSocketEnginePrivate::setOption(QNativeSocketEngine::SocketOption opt return true; } case QNativeSocketEngine::AddressReusable: -#if defined(SO_REUSEPORT) && !defined(Q_OS_SYMBIAN) +#if defined(SO_REUSEPORT) n = SO_REUSEPORT; #else n = SO_REUSEADDR; @@ -327,6 +342,30 @@ bool QNativeSocketEnginePrivate::setOption(QNativeSocketEngine::SocketOption opt case QNativeSocketEngine::KeepAliveOption: n = SO_KEEPALIVE; break; + case QNativeSocketEngine::MulticastTtlOption: +#ifndef QT_NO_IPV6 + if (socketProtocol == QAbstractSocket::IPv6Protocol) { + level = IPPROTO_IPV6; + n = IPV6_MULTICAST_HOPS; + } else +#endif + { + level = IPPROTO_IP; + n = IP_MULTICAST_TTL; + } + break; + case QNativeSocketEngine::MulticastLoopbackOption: +#ifndef QT_NO_IPV6 + if (socketProtocol == QAbstractSocket::IPv6Protocol) { + level = IPPROTO_IPV6; + n = IPV6_MULTICAST_LOOP; + } else +#endif + { + level = IPPROTO_IP; + n = IP_MULTICAST_LOOP; + } + break; } return ::setsockopt(socketDescriptor, level, n, (char *) &v, sizeof(v)) == 0; @@ -378,11 +417,8 @@ bool QNativeSocketEnginePrivate::nativeConnect(const QHostAddress &addr, quint16 } else { // unreachable } -#ifdef Q_OS_SYMBIAN - int connectResult = ::connect(socketDescriptor, sockAddrPtr, sockAddrSize); -#else + int connectResult = qt_safe_connect(socketDescriptor, sockAddrPtr, sockAddrSize); -#endif if (connectResult == -1) { switch (errno) { case EISCONN: @@ -425,9 +461,6 @@ bool QNativeSocketEnginePrivate::nativeConnect(const QHostAddress &addr, quint16 case EBADF: case EFAULT: case ENOTSOCK: -#ifdef Q_OS_SYMBIAN - case EPIPE: -#endif socketState = QAbstractSocket::UnconnectedState; default: break; @@ -526,11 +559,7 @@ bool QNativeSocketEnginePrivate::nativeBind(const QHostAddress &address, quint16 bool QNativeSocketEnginePrivate::nativeListen(int backlog) { -#ifdef Q_OS_SYMBIAN - if (::listen(socketDescriptor, backlog) < 0) { -#else if (qt_safe_listen(socketDescriptor, backlog) < 0) { -#endif switch (errno) { case EADDRINUSE: setError(QAbstractSocket::AddressInUseError, @@ -557,25 +586,185 @@ bool QNativeSocketEnginePrivate::nativeListen(int backlog) int QNativeSocketEnginePrivate::nativeAccept() { -#ifdef Q_OS_SYMBIAN - int acceptedDescriptor = ::accept(socketDescriptor, 0, 0); -#else int acceptedDescriptor = qt_safe_accept(socketDescriptor, 0, 0); -#endif return acceptedDescriptor; } +#ifndef QT_NO_NETWORKINTERFACE + +static bool multicastMembershipHelper(QNativeSocketEnginePrivate *d, + int how6, + int how4, + const QHostAddress &groupAddress, + const QNetworkInterface &interface) +{ + int level = 0; + int sockOpt = 0; + void *sockArg; + int sockArgSize; + + ip_mreq mreq4; +#ifndef QT_NO_IPV6 + ipv6_mreq mreq6; + + if (groupAddress.protocol() == QAbstractSocket::IPv6Protocol) { + level = IPPROTO_IPV6; + sockOpt = how6; + sockArg = &mreq6; + sockArgSize = sizeof(mreq6); + memset(&mreq6, 0, sizeof(mreq6)); + Q_IPV6ADDR ip6 = groupAddress.toIPv6Address(); + memcpy(&mreq6.ipv6mr_multiaddr, &ip6, sizeof(ip6)); + mreq6.ipv6mr_interface = interface.index(); + } else +#endif + if (groupAddress.protocol() == QAbstractSocket::IPv4Protocol) { + level = IPPROTO_IP; + sockOpt = how4; + sockArg = &mreq4; + sockArgSize = sizeof(mreq4); + memset(&mreq4, 0, sizeof(mreq4)); + mreq4.imr_multiaddr.s_addr = htonl(groupAddress.toIPv4Address()); + + if (interface.isValid()) { + QList<QNetworkAddressEntry> addressEntries = interface.addressEntries(); + if (!addressEntries.isEmpty()) { + QHostAddress firstIP = addressEntries.first().ip(); + mreq4.imr_interface.s_addr = htonl(firstIP.toIPv4Address()); + } else { + d->setError(QAbstractSocket::NetworkError, + QNativeSocketEnginePrivate::NetworkUnreachableErrorString); + return false; + } + } else { + mreq4.imr_interface.s_addr = INADDR_ANY; + } + } else { + // unreachable + d->setError(QAbstractSocket::UnsupportedSocketOperationError, + QNativeSocketEnginePrivate::ProtocolUnsupportedErrorString); + return false; + } + + int res = setsockopt(d->socketDescriptor, level, sockOpt, sockArg, sockArgSize); + if (res == -1) { + switch (errno) { + case ENOPROTOOPT: + d->setError(QAbstractSocket::UnsupportedSocketOperationError, + QNativeSocketEnginePrivate::OperationUnsupportedErrorString); + break; + case EADDRNOTAVAIL: + d->setError(QAbstractSocket::SocketAddressNotAvailableError, + QNativeSocketEnginePrivate::AddressNotAvailableErrorString); + break; + default: + d->setError(QAbstractSocket::UnknownSocketError, + QNativeSocketEnginePrivate::UnknownSocketErrorString); + break; + } + return false; + } + return true; +} + +bool QNativeSocketEnginePrivate::nativeJoinMulticastGroup(const QHostAddress &groupAddress, + const QNetworkInterface &interface) +{ + return multicastMembershipHelper(this, +#ifndef QT_NO_IPV6 + IPV6_JOIN_GROUP, +#else + 0, +#endif + IP_ADD_MEMBERSHIP, + groupAddress, + interface); +} + +bool QNativeSocketEnginePrivate::nativeLeaveMulticastGroup(const QHostAddress &groupAddress, + const QNetworkInterface &interface) +{ + return multicastMembershipHelper(this, +#ifndef QT_NO_IPV6 + IPV6_LEAVE_GROUP, +#else + 0, +#endif + IP_DROP_MEMBERSHIP, + groupAddress, + interface); +} + +QNetworkInterface QNativeSocketEnginePrivate::nativeMulticastInterface() const +{ +#ifndef QT_NO_IPV6 + if (socketProtocol == QAbstractSocket::IPv6Protocol) { + uint v; + QT_SOCKOPTLEN_T sizeofv = sizeof(v); + if (::getsockopt(socketDescriptor, IPPROTO_IPV6, IPV6_MULTICAST_IF, &v, &sizeofv) == -1) + return QNetworkInterface(); + return QNetworkInterface::interfaceFromIndex(v); + } +#endif + + struct in_addr v = { 0 }; + QT_SOCKOPTLEN_T sizeofv = sizeof(v); + if (::getsockopt(socketDescriptor, IPPROTO_IP, IP_MULTICAST_IF, &v, &sizeofv) == -1) + return QNetworkInterface(); + if (v.s_addr != 0 && sizeofv >= sizeof(v)) { + QHostAddress ipv4(ntohl(v.s_addr)); + QList<QNetworkInterface> ifaces = QNetworkInterface::allInterfaces(); + for (int i = 0; i < ifaces.count(); ++i) { + const QNetworkInterface &iface = ifaces.at(i); + QList<QNetworkAddressEntry> entries = iface.addressEntries(); + for (int j = 0; j < entries.count(); ++j) { + const QNetworkAddressEntry &entry = entries.at(j); + if (entry.ip() == ipv4) + return iface; + } + } + } + return QNetworkInterface(); +} + +bool QNativeSocketEnginePrivate::nativeSetMulticastInterface(const QNetworkInterface &iface) +{ +#ifndef QT_NO_IPV6 + if (socketProtocol == QAbstractSocket::IPv6Protocol) { + uint v = iface.index(); + return (::setsockopt(socketDescriptor, IPPROTO_IPV6, IPV6_MULTICAST_IF, &v, sizeof(v)) != -1); + } +#endif + + struct in_addr v; + if (iface.isValid()) { + QList<QNetworkAddressEntry> entries = iface.addressEntries(); + for (int i = 0; i < entries.count(); ++i) { + const QNetworkAddressEntry &entry = entries.at(i); + const QHostAddress &ip = entry.ip(); + if (ip.protocol() == QAbstractSocket::IPv4Protocol) { + v.s_addr = htonl(ip.toIPv4Address()); + int r = ::setsockopt(socketDescriptor, IPPROTO_IP, IP_MULTICAST_IF, &v, sizeof(v)); + if (r != -1) + return true; + } + } + return false; + } + + v.s_addr = INADDR_ANY; + return (::setsockopt(socketDescriptor, IPPROTO_IP, IP_MULTICAST_IF, &v, sizeof(v)) != -1); +} + +#endif // QT_NO_NETWORKINTERFACE + qint64 QNativeSocketEnginePrivate::nativeBytesAvailable() const { int nbytes = 0; // gives shorter than true amounts on Unix domain sockets. qint64 available = 0; -#ifdef Q_OS_SYMBIAN - if (::ioctl(socketDescriptor, FIONREAD, (char *) &nbytes) >= 0) -#else if (qt_safe_ioctl(socketDescriptor, FIONREAD, (char *) &nbytes) >= 0) -#endif available = (qint64) nbytes; #if defined (QNATIVESOCKETENGINE_DEBUG) @@ -594,15 +783,10 @@ bool QNativeSocketEnginePrivate::nativeHasPendingDatagrams() const // Peek 0 bytes into the next message. The size of the message may // well be 0, so we can't check recvfrom's return value. ssize_t readBytes; -#ifdef Q_OS_SYMBIAN - char c; - readBytes = ::recvfrom(socketDescriptor, &c, 1, MSG_PEEK, &storage.a, &storageSize); -#else do { char c; readBytes = ::recvfrom(socketDescriptor, &c, 1, MSG_PEEK, &storage.a, &storageSize); } while (readBytes == -1 && errno == EINTR); -#endif // If there's no error, or if our buffer was too small, there must be a // pending datagram. @@ -615,14 +799,6 @@ bool QNativeSocketEnginePrivate::nativeHasPendingDatagrams() const return result; } -#ifdef Q_OS_SYMBIAN -qint64 QNativeSocketEnginePrivate::nativePendingDatagramSize() const -{ - size_t nbytes = 0; - ::ioctl(socketDescriptor, E32IONREAD, (char *) &nbytes); - return qint64(nbytes-28); -} -#else qint64 QNativeSocketEnginePrivate::nativePendingDatagramSize() const { QVarLengthArray<char, 8192> udpMessagePeekBuffer(8192); @@ -649,7 +825,7 @@ qint64 QNativeSocketEnginePrivate::nativePendingDatagramSize() const return qint64(recvResult); } -#endif + qint64 QNativeSocketEnginePrivate::nativeReceiveDatagram(char *data, qint64 maxSize, QHostAddress *address, quint16 *port) { @@ -659,17 +835,11 @@ qint64 QNativeSocketEnginePrivate::nativeReceiveDatagram(char *data, qint64 maxS sz = sizeof(aa); ssize_t recvFromResult = 0; -#ifdef Q_OS_SYMBIAN - char c; - recvFromResult = ::recvfrom(socketDescriptor, maxSize ? data : &c, maxSize ? maxSize : 1, - 0, &aa.a, &sz); -#else do { char c; recvFromResult = ::recvfrom(socketDescriptor, maxSize ? data : &c, maxSize ? maxSize : 1, 0, &aa.a, &sz); } while (recvFromResult == -1 && errno == EINTR); -#endif if (recvFromResult == -1) { setError(QAbstractSocket::NetworkError, ReceiveDatagramErrorString); @@ -718,13 +888,8 @@ qint64 QNativeSocketEnginePrivate::nativeSendDatagram(const char *data, qint64 l // ignore the SIGPIPE signal qt_ignore_sigpipe(); -#ifdef Q_OS_SYMBIAN - ssize_t sentBytes = ::sendto(socketDescriptor, data, len, - 0, sockAddrPtr, sockAddrSize); -#else ssize_t sentBytes = qt_safe_sendto(socketDescriptor, data, len, 0, sockAddrPtr, sockAddrSize); -#endif if (sentBytes < 0) { switch (errno) { @@ -822,11 +987,7 @@ void QNativeSocketEnginePrivate::nativeClose() qDebug("QNativeSocketEngine::nativeClose()"); #endif -#ifdef Q_OS_SYMBIAN - ::close(socketDescriptor); -#else - qt_safe_close(socketDescriptor); -#endif + qt_safe_close(socketDescriptor); } qint64 QNativeSocketEnginePrivate::nativeWrite(const char *data, qint64 len) @@ -837,12 +998,7 @@ qint64 QNativeSocketEnginePrivate::nativeWrite(const char *data, qint64 len) qt_ignore_sigpipe(); ssize_t writtenBytes; -#ifdef Q_OS_SYMBIAN - // Symbian does not support signals natively and Open C returns EINTR when moving to offline - writtenBytes = ::write(socketDescriptor, data, len); -#else writtenBytes = qt_safe_write(socketDescriptor, data, len); -#endif if (writtenBytes < 0) { switch (errno) { @@ -877,16 +1033,12 @@ qint64 QNativeSocketEnginePrivate::nativeRead(char *data, qint64 maxSize) { Q_Q(QNativeSocketEngine); if (!q->isValid()) { - qWarning("QNativeSocketEngine::unbufferedRead: Invalid socket"); + qWarning("QNativeSocketEngine::nativeRead: Invalid socket"); return -1; } ssize_t r = 0; -#ifdef Q_OS_SYMBIAN - r = ::read(socketDescriptor, data, maxSize); -#else r = qt_safe_read(socketDescriptor, data, maxSize); -#endif if (r < 0) { r = -1; @@ -903,9 +1055,6 @@ qint64 QNativeSocketEnginePrivate::nativeRead(char *data, qint64 maxSize) case EIO: //error string is now set in read(), not here in nativeRead() break; -#ifdef Q_OS_SYMBIAN - case EPIPE: -#endif case ECONNRESET: #if defined(Q_OS_VXWORKS) case ESHUTDOWN: @@ -936,40 +1085,11 @@ int QNativeSocketEnginePrivate::nativeSelect(int timeout, bool selectForRead) co tv.tv_sec = timeout / 1000; tv.tv_usec = (timeout % 1000) * 1000; -#ifdef Q_OS_SYMBIAN - fd_set fdexception; - FD_ZERO(&fdexception); - FD_SET(socketDescriptor, &fdexception); -#endif - int retval; if (selectForRead) -#ifdef Q_OS_SYMBIAN - retval = ::select(socketDescriptor + 1, &fds, 0, &fdexception, timeout < 0 ? 0 : &tv); -#else retval = qt_safe_select(socketDescriptor + 1, &fds, 0, 0, timeout < 0 ? 0 : &tv); -#endif else -#ifdef Q_OS_SYMBIAN - retval = ::select(socketDescriptor + 1, 0, &fds, &fdexception, timeout < 0 ? 0 : &tv); -#else retval = qt_safe_select(socketDescriptor + 1, 0, &fds, 0, timeout < 0 ? 0 : &tv); -#endif - - -#ifdef Q_OS_SYMBIAN - bool selectForExec = false; - if(retval != 0) { - if(retval < 0) { - qWarning("nativeSelect(....) returned < 0 for socket %d", socketDescriptor); - } - selectForExec = FD_ISSET(socketDescriptor, &fdexception); - } - if(selectForExec) { - qWarning("nativeSelect (selectForRead %d, retVal %d, errno %d) Unexpected exception for fd %d", - selectForRead, retval, errno, socketDescriptor); - } -#endif return retval; } @@ -987,65 +1107,12 @@ int QNativeSocketEnginePrivate::nativeSelect(int timeout, bool checkRead, bool c if (checkWrite) FD_SET(socketDescriptor, &fdwrite); -#ifdef Q_OS_SYMBIAN - fd_set fdexception; - FD_ZERO(&fdexception); - FD_SET(socketDescriptor, &fdexception); -#endif - struct timeval tv; tv.tv_sec = timeout / 1000; tv.tv_usec = (timeout % 1000) * 1000; int ret; -#ifndef Q_OS_SYMBIAN ret = qt_safe_select(socketDescriptor + 1, &fdread, &fdwrite, 0, timeout < 0 ? 0 : &tv); -#else - QElapsedTimer timer; - timer.start(); - - do { - ret = ::select(socketDescriptor + 1, &fdread, &fdwrite, &fdexception, timeout < 0 ? 0 : &tv); - bool selectForExec = false; - if(ret != 0) { - if(ret < 0) { - qWarning("nativeSelect(....) returned < 0 for socket %d", socketDescriptor); - } - selectForExec = FD_ISSET(socketDescriptor, &fdexception); - } - if(selectForExec) { - qWarning("nativeSelect (checkRead %d, checkWrite %d, ret %d, errno %d): Unexpected expectfds ready in fd %d", - checkRead, checkWrite, ret, errno, socketDescriptor); - if (checkWrite){ - FD_CLR(socketDescriptor, &fdread); - FD_SET(socketDescriptor, &fdwrite); - } else if (checkRead) - FD_SET(socketDescriptor, &fdread); - - - if ((ret == -1) && ( errno == ECONNREFUSED || errno == EPIPE )) - ret = 1; - - } - - if (ret != -1 || errno != EINTR) { - break; - } - - if (timeout > 0) { - // recalculate the timeout - int t = timeout - timer.elapsed(); - if (t < 0) { - // oops, timeout turned negative? - ret = -1; - break; - } - - tv.tv_sec = t / 1000; - tv.tv_usec = (t % 1000) * 1000; - } - } while (true); -#endif if (ret <= 0) return ret; diff --git a/src/network/socket/qnativesocketengine_win.cpp b/src/network/socket/qnativesocketengine_win.cpp index 6350651..88b87b9 100644 --- a/src/network/socket/qnativesocketengine_win.cpp +++ b/src/network/socket/qnativesocketengine_win.cpp @@ -40,6 +40,7 @@ ****************************************************************************/ #include <winsock2.h> +#include <ws2tcpip.h> #include "qnativesocketengine_p.h" @@ -47,6 +48,7 @@ #include <qsocketnotifier.h> #include <qdebug.h> #include <qdatetime.h> +#include <qnetworkinterface.h> //#define QNATIVESOCKETENGINE_DEBUG #if defined(QNATIVESOCKETENGINE_DEBUG) @@ -335,15 +337,17 @@ bool QNativeSocketEnginePrivate::createNewSocket(QAbstractSocket::SocketType soc } #if !defined(Q_OS_WINCE) - // enable new behavior using - // SIO_UDP_CONNRESET - DWORD dwBytesReturned = 0; - int bNewBehavior = 1; - if (::WSAIoctl(socket, SIO_UDP_CONNRESET, &bNewBehavior, sizeof(bNewBehavior), - NULL, 0, &dwBytesReturned, NULL, NULL) == SOCKET_ERROR) { - // not to worry isBogusUdpReadNotification() should handle this otherwise - int err = WSAGetLastError(); - WS_ERROR_DEBUG(err); + if (socketType == QAbstractSocket::UdpSocket) { + // enable new behavior using + // SIO_UDP_CONNRESET + DWORD dwBytesReturned = 0; + int bNewBehavior = 1; + if (::WSAIoctl(socket, SIO_UDP_CONNRESET, &bNewBehavior, sizeof(bNewBehavior), + NULL, 0, &dwBytesReturned, NULL, NULL) == SOCKET_ERROR) { + // not to worry isBogusUdpReadNotification() should handle this otherwise + int err = WSAGetLastError(); + WS_ERROR_DEBUG(err); + } } #endif @@ -399,6 +403,30 @@ int QNativeSocketEnginePrivate::option(QNativeSocketEngine::SocketOption opt) co case QNativeSocketEngine::KeepAliveOption: n = SO_KEEPALIVE; break; + case QNativeSocketEngine::MulticastTtlOption: +#ifndef QT_NO_IPV6 + if (socketProtocol == QAbstractSocket::IPv6Protocol) { + level = IPPROTO_IPV6; + n = IPV6_MULTICAST_HOPS; + } else +#endif + { + level = IPPROTO_IP; + n = IP_MULTICAST_TTL; + } + break; + case QNativeSocketEngine::MulticastLoopbackOption: +#ifndef QT_NO_IPV6 + if (socketProtocol == QAbstractSocket::IPv6Protocol) { + level = IPPROTO_IPV6; + n = IPV6_MULTICAST_LOOP; + } else +#endif + { + level = IPPROTO_IP; + n = IP_MULTICAST_LOOP; + } + break; } int v = -1; @@ -459,6 +487,30 @@ bool QNativeSocketEnginePrivate::setOption(QNativeSocketEngine::SocketOption opt case QNativeSocketEngine::KeepAliveOption: n = SO_KEEPALIVE; break; + case QNativeSocketEngine::MulticastTtlOption: +#ifndef QT_NO_IPV6 + if (socketProtocol == QAbstractSocket::IPv6Protocol) { + level = IPPROTO_IPV6; + n = IPV6_MULTICAST_HOPS; + } else +#endif + { + level = IPPROTO_IP; + n = IP_MULTICAST_TTL; + } + break; + case QNativeSocketEngine::MulticastLoopbackOption: +#ifndef QT_NO_IPV6 + if (socketProtocol == QAbstractSocket::IPv6Protocol) { + level = IPPROTO_IPV6; + n = IPV6_MULTICAST_LOOP; + } else +#endif + { + level = IPPROTO_IP; + n = IP_MULTICAST_LOOP; + } + break; } if (::setsockopt(socketDescriptor, level, n, (char*)&v, sizeof(v)) != 0) { @@ -589,6 +641,11 @@ bool QNativeSocketEnginePrivate::nativeConnect(const QHostAddress &address, quin socketState = QAbstractSocket::UnconnectedState; break; } + if (value == WSAEADDRNOTAVAIL) { + setError(QAbstractSocket::NetworkError, AddressNotAvailableErrorString); + socketState = QAbstractSocket::UnconnectedState; + break; + } } // fall through } @@ -648,8 +705,26 @@ bool QNativeSocketEnginePrivate::nativeConnect(const QHostAddress &address, quin } -bool QNativeSocketEnginePrivate::nativeBind(const QHostAddress &address, quint16 port) +bool QNativeSocketEnginePrivate::nativeBind(const QHostAddress &a, quint16 port) { + QHostAddress address = a; + switch (address.protocol()) { + case QAbstractSocket::IPv6Protocol: + if (address.toIPv6Address()[0] == 0xff) { + // binding to a multicast address + address = QHostAddress(QHostAddress::AnyIPv6); + } + break; + case QAbstractSocket::IPv4Protocol: + if ((address.toIPv4Address() & 0xffff0000) == 0xefff0000) { + // binding to a multicast address + address = QHostAddress(QHostAddress::Any); + } + break; + default: + break; + } + struct sockaddr_in sockAddrIPv4; qt_sockaddr_in6 sockAddrIPv6; struct sockaddr *sockAddrPtr = 0; @@ -747,6 +822,160 @@ int QNativeSocketEnginePrivate::nativeAccept() return acceptedDescriptor; } +static bool multicastMembershipHelper(QNativeSocketEnginePrivate *d, + int how6, + int how4, + const QHostAddress &groupAddress, + const QNetworkInterface &iface) +{ + int level = 0; + int sockOpt = 0; + char *sockArg; + int sockArgSize; + + struct ip_mreq mreq4; +#ifndef QT_NO_IPV6 + struct ipv6_mreq mreq6; + + if (groupAddress.protocol() == QAbstractSocket::IPv6Protocol) { + level = IPPROTO_IPV6; + sockOpt = how6; + sockArg = reinterpret_cast<char *>(&mreq6); + sockArgSize = sizeof(mreq6); + memset(&mreq6, 0, sizeof(mreq6)); + Q_IPV6ADDR ip6 = groupAddress.toIPv6Address(); + memcpy(&mreq6.ipv6mr_multiaddr, &ip6, sizeof(ip6)); + mreq6.ipv6mr_interface = iface.index(); + } else +#endif + if (groupAddress.protocol() == QAbstractSocket::IPv4Protocol) { + level = IPPROTO_IP; + sockOpt = how4; + sockArg = reinterpret_cast<char *>(&mreq4); + sockArgSize = sizeof(mreq4); + memset(&mreq4, 0, sizeof(mreq4)); + mreq4.imr_multiaddr.s_addr = htonl(groupAddress.toIPv4Address()); + + if (iface.isValid()) { + QList<QNetworkAddressEntry> addressEntries = iface.addressEntries(); + if (!addressEntries.isEmpty()) { + QHostAddress firstIP = addressEntries.first().ip(); + mreq4.imr_interface.s_addr = htonl(firstIP.toIPv4Address()); + } else { + d->setError(QAbstractSocket::NetworkError, + QNativeSocketEnginePrivate::NetworkUnreachableErrorString); + return false; + } + } else { + mreq4.imr_interface.s_addr = INADDR_ANY; + } + } else { + // unreachable + d->setError(QAbstractSocket::UnsupportedSocketOperationError, + QNativeSocketEnginePrivate::ProtocolUnsupportedErrorString); + return false; + } + + int res = setsockopt(d->socketDescriptor, level, sockOpt, sockArg, sockArgSize); + if (res == -1) { + d->setError(QAbstractSocket::UnsupportedSocketOperationError, + QNativeSocketEnginePrivate::OperationUnsupportedErrorString); + return false; + } + return true; +} + +bool QNativeSocketEnginePrivate::nativeJoinMulticastGroup(const QHostAddress &groupAddress, + const QNetworkInterface &iface) +{ + return multicastMembershipHelper(this, +#ifndef QT_NO_IPV6 + IPV6_JOIN_GROUP, +#else + 0, +#endif + IP_ADD_MEMBERSHIP, + groupAddress, + iface); +} + +bool QNativeSocketEnginePrivate::nativeLeaveMulticastGroup(const QHostAddress &groupAddress, + const QNetworkInterface &iface) +{ + return multicastMembershipHelper(this, +#ifndef QT_NO_IPV6 + IPV6_LEAVE_GROUP, +#else + 0, +#endif + IP_DROP_MEMBERSHIP, + groupAddress, + iface); +} + +QNetworkInterface QNativeSocketEnginePrivate::nativeMulticastInterface() const +{ +#ifndef QT_NO_IPV6 + if (socketProtocol == QAbstractSocket::IPv6Protocol) { + uint v; + QT_SOCKOPTLEN_T sizeofv = sizeof(v); + if (::getsockopt(socketDescriptor, IPPROTO_IPV6, IPV6_MULTICAST_IF, (char *) &v, &sizeofv) == -1) + return QNetworkInterface(); + return QNetworkInterface::interfaceFromIndex(v); + } +#endif + + struct in_addr v; + v.s_addr = 0; + QT_SOCKOPTLEN_T sizeofv = sizeof(v); + if (::getsockopt(socketDescriptor, IPPROTO_IP, IP_MULTICAST_IF, (char *) &v, &sizeofv) == -1) + return QNetworkInterface(); + if (v.s_addr != 0 && sizeofv >= QT_SOCKOPTLEN_T(sizeof(v))) { + QHostAddress ipv4(ntohl(v.s_addr)); + QList<QNetworkInterface> ifaces = QNetworkInterface::allInterfaces(); + for (int i = 0; i < ifaces.count(); ++i) { + const QNetworkInterface &iface = ifaces.at(i); + if (!(iface.flags() & QNetworkInterface::CanMulticast)) + continue; + QList<QNetworkAddressEntry> entries = iface.addressEntries(); + for (int j = 0; j < entries.count(); ++j) { + const QNetworkAddressEntry &entry = entries.at(j); + if (entry.ip() == ipv4) + return iface; + } + } + } + return QNetworkInterface(); +} + +bool QNativeSocketEnginePrivate::nativeSetMulticastInterface(const QNetworkInterface &iface) +{ +#ifndef QT_NO_IPV6 + if (socketProtocol == QAbstractSocket::IPv6Protocol) { + uint v = iface.isValid() ? iface.index() : 0; + return (::setsockopt(socketDescriptor, IPPROTO_IPV6, IPV6_MULTICAST_IF, (char *) &v, sizeof(v)) != -1); + } +#endif + + struct in_addr v; + if (iface.isValid()) { + QList<QNetworkAddressEntry> entries = iface.addressEntries(); + for (int i = 0; i < entries.count(); ++i) { + const QNetworkAddressEntry &entry = entries.at(i); + const QHostAddress &ip = entry.ip(); + if (ip.protocol() == QAbstractSocket::IPv4Protocol) { + v.s_addr = htonl(ip.toIPv4Address()); + int r = ::setsockopt(socketDescriptor, IPPROTO_IP, IP_MULTICAST_IF, (char *) &v, sizeof(v)); + if (r != -1) + return true; + } + } + return false; + } + + v.s_addr = INADDR_ANY; + return (::setsockopt(socketDescriptor, IPPROTO_IP, IP_MULTICAST_IF, (char *) &v, sizeof(v)) != -1); +} qint64 QNativeSocketEnginePrivate::nativeBytesAvailable() const { @@ -816,7 +1045,7 @@ bool QNativeSocketEnginePrivate::nativeHasPendingDatagrams() const bool result = false; fd_set readS; FD_ZERO(&readS); - FD_SET(socketDescriptor, &readS); + FD_SET((SOCKET)socketDescriptor, &readS); timeval timeout; timeout.tv_sec = 0; timeout.tv_usec = 5000; @@ -1111,7 +1340,7 @@ int QNativeSocketEnginePrivate::nativeSelect(int timeout, bool selectForRead) co memset(&fds, 0, sizeof(fd_set)); fds.fd_count = 1; - fds.fd_array[0] = socketDescriptor; + fds.fd_array[0] = (SOCKET)socketDescriptor; struct timeval tv; tv.tv_sec = timeout / 1000; @@ -1125,12 +1354,12 @@ int QNativeSocketEnginePrivate::nativeSelect(int timeout, bool selectForRead) co // Windows needs this to report errors when connecting a socket ... fd_set fdexception; FD_ZERO(&fdexception); - FD_SET(socketDescriptor, &fdexception); + FD_SET((SOCKET)socketDescriptor, &fdexception); ret = select(0, 0, &fds, &fdexception, timeout < 0 ? 0 : &tv); // ... but if it is actually set, pretend it did not happen - if (ret > 0 && FD_ISSET(socketDescriptor, &fdexception)) + if (ret > 0 && FD_ISSET((SOCKET)socketDescriptor, &fdexception)) ret--; } @@ -1157,16 +1386,16 @@ int QNativeSocketEnginePrivate::nativeSelect(int timeout, memset(&fdread, 0, sizeof(fd_set)); if (checkRead) { fdread.fd_count = 1; - fdread.fd_array[0] = socketDescriptor; + fdread.fd_array[0] = (SOCKET)socketDescriptor; } memset(&fdwrite, 0, sizeof(fd_set)); FD_ZERO(&fdexception); if (checkWrite) { fdwrite.fd_count = 1; - fdwrite.fd_array[0] = socketDescriptor; + fdwrite.fd_array[0] = (SOCKET)socketDescriptor; // Windows needs this to report errors when connecting a socket - FD_SET(socketDescriptor, &fdexception); + FD_SET((SOCKET)socketDescriptor, &fdexception); } struct timeval tv; @@ -1180,7 +1409,7 @@ int QNativeSocketEnginePrivate::nativeSelect(int timeout, #endif //... but if it is actually set, pretend it did not happen - if (ret > 0 && FD_ISSET(socketDescriptor, &fdexception)) + if (ret > 0 && FD_ISSET((SOCKET)socketDescriptor, &fdexception)) ret--; if (readEnabled) @@ -1189,8 +1418,8 @@ int QNativeSocketEnginePrivate::nativeSelect(int timeout, if (ret <= 0) return ret; - *selectForRead = FD_ISSET(socketDescriptor, &fdread); - *selectForWrite = FD_ISSET(socketDescriptor, &fdwrite); + *selectForRead = FD_ISSET((SOCKET)socketDescriptor, &fdread); + *selectForWrite = FD_ISSET((SOCKET)socketDescriptor, &fdwrite); return ret; } diff --git a/src/network/socket/qsocks5socketengine.cpp b/src/network/socket/qsocks5socketengine.cpp index 87e7700..575c0bc 100644 --- a/src/network/socket/qsocks5socketengine.cpp +++ b/src/network/socket/qsocks5socketengine.cpp @@ -56,6 +56,7 @@ #include "qurl.h" #include "qauthenticator.h" #include <qendian.h> +#include <qnetworkinterface.h> QT_BEGIN_NAMESPACE @@ -555,6 +556,9 @@ void QSocks5SocketEnginePrivate::initialize(Socks5Mode socks5Mode) udpData = new QSocks5UdpAssociateData; data = udpData; udpData->udpSocket = new QUdpSocket(q); +#ifndef QT_NO_BEARERMANAGEMENT + udpData->udpSocket->setProperty("_q_networksession", q->property("_q_networksession")); +#endif udpData->udpSocket->setProxy(QNetworkProxy::NoProxy); QObject::connect(udpData->udpSocket, SIGNAL(readyRead()), q, SLOT(_q_udpSocketReadNotification()), @@ -566,6 +570,9 @@ void QSocks5SocketEnginePrivate::initialize(Socks5Mode socks5Mode) } data->controlSocket = new QTcpSocket(q); +#ifndef QT_NO_BEARERMANAGEMENT + data->controlSocket->setProperty("_q_networksession", q->property("_q_networksession")); +#endif data->controlSocket->setProxy(QNetworkProxy::NoProxy); QObject::connect(data->controlSocket, SIGNAL(connected()), q, SLOT(_q_controlSocketConnected()), Qt::DirectConnection); @@ -1375,6 +1382,9 @@ bool QSocks5SocketEngine::bind(const QHostAddress &address, quint16 port) d->udpData->associatePort = d->localPort; d->localPort = 0; QUdpSocket dummy; +#ifndef QT_NO_BEARERMANAGEMENT + dummy.setProperty("_q_networksession", property("_q_networksession")); +#endif dummy.setProxy(QNetworkProxy::NoProxy); if (!dummy.bind() || writeDatagram(0,0, d->data->controlSocket->localAddress(), dummy.localPort()) != 0 @@ -1530,8 +1540,13 @@ qint64 QSocks5SocketEngine::write(const char *data, qint64 len) // ### Handle this error. } - d->data->controlSocket->write(sealedBuf); + qint64 written = d->data->controlSocket->write(sealedBuf); + if (written <= 0) { + QSOCKS5_Q_DEBUG << "native write returned" << written; + return written; + } d->data->controlSocket->waitForBytesWritten(0); + //NB: returning len rather than written for the OK case, because the "sealing" may increase the length return len; #ifndef QT_NO_UDPSOCKET } else if (d->mode == QSocks5SocketEnginePrivate::UdpAssociateMode) { @@ -1544,6 +1559,37 @@ qint64 QSocks5SocketEngine::write(const char *data, qint64 len) } #ifndef QT_NO_UDPSOCKET +#ifndef QT_NO_NETWORKINTERFACE +bool QSocks5SocketEngine::joinMulticastGroup(const QHostAddress &, + const QNetworkInterface &) +{ + setError(QAbstractSocket::UnsupportedSocketOperationError, + QLatin1String("Operation on socket is not supported")); + return false; +} + +bool QSocks5SocketEngine::leaveMulticastGroup(const QHostAddress &, + const QNetworkInterface &) +{ + setError(QAbstractSocket::UnsupportedSocketOperationError, + QLatin1String("Operation on socket is not supported")); + return false; +} + + +QNetworkInterface QSocks5SocketEngine::multicastInterface() const +{ + return QNetworkInterface(); +} + +bool QSocks5SocketEngine::setMulticastInterface(const QNetworkInterface &) +{ + setError(QAbstractSocket::UnsupportedSocketOperationError, + QLatin1String("Operation on socket is not supported")); + return false; +} +#endif // QT_NO_NETWORKINTERFACE + qint64 QSocks5SocketEngine::readDatagram(char *data, qint64 maxlen, QHostAddress *addr, quint16 *port) { diff --git a/src/network/socket/qsocks5socketengine_p.h b/src/network/socket/qsocks5socketengine_p.h index 7c1b762..9b2f6e3 100644 --- a/src/network/socket/qsocks5socketengine_p.h +++ b/src/network/socket/qsocks5socketengine_p.h @@ -92,6 +92,15 @@ public: qint64 write(const char *data, qint64 len); #ifndef QT_NO_UDPSOCKET +#ifndef QT_NO_NETWORKINTERFACE + bool joinMulticastGroup(const QHostAddress &groupAddress, + const QNetworkInterface &interface); + bool leaveMulticastGroup(const QHostAddress &groupAddress, + const QNetworkInterface &interface); + QNetworkInterface multicastInterface() const; + bool setMulticastInterface(const QNetworkInterface &iface); +#endif // QT_NO_NETWORKINTERFACE + qint64 readDatagram(char *data, qint64 maxlen, QHostAddress *addr = 0, quint16 *port = 0); qint64 writeDatagram(const char *data, qint64 len, const QHostAddress &addr, diff --git a/src/network/socket/qsymbiansocketengine.cpp b/src/network/socket/qsymbiansocketengine.cpp new file mode 100644 index 0000000..15635ff --- /dev/null +++ b/src/network/socket/qsymbiansocketengine.cpp @@ -0,0 +1,1770 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtNetwork 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$ +** +****************************************************************************/ + +//#define QNATIVESOCKETENGINE_DEBUG +#include "qsymbiansocketengine_p.h" + +#include "qiodevice.h" +#include "qhostaddress.h" +#include "qelapsedtimer.h" +#include "qvarlengtharray.h" +#include "qnetworkinterface.h" +#include <private/qnetworksession_p.h> +#include <es_sock.h> +#include <in_sock.h> +#include <net/if.h> + +#include <private/qcore_symbian_p.h> + +#if !defined(QT_NO_NETWORKPROXY) +# include "qnetworkproxy.h" +# include "qabstractsocket.h" +# include "qtcpserver.h" +#endif + +#include <QCoreApplication> + +#include <qabstracteventdispatcher.h> +#include <private/qeventdispatcher_symbian_p.h> +#include <qsocketnotifier.h> +#include <qnetworkinterface.h> + +#include <private/qthread_p.h> +#include <private/qobject_p.h> +#include <private/qsystemerror_p.h> + +#if defined QNATIVESOCKETENGINE_DEBUG +#include <qstring.h> +#include <ctype.h> +#endif + +QT_BEGIN_NAMESPACE + +#define Q_VOID +// Common constructs +#define Q_CHECK_VALID_SOCKETLAYER(function, returnValue) do { \ + if (!isValid()) { \ + qWarning(""#function" was called on an uninitialized socket device"); \ + return returnValue; \ + } } while (0) +#define Q_CHECK_INVALID_SOCKETLAYER(function, returnValue) do { \ + if (isValid()) { \ + qWarning(""#function" was called on an already initialized socket device"); \ + return returnValue; \ + } } while (0) +#define Q_CHECK_STATE(function, checkState, returnValue) do { \ + if (d->socketState != (checkState)) { \ + qWarning(""#function" was not called in "#checkState); \ + return (returnValue); \ + } } while (0) +#define Q_CHECK_NOT_STATE(function, checkState, returnValue) do { \ + if (d->socketState == (checkState)) { \ + qWarning(""#function" was called in "#checkState); \ + return (returnValue); \ + } } while (0) +#define Q_CHECK_STATES(function, state1, state2, returnValue) do { \ + if (d->socketState != (state1) && d->socketState != (state2)) { \ + qWarning(""#function" was called" \ + " not in "#state1" or "#state2); \ + return (returnValue); \ + } } while (0) +#define Q_CHECK_TYPE(function, type, returnValue) do { \ + if (d->socketType != (type)) { \ + qWarning(#function" was called by a" \ + " socket other than "#type""); \ + return (returnValue); \ + } } while (0) + +#if defined QNATIVESOCKETENGINE_DEBUG + +/* + Returns a human readable representation of the first \a len + characters in \a data. +*/ +static QByteArray qt_prettyDebug(const char *data, int len, int maxSize) +{ + if (!data) return "(null)"; + QByteArray out; + for (int i = 0; i < len; ++i) { + char c = data[i]; + if (isprint(c)) { + out += c; + } else switch (c) { + case '\n': out += "\\n"; break; + case '\r': out += "\\r"; break; + case '\t': out += "\\t"; break; + default: + QString tmp; + tmp.sprintf("\\%o", c); + out += tmp.toLatin1(); + } + } + + if (len < maxSize) + out += "..."; + + return out; +} +#endif + +void QSymbianSocketEnginePrivate::getPortAndAddress(const TInetAddr& a, quint16 *port, QHostAddress *addr) +{ + if (a.Family() == KAfInet6 && !a.IsV4Compat() && !a.IsV4Mapped()) { + Q_IPV6ADDR tmp; + memcpy(&tmp, a.Ip6Address().u.iAddr8, sizeof(tmp)); + if (addr) { + QHostAddress tmpAddress; + tmpAddress.setAddress(tmp); + *addr = tmpAddress; + TPckgBuf<TSoInetIfQuery> query; + query().iSrcAddr = a; + TInt err = nativeSocket.GetOpt(KSoInetIfQueryBySrcAddr, KSolInetIfQuery, query); + if (!err) + addr->setScopeId(qt_TDesC2QString(query().iName)); + else + addr->setScopeId(QString::number(a.Scope())); + } + if (port) + *port = a.Port(); + return; + } + if (port) + *port = a.Port(); + if (addr) { + QHostAddress tmpAddress; + tmpAddress.setAddress(a.Address()); + *addr = tmpAddress; + } +} +/*! \internal + + Creates and returns a new socket descriptor of type \a socketType + and \a socketProtocol. Returns -1 on failure. +*/ +bool QSymbianSocketEnginePrivate::createNewSocket(QAbstractSocket::SocketType socketType, + QAbstractSocket::NetworkLayerProtocol socketProtocol) +{ + Q_Q(QSymbianSocketEngine); + TUint family = KAfInet; // KAfInet6 is only used as an address family, not as a protocol family + TUint type = (socketType == QAbstractSocket::UdpSocket) ? KSockDatagram : KSockStream; + TUint protocol = (socketType == QAbstractSocket::UdpSocket) ? KProtocolInetUdp : KProtocolInetTcp; + + //Check if there is a user specified session + QVariant v(q->property("_q_networksession")); + TInt err; + if (v.isValid()) { + QSharedPointer<QNetworkSession> s = qvariant_cast<QSharedPointer<QNetworkSession> >(v); + err = QNetworkSessionPrivate::nativeOpenSocket(*s, nativeSocket, family, type, protocol); +#ifdef QNATIVESOCKETENGINE_DEBUG + qDebug() << "QSymbianSocketEnginePrivate::createNewSocket - _q_networksession was set" << err; +#endif + } else + err = nativeSocket.Open(socketServer, family, type, protocol); //TODO: FIXME - deprecated API, make sure we always have a connection instead + + if (err != KErrNone) { + switch (err) { + case KErrNotSupported: + case KErrNotFound: + setError(QAbstractSocket::UnsupportedSocketOperationError, + ProtocolUnsupportedErrorString); + break; + default: + setError(err); + break; + } + + return false; + } +#ifdef QNATIVESOCKETENGINE_DEBUG + qDebug() << "QSymbianSocketEnginePrivate::createNewSocket - created" << nativeSocket.SubSessionHandle(); +#endif + socketDescriptor = QSymbianSocketManager::instance().addSocket(nativeSocket); +#ifdef QNATIVESOCKETENGINE_DEBUG + qDebug() << " - allocated socket descriptor" << socketDescriptor; +#endif + return true; +} + +void QSymbianSocketEnginePrivate::setPortAndAddress(TInetAddr& nativeAddr, quint16 port, const QHostAddress &addr) +{ + nativeAddr.SetPort(port); + if (addr.protocol() == QAbstractSocket::IPv6Protocol) { + TPckgBuf<TSoInetIfQuery> query; + query().iName = qt_QString2TPtrC(addr.scopeId()); + TInt err = nativeSocket.GetOpt(KSoInetIfQueryByName, KSolInetIfQuery, query); + if (!err) + nativeAddr.SetScope(query().iIndex); + else + nativeAddr.SetScope(0); + Q_IPV6ADDR ip6 = addr.toIPv6Address(); + TIp6Addr v6addr; + memcpy(v6addr.u.iAddr8, ip6.c, 16); + nativeAddr.SetAddress(v6addr); + } else if (addr.protocol() == QAbstractSocket::IPv4Protocol) { + nativeAddr.SetAddress(addr.toIPv4Address()); + } else { + qWarning("unsupported network protocol (%d)", addr.protocol()); + } +} + +QSymbianSocketEnginePrivate::QSymbianSocketEnginePrivate() : + socketDescriptor(-1), + socketServer(QSymbianSocketManager::instance().getSocketServer()), + readNotificationsEnabled(false), + writeNotificationsEnabled(false), + exceptNotificationsEnabled(false), + asyncSelect(0), + hasReceivedBufferedDatagram(false) +{ +} + +QSymbianSocketEnginePrivate::~QSymbianSocketEnginePrivate() +{ +} + + +QSymbianSocketEngine::QSymbianSocketEngine(QObject *parent) + : QAbstractSocketEngine(*new QSymbianSocketEnginePrivate(), parent) +{ +} + + +QSymbianSocketEngine::~QSymbianSocketEngine() +{ + close(); +} + +/*! + Initializes a QSymbianSocketEngine by creating a new socket of type \a + socketType and network layer protocol \a protocol. Returns true on + success; otherwise returns false. + + If the socket was already initialized, this function closes the + socket before reeinitializing it. + + The new socket is non-blocking, and for UDP sockets it's also + broadcast enabled. +*/ +bool QSymbianSocketEngine::initialize(QAbstractSocket::SocketType socketType, QAbstractSocket::NetworkLayerProtocol protocol) +{ + Q_D(QSymbianSocketEngine); + if (isValid()) + close(); + + // Create the socket + if (!d->createNewSocket(socketType, protocol)) { +#if defined (QNATIVESOCKETENGINE_DEBUG) + QString typeStr = QLatin1String("UnknownSocketType"); + if (socketType == QAbstractSocket::TcpSocket) typeStr = QLatin1String("TcpSocket"); + else if (socketType == QAbstractSocket::UdpSocket) typeStr = QLatin1String("UdpSocket"); + QString protocolStr = QLatin1String("UnknownProtocol"); + if (protocol == QAbstractSocket::IPv4Protocol) protocolStr = QLatin1String("IPv4Protocol"); + else if (protocol == QAbstractSocket::IPv6Protocol) protocolStr = QLatin1String("IPv6Protocol"); + qDebug("QSymbianSocketEngine::initialize(type == %s, protocol == %s) failed: %s", + typeStr.toLatin1().constData(), protocolStr.toLatin1().constData(), d->socketErrorString.toLatin1().constData()); +#endif + return false; + } + + // Make the socket nonblocking. + if (!setOption(NonBlockingSocketOption, 1)) { + d->setError(QAbstractSocket::UnsupportedSocketOperationError, + d->NonBlockingInitFailedErrorString); + close(); + return false; + } + + // Set the broadcasting flag if it's a UDP socket. + if (socketType == QAbstractSocket::UdpSocket + && !setOption(BroadcastSocketOption, 1)) { + d->setError(QAbstractSocket::UnsupportedSocketOperationError, + d->BroadcastingInitFailedErrorString); + close(); + return false; + } + + + // Make sure we receive out-of-band data + if (socketType == QAbstractSocket::TcpSocket + && !setOption(ReceiveOutOfBandData, 1)) { + qWarning("QSymbianSocketEngine::initialize unable to inline out-of-band data"); + } + + + d->socketType = socketType; + d->socketProtocol = protocol; + return true; +} + +/*! \overload + + Initializes the socket using \a socketDescriptor instead of + creating a new one. The socket type and network layer protocol are + determined automatically. The socket's state is set to \a + socketState. + + If the socket type is either TCP or UDP, it is made non-blocking. + UDP sockets are also broadcast enabled. + */ +bool QSymbianSocketEngine::initialize(int socketDescriptor, QAbstractSocket::SocketState socketState) +{ + Q_D(QSymbianSocketEngine); + + if (isValid()) + close(); + + if (!QSymbianSocketManager::instance().lookupSocket(socketDescriptor, d->nativeSocket)) { + qWarning("QSymbianSocketEngine::initialize - socket descriptor not found"); + d->setError(QAbstractSocket::UnsupportedSocketOperationError, + QSymbianSocketEnginePrivate::InvalidSocketErrorString); + return false; + } +#ifdef QNATIVESOCKETENGINE_DEBUG + qDebug() << "QSymbianSocketEngine::initialize - attached to" << d->nativeSocket.SubSessionHandle() << socketDescriptor; +#endif + Q_ASSERT(d->socketDescriptor == socketDescriptor || d->socketDescriptor == -1); + d->socketDescriptor = socketDescriptor; + + // determine socket type and protocol + if (!d->fetchConnectionParameters()) { +#if defined (QNATIVESOCKETENGINE_DEBUG) + qDebug("QSymbianSocketEngine::initialize(socketDescriptor == %i) failed: %s", + socketDescriptor, d->socketErrorString.toLatin1().constData()); +#endif + d->socketDescriptor = -1; + return false; + } + + if (d->socketType != QAbstractSocket::UnknownSocketType) { + // Make the socket nonblocking. + if (!setOption(NonBlockingSocketOption, 1)) { + d->setError(QAbstractSocket::UnsupportedSocketOperationError, + d->NonBlockingInitFailedErrorString); + close(); + return false; + } + + // Set the broadcasting flag if it's a UDP socket. + if (d->socketType == QAbstractSocket::UdpSocket + && !setOption(BroadcastSocketOption, 1)) { + d->setError(QAbstractSocket::UnsupportedSocketOperationError, + d->BroadcastingInitFailedErrorString); + close(); + return false; + } + + // Make sure we receive out-of-band data + if (d->socketType == QAbstractSocket::TcpSocket + && !setOption(ReceiveOutOfBandData, 1)) { + qWarning("QSymbianSocketEngine::initialize unable to inline out-of-band data"); + } + } + + d->socketState = socketState; + return true; +} + +/*! + Returns true if the socket is valid; otherwise returns false. A + socket is valid if it has not been successfully initialized, or if + it has been closed. +*/ +bool QSymbianSocketEngine::isValid() const +{ + Q_D(const QSymbianSocketEngine); + return d->socketDescriptor != -1; +} + + +/*! + Returns the native socket descriptor. Any use of this descriptor + stands the risk of being non-portable. +*/ +int QSymbianSocketEngine::socketDescriptor() const +{ + Q_D(const QSymbianSocketEngine); + return d->socketDescriptor; +} + +/* + Sets the socket option \a opt to \a v. +*/ +bool QSymbianSocketEngine::setOption(QAbstractSocketEngine::SocketOption opt, int v) +{ + Q_D(QSymbianSocketEngine); + Q_CHECK_VALID_SOCKETLAYER(QSymbianSocketEngine::setOption(), false); + + TUint n = 0; + TUint level = KSOLSocket; // default + + if (!QSymbianSocketEnginePrivate::translateSocketOption(opt, n, level)) + return false; + + if (!level && !n) + return true; + + return (KErrNone == d->nativeSocket.SetOpt(n, level, v)); +} + +/* + Returns the value of the socket option \a opt. +*/ +int QSymbianSocketEngine::option(QAbstractSocketEngine::SocketOption opt) const +{ + Q_D(const QSymbianSocketEngine); + Q_CHECK_VALID_SOCKETLAYER(QSymbianSocketEngine::option(), -1); + + TUint n; + TUint level = KSOLSocket; // default + + if (!QSymbianSocketEnginePrivate::translateSocketOption(opt, n, level)) + return false; + + if (!level && !n) + return 1; + + int v = -1; + //GetOpt() is non const + TInt err = d->nativeSocket.GetOpt(n, level, v); + if (!err) + return v; + + return -1; +} + +bool QSymbianSocketEnginePrivate::translateSocketOption(QAbstractSocketEngine::SocketOption opt, TUint &n, TUint &level) +{ + + switch (opt) { + case QAbstractSocketEngine::ReceiveBufferSocketOption: + n = KSORecvBuf; + break; + case QAbstractSocketEngine::SendBufferSocketOption: + n = KSOSendBuf; + break; + case QAbstractSocketEngine::NonBlockingSocketOption: + n = KSONonBlockingIO; + break; + case QAbstractSocketEngine::AddressReusable: + level = KSolInetIp; + n = KSoReuseAddr; + break; + case QAbstractSocketEngine::BroadcastSocketOption: + case QAbstractSocketEngine::BindExclusively: + level = 0; + n = 0; + return true; + case QAbstractSocketEngine::ReceiveOutOfBandData: + level = KSolInetTcp; + n = KSoTcpOobInline; + break; + case QAbstractSocketEngine::LowDelayOption: + level = KSolInetTcp; + n = KSoTcpNoDelay; + break; + case QAbstractSocketEngine::KeepAliveOption: + level = KSolInetTcp; + n = KSoTcpKeepAlive; + break; + case QAbstractSocketEngine::MulticastLoopbackOption: + level = KSolInetIp; + n = KSoIp6MulticastLoop; + break; + case QAbstractSocketEngine::MulticastTtlOption: + level = KSolInetIp; + n = KSoIp6MulticastHops; + break; + default: + return false; + } + return true; +} + +qint64 QSymbianSocketEngine::receiveBufferSize() const +{ + Q_CHECK_VALID_SOCKETLAYER(QSymbianSocketEngine::receiveBufferSize(), -1); + return option(ReceiveBufferSocketOption); +} + +void QSymbianSocketEngine::setReceiveBufferSize(qint64 size) +{ + Q_CHECK_VALID_SOCKETLAYER(QSymbianSocketEngine::setReceiveBufferSize(), Q_VOID); + setOption(ReceiveBufferSocketOption, size); +} + +qint64 QSymbianSocketEngine::sendBufferSize() const +{ + Q_CHECK_VALID_SOCKETLAYER(QSymbianSocketEngine::setSendBufferSize(), -1); + return option(SendBufferSocketOption); +} + +void QSymbianSocketEngine::setSendBufferSize(qint64 size) +{ + Q_CHECK_VALID_SOCKETLAYER(QSymbianSocketEngine::setSendBufferSize(), Q_VOID); + setOption(SendBufferSocketOption, size); +} + +/*! + Connects to the remote host name given by \a name on port \a + port. When this function is called, the upper-level will not + perform a hostname lookup. + + The native socket engine does not support this operation, + but some other socket engines (notably proxy-based ones) do. +*/ +bool QSymbianSocketEngine::connectToHostByName(const QString &name, quint16 port) +{ + Q_UNUSED(name); + Q_UNUSED(port); + Q_D(QSymbianSocketEngine); + d->setError(QAbstractSocket::UnsupportedSocketOperationError, + QSymbianSocketEnginePrivate::OperationUnsupportedErrorString); + return false; +} + +/*! + If there's a connection activity on the socket, process it. Then + notify our parent if there really was activity. +*/ +void QSymbianSocketEngine::connectionNotification() +{ + // FIXME check if we really need to do it like that in Symbian + Q_D(QSymbianSocketEngine); + Q_ASSERT(state() == QAbstractSocket::ConnectingState); + + connectToHost(d->peerAddress, d->peerPort); + if (state() != QAbstractSocket::ConnectingState) { + // we changed states + QAbstractSocketEngine::connectionNotification(); + } +} + + +bool QSymbianSocketEngine::connectToHost(const QHostAddress &addr, quint16 port) +{ + Q_D(QSymbianSocketEngine); + Q_CHECK_VALID_SOCKETLAYER(QSymbianSocketEngine::connectToHost(), false); + +#ifdef QNATIVESOCKETENGINE_DEBUG + qDebug("QSymbianSocketEngine::connectToHost() : %d ", d->socketDescriptor); +#endif + + if (!d->checkProxy(addr)) + return false; + + d->peerAddress = addr; + d->peerPort = port; + + TInetAddr nativeAddr; + d->setPortAndAddress(nativeAddr, port, addr); + TRequestStatus status; + d->nativeSocket.Connect(nativeAddr, status); + User::WaitForRequest(status); + TInt err = status.Int(); + //For non blocking connect, KErrAlreadyExists is returned from the second Connect() to indicate + //the connection is up. So treat this the same as KErrNone which would be returned from the first + //call if it wouldn't block. (e.g. winsock wrapper in the emulator ignores the nonblocking flag) + if (err && err != KErrAlreadyExists) { + switch (err) { + case KErrWouldBlock: + d->socketState = QAbstractSocket::ConnectingState; + break; + default: + d->setError(err); + d->socketState = QAbstractSocket::UnconnectedState; + break; + } + + if (d->socketState != QAbstractSocket::ConnectedState) { +#if defined (QNATIVESOCKETENGINE_DEBUG) + qDebug("QSymbianSocketEngine::connectToHost(%s, %i) == false (%s)", + addr.toString().toLatin1().constData(), port, + d->socketState == QAbstractSocket::ConnectingState + ? "Connection in progress" : d->socketErrorString.toLatin1().constData()); +#endif + return false; + } + } + +#if defined (QNATIVESOCKETENGINE_DEBUG) + qDebug("QSymbianSocketEngine::Connect(%s, %i) == true", + addr.toString().toLatin1().constData(), port); +#endif + + d->socketState = QAbstractSocket::ConnectedState; + d->fetchConnectionParameters(); + return true; +} + +bool QSymbianSocketEngine::bind(const QHostAddress &address, quint16 port) +{ + Q_D(QSymbianSocketEngine); + Q_CHECK_VALID_SOCKETLAYER(QSymbianSocketEngine::bind(), false); + + if (!d->checkProxy(address)) + return false; + + Q_CHECK_STATE(QSymbianSocketEngine::bind(), QAbstractSocket::UnconnectedState, false); + + TInetAddr nativeAddr; + if (address == QHostAddress::Any || address == QHostAddress::AnyIPv6) { + //Should allow both IPv4 and IPv6 + //Listening on "0.0.0.0" accepts ONLY ipv4 connections + //Listening on "::" accepts ONLY ipv6 connections + nativeAddr.SetFamily(KAFUnspec); + nativeAddr.SetPort(port); + } else { + d->setPortAndAddress(nativeAddr, port, address); + } + + TInt err = d->nativeSocket.Bind(nativeAddr); +#ifdef __WINS__ + if (err == KErrArgument) // winsock prt returns wrong error code + err = KErrInUse; +#endif + + if (err) { + switch (err) { + case KErrNotFound: + // the specified interface was not found - use the error code expected + d->setError(QAbstractSocket::SocketAddressNotAvailableError, QSymbianSocketEnginePrivate::AddressNotAvailableErrorString); + break; + default: + d->setError(err); + break; + } + +#if defined (QNATIVESOCKETENGINE_DEBUG) + qDebug("QSymbianSocketEngine::bind(%s, %i) == false (%s)", + address.toString().toLatin1().constData(), port, d->socketErrorString.toLatin1().constData()); +#endif + + return false; + } + +#if defined (QNATIVESOCKETENGINE_DEBUG) + qDebug("QSymbianSocketEngine::bind(%s, %i) == true", + address.toString().toLatin1().constData(), port); +#endif + d->socketState = QAbstractSocket::BoundState; + + d->fetchConnectionParameters(); + + // When we bind to unspecified address (to get a dual mode socket), report back the + // same type of address that was requested. This is required for SOCKS proxy to work. + if (nativeAddr.Family() == KAFUnspec) + d->localAddress = address; + return true; +} + +bool QSymbianSocketEngine::listen() +{ + Q_D(QSymbianSocketEngine); + Q_CHECK_VALID_SOCKETLAYER(QSymbianSocketEngine::listen(), false); + Q_CHECK_STATE(QSymbianSocketEngine::listen(), QAbstractSocket::BoundState, false); + Q_CHECK_TYPE(QSymbianSocketEngine::listen(), QAbstractSocket::TcpSocket, false); + TInt err = d->nativeSocket.Listen(50); + if (err) { + d->setError(err); + +#if defined (QNATIVESOCKETENGINE_DEBUG) + qDebug("QSymbianSocketEngine::listen() == false (%s)", + d->socketErrorString.toLatin1().constData()); +#endif + return false; + } + +#if defined (QNATIVESOCKETENGINE_DEBUG) + qDebug("QSymbianSocketEngine::listen() == true"); +#endif + + d->socketState = QAbstractSocket::ListeningState; + return true; +} + +int QSymbianSocketEngine::accept() +{ + Q_D(QSymbianSocketEngine); + Q_CHECK_VALID_SOCKETLAYER(QSymbianSocketEngine::accept(), -1); + Q_CHECK_STATE(QSymbianSocketEngine::accept(), QAbstractSocket::ListeningState, false); + Q_CHECK_TYPE(QSymbianSocketEngine::accept(), QAbstractSocket::TcpSocket, false); + RSocket blankSocket; + blankSocket.Open(d->socketServer); + TRequestStatus status; + d->nativeSocket.Accept(blankSocket, status); + User::WaitForRequest(status); + if (status.Int()) { + blankSocket.Close(); + if (status != KErrWouldBlock) + qWarning("QSymbianSocketEngine::accept() - error %d", status.Int()); + return -1; + } + +#ifdef QNATIVESOCKETENGINE_DEBUG + qDebug() << "QSymbianSocketEnginePrivate::accept - created" << blankSocket.SubSessionHandle(); +#endif + int fd = QSymbianSocketManager::instance().addSocket(blankSocket); +#ifdef QNATIVESOCKETENGINE_DEBUG + qDebug() << " - allocated socket descriptor" << fd; +#endif + return fd; +} + +qint64 QSymbianSocketEngine::bytesAvailable() const +{ + Q_D(const QSymbianSocketEngine); + Q_CHECK_VALID_SOCKETLAYER(QSymbianSocketEngine::bytesAvailable(), -1); + Q_CHECK_NOT_STATE(QSymbianSocketEngine::bytesAvailable(), QAbstractSocket::UnconnectedState, false); + int nbytes = 0; + qint64 available = 0; + TInt err = d->nativeSocket.GetOpt(KSOReadBytesPending, KSOLSocket, nbytes); + if (err) + return 0; + available = (qint64) nbytes; + +#if defined (QNATIVESOCKETENGINE_DEBUG) + qDebug("QSymbianSocketEngine::bytesAvailable() == %lli", available); +#endif + return available; +} + +bool QSymbianSocketEngine::hasPendingDatagrams() const +{ + Q_D(const QSymbianSocketEngine); + Q_CHECK_VALID_SOCKETLAYER(QSymbianSocketEngine::hasPendingDatagrams(), false); + Q_CHECK_NOT_STATE(QSymbianSocketEngine::hasPendingDatagrams(), QAbstractSocket::UnconnectedState, false); + Q_CHECK_TYPE(QSymbianSocketEngine::hasPendingDatagrams(), QAbstractSocket::UdpSocket, false); + int nbytes; + TInt err = d->nativeSocket.GetOpt(KSOReadBytesPending,KSOLSocket, nbytes); + return err == KErrNone && nbytes > 0; +} + +qint64 QSymbianSocketEngine::pendingDatagramSize() const +{ + Q_D(const QSymbianSocketEngine); + Q_CHECK_VALID_SOCKETLAYER(QSymbianSocketEngine::pendingDatagramSize(), false); + Q_CHECK_TYPE(QSymbianSocketEngine::hasPendingDatagrams(), QAbstractSocket::UdpSocket, false); + //can only buffer one datagram at a time + if (d->hasReceivedBufferedDatagram) + return d->receivedDataBuffer.size(); + int nbytes = 0; + TInt err = d->nativeSocket.GetOpt(KSOReadBytesPending,KSOLSocket, nbytes); + if (nbytes > 0) { + //nbytes includes IP header, which is of variable length (IPv4 with or without options, IPv6...) + //therefore read the datagram into a buffer to find its true size + d->receivedDataBuffer.resize(nbytes); + TPtr8 buffer((TUint8*)d->receivedDataBuffer.data(), nbytes); + //nbytes = size including IP header, buffer is a pointer descriptor backed by the receivedDataBuffer + TInetAddr addr; + TRequestStatus status; + //RecvFrom copies only the payload (we don't want the header so don't specify the option to retrieve it) + d->nativeSocket.RecvFrom(buffer, addr, 0, status); + User::WaitForRequest(status); + if (status != KErrNone) { + d->receivedDataBuffer.clear(); + return 0; + } + nbytes = buffer.Length(); + //nbytes = size of payload, resize the receivedDataBuffer to the final size + d->receivedDataBuffer.resize(nbytes); + d->hasReceivedBufferedDatagram = true; + //now receivedDataBuffer contains one datagram, which has been removed from the socket's internal buffer +#if defined (QNATIVESOCKETENGINE_DEBUG) + qDebug() << "QSymbianSocketEngine::pendingDatagramSize buffering" << nbytes << "bytes"; +#endif + } + return qint64(nbytes); +} + + +qint64 QSymbianSocketEngine::readDatagram(char *data, qint64 maxSize, + QHostAddress *address, quint16 *port) +{ + Q_D(QSymbianSocketEngine); + Q_CHECK_VALID_SOCKETLAYER(QSymbianSocketEngine::readDatagram(), -1); + Q_CHECK_TYPE(QSymbianSocketEngine::readDatagram(), QAbstractSocket::UdpSocket, false); + + // if a datagram was buffered in pendingDatagramSize(), return it now + if (d->hasReceivedBufferedDatagram) { + qint64 size = qMin(maxSize, (qint64)d->receivedDataBuffer.size()); + memcpy(data, d->receivedDataBuffer.constData(), size); + d->receivedDataBuffer.clear(); + d->hasReceivedBufferedDatagram = false; +#if defined (QNATIVESOCKETENGINE_DEBUG) + qDebug() << "QSymbianSocketEngine::readDatagram returning" << size << "bytes from buffer"; +#endif + return size; + } + + TPtr8 buffer((TUint8*)data, (int)maxSize); + TInetAddr addr; + TRequestStatus status; + d->nativeSocket.RecvFrom(buffer, addr, 0, status); + User::WaitForRequest(status); //Non blocking receive + + if (status.Int()) { + d->setError(QAbstractSocket::NetworkError, d->ReceiveDatagramErrorString); + } else if (port || address) { + d->getPortAndAddress(addr, port, address); + } + +#if defined (QNATIVESOCKETENGINE_DEBUG) + int len = buffer.Length(); + qDebug("QSymbianSocketEngine::receiveDatagram(%p \"%s\", %lli, %s, %i) == %lli", + data, qt_prettyDebug(data, qMin(len, ssize_t(16)), len).data(), maxSize, + address ? address->toString().toLatin1().constData() : "(nil)", + port ? *port : 0, (qint64) len); +#endif + + if (status.Int()) + return -1; + return qint64(buffer.Length()); +} + + +qint64 QSymbianSocketEngine::writeDatagram(const char *data, qint64 len, + const QHostAddress &host, quint16 port) +{ + Q_D(QSymbianSocketEngine); + Q_CHECK_VALID_SOCKETLAYER(QSymbianSocketEngine::writeDatagram(), -1); + Q_CHECK_TYPE(QSymbianSocketEngine::writeDatagram(), QAbstractSocket::UdpSocket, -1); + TPtrC8 buffer((TUint8*)data, (int)len); + TInetAddr addr; + d->setPortAndAddress(addr, port, host); + TSockXfrLength sentBytes; + TRequestStatus status; + d->nativeSocket.SendTo(buffer, addr, 0, status, sentBytes); + User::WaitForRequest(status); //Non blocking send + TInt err = status.Int(); + +#if defined (QNATIVESOCKETENGINE_DEBUG) + qDebug("QSymbianSocketEngine::writeDatagram(%p \"%s\", %lli, \"%s\", %i) == %lli (err=%d)", data, + qt_prettyDebug(data, qMin<int>(len, 16), len).data(), len, host.toString().toLatin1().constData(), + port, (qint64) sentBytes(), err); +#endif + + if (err) { + switch (err) { + case KErrWouldBlock: + // do not error the socket. (otherwise socket layer is reset) + // On symbian^1 and earlier, KErrWouldBlock is returned when interface is not up yet + // On symbian^3, KErrNone is returned but sentBytes = 0 + return 0; + case KErrTooBig: + d->setError(QAbstractSocket::DatagramTooLargeError, d->DatagramTooLargeErrorString); + break; + default: + d->setError(QAbstractSocket::NetworkError, d->SendDatagramErrorString); + } + return -1; + } + + if (QSysInfo::s60Version() <= QSysInfo::SV_S60_5_0) { + // This is evil hack, but for some reason native RSocket::SendTo returns 0, + // for large datagrams (such as 600 bytes). Based on comments from Open C team + // this should happen only in platforms <= S60 5.0. + return len; + } + return sentBytes(); +} + +// FIXME check where the native socket engine called that.. +bool QSymbianSocketEnginePrivate::fetchConnectionParameters() +{ + localPort = 0; + localAddress.clear(); + peerPort = 0; + peerAddress.clear(); + + if (socketDescriptor == -1) + return false; + + if (!nativeSocket.SubSessionHandle()) { + if (!QSymbianSocketManager::instance().lookupSocket(socketDescriptor, nativeSocket)) { + setError(QAbstractSocket::UnsupportedSocketOperationError, InvalidSocketErrorString); + return false; + } + } + + // Determine local address + TSockAddr addr; + nativeSocket.LocalName(addr); + getPortAndAddress(addr, &localPort, &localAddress); + + // Determine protocol family + socketProtocol = localAddress.protocol(); + + // Determine the remote address + nativeSocket.RemoteName(addr); + getPortAndAddress(addr, &peerPort, &peerAddress); + + // Determine the socket type (UDP/TCP) + TProtocolDesc protocol; + TInt err = nativeSocket.Info(protocol); + if (err) { + setError(err); + return false; + } else { + switch (protocol.iProtocol) { + case KProtocolInetTcp: + socketType = QAbstractSocket::TcpSocket; + break; + case KProtocolInetUdp: + socketType = QAbstractSocket::UdpSocket; + break; + default: + socketType = QAbstractSocket::UnknownSocketType; + break; + } + } +#if defined (QNATIVESOCKETENGINE_DEBUG) + QString socketProtocolStr = QLatin1String("UnknownProtocol"); + if (socketProtocol == QAbstractSocket::IPv4Protocol) socketProtocolStr = QLatin1String("IPv4Protocol"); + else if (socketProtocol == QAbstractSocket::IPv6Protocol) socketProtocolStr = QLatin1String("IPv6Protocol"); + + QString socketTypeStr = QLatin1String("UnknownSocketType"); + if (socketType == QAbstractSocket::TcpSocket) socketTypeStr = QLatin1String("TcpSocket"); + else if (socketType == QAbstractSocket::UdpSocket) socketTypeStr = QLatin1String("UdpSocket"); + + qDebug("QSymbianSocketEnginePrivate::fetchConnectionParameters() local == %s:%i," + " peer == %s:%i, socket == %s - %s", + localAddress.toString().toLatin1().constData(), localPort, + peerAddress.toString().toLatin1().constData(), peerPort,socketTypeStr.toLatin1().constData(), + socketProtocolStr.toLatin1().constData()); +#endif + return true; +} + +void QSymbianSocketEngine::close() +{ + if (!isValid()) + return; + Q_D(QSymbianSocketEngine); +#if defined (QNATIVESOCKETENGINE_DEBUG) + qDebug("QSymbianSocketEngine::close()"); +#endif + + d->readNotificationsEnabled = false; + d->writeNotificationsEnabled = false; + d->exceptNotificationsEnabled = false; + if (d->asyncSelect) { + d->asyncSelect->deleteLater(); + d->asyncSelect = 0; + } + + //TODO: call nativeSocket.Shutdown(EImmediate) in some cases? + if (d->socketType == QAbstractSocket::UdpSocket) { + //TODO: Close hangs without this, but only for UDP - why? + TRequestStatus stat; + d->nativeSocket.Shutdown(RSocket::EImmediate, stat); + User::WaitForRequest(stat); + } +#ifdef QNATIVESOCKETENGINE_DEBUG + qDebug() << "QSymbianSocketEngine::close - closing socket" << d->nativeSocket.SubSessionHandle() << d->socketDescriptor; +#endif + //remove must come before close to avoid a race where another thread gets the old subsession handle + //reused & asserts when calling QSymbianSocketManager::instance->addSocket + QSymbianSocketManager::instance().removeSocket(d->nativeSocket); + d->nativeSocket.Close(); + d->socketDescriptor = -1; + + d->socketState = QAbstractSocket::UnconnectedState; + d->hasSetSocketError = false; + d->localPort = 0; + d->localAddress.clear(); + d->peerPort = 0; + d->peerAddress.clear(); + + d->hasReceivedBufferedDatagram = false; + d->receivedDataBuffer.clear(); +} + +qint64 QSymbianSocketEngine::write(const char *data, qint64 len) +{ + Q_D(QSymbianSocketEngine); + Q_CHECK_VALID_SOCKETLAYER(QSymbianSocketEngine::write(), -1); + Q_CHECK_STATE(QSymbianSocketEngine::write(), QAbstractSocket::ConnectedState, -1); + TPtrC8 buffer((TUint8*)data, (int)len); + TSockXfrLength sentBytes = 0; + TRequestStatus status; + d->nativeSocket.Send(buffer, 0, status, sentBytes); + User::WaitForRequest(status); //TODO: on emulator this blocks for write >16kB (non blocking IO not implemented properly?) + TInt err = status.Int(); + + if (err) { + switch (err) { + case KErrDisconnected: + case KErrEof: + sentBytes = -1; + d->setError(QAbstractSocket::RemoteHostClosedError, d->RemoteHostClosedErrorString); + close(); + break; + case KErrTooBig: + d->setError(QAbstractSocket::DatagramTooLargeError, d->DatagramTooLargeErrorString); + break; + case KErrWouldBlock: + break; + default: + sentBytes = -1; + d->setError(err); + close(); + break; + } + } + +#if defined (QNATIVESOCKETENGINE_DEBUG) + qDebug("QSymbianSocketEngine::write(%p \"%s\", %llu) == %i", + data, qt_prettyDebug(data, qMin((int) len, 16), + (int) len).data(), len, (int) sentBytes()); +#endif + + return qint64(sentBytes()); +} +/* +*/ +qint64 QSymbianSocketEngine::read(char *data, qint64 maxSize) +{ + Q_D(QSymbianSocketEngine); + Q_CHECK_VALID_SOCKETLAYER(QSymbianSocketEngine::read(), -1); + Q_CHECK_STATES(QSymbianSocketEngine::read(), QAbstractSocket::ConnectedState, QAbstractSocket::BoundState, -1); + + // if a datagram was buffered in pendingDatagramSize(), return it now + if (d->hasReceivedBufferedDatagram) { + qint64 size = qMin(maxSize, (qint64)d->receivedDataBuffer.size()); + memcpy(data, d->receivedDataBuffer.constData(), size); + d->receivedDataBuffer.clear(); + d->hasReceivedBufferedDatagram = false; +#if defined (QNATIVESOCKETENGINE_DEBUG) + qDebug() << "QSymbianSocketEngine::read returning" << size << "bytes from buffer"; +#endif + return size; + } + + TPtr8 buffer((TUint8*)data, (int)maxSize); + TSockXfrLength received = 0; + TRequestStatus status; + TSockAddr dummy; + if (d->socketType == QAbstractSocket::UdpSocket) { + //RecvOneOrMore() can only be used with stream-interfaced connected sockets; datagram interface sockets will return KErrNotSupported. + d->nativeSocket.RecvFrom(buffer, dummy, 0, status); + } else { + d->nativeSocket.RecvOneOrMore(buffer, 0, status, received); + } + User::WaitForRequest(status); //Non blocking receive + TInt err = status.Int(); + int r = buffer.Length(); + + if (err == KErrWouldBlock) { + // No data was available for reading + r = -2; + } else if (err != KErrNone) { + d->setError(err); + close(); + r = -1; + } + +#if defined (QNATIVESOCKETENGINE_DEBUG) + qDebug("QSymbianSocketEngine::read(%p \"%s\", %llu) == %i (err = %d)", + data, qt_prettyDebug(data, qMin(r, ssize_t(16)), r).data(), + maxSize, r, err); +#endif + + return qint64(r); +} + +int QSymbianSocketEnginePrivate::nativeSelect(int timeout, bool selectForRead) const +{ + bool readyRead = false; + bool readyWrite = false; + if (selectForRead) + return nativeSelect(timeout, true, false, &readyRead, &readyWrite); + else + return nativeSelect(timeout, false, true, &readyRead, &readyWrite); +} + +/*! + \internal + \param timeout timeout in milliseconds + \param checkRead caller is interested if the socket is ready to read + \param checkWrite caller is interested if the socket is ready for write + \param selectForRead (out) should set to true if ready to read + \param selectForWrite (out) should set to true if ready to write + \return 0 on timeout, >0 on success, <0 on error + */ +int QSymbianSocketEnginePrivate::nativeSelect(int timeout, bool checkRead, bool checkWrite, + bool *selectForRead, bool *selectForWrite) const +{ + //cancel asynchronous notifier (only one IOCTL allowed at a time) + if (asyncSelect) + asyncSelect->Cancel(); + + TPckgBuf<TUint> selectFlags; + selectFlags() = KSockSelectExcept; + if (checkRead) + selectFlags() |= KSockSelectRead; + if (checkWrite) + selectFlags() |= KSockSelectWrite; + TInt err; + if (timeout == 0) { + //if timeout is zero, poll + err = nativeSocket.GetOpt(KSOSelectPoll, KSOLSocket, selectFlags); + } else { + TRequestStatus selectStat; + nativeSocket.Ioctl(KIOctlSelect, selectStat, &selectFlags, KSOLSocket); + + if (timeout < 0) + User::WaitForRequest(selectStat); //negative means no timeout + else { + if (!selectTimer.Handle()) + qt_symbian_throwIfError(selectTimer.CreateLocal()); + TRequestStatus timerStat; + selectTimer.HighRes(timerStat, timeout * 1000); + User::WaitForRequest(timerStat, selectStat); + if (selectStat == KRequestPending) { + nativeSocket.CancelIoctl(); + //CancelIoctl completes the request (most likely with KErrCancel) + //We need to wait for this to keep the thread semaphore balanced (or active scheduler will panic) + User::WaitForRequest(selectStat); + //restart asynchronous notifier (only one IOCTL allowed at a time) + if (asyncSelect) + asyncSelect->IssueRequest(); + #ifdef QNATIVESOCKETENGINE_DEBUG + qDebug() << "QSymbianSocketEnginePrivate::nativeSelect: select timeout"; + #endif + return 0; //timeout + } else { + selectTimer.Cancel(); + User::WaitForRequest(timerStat); + } + } + + #ifdef QNATIVESOCKETENGINE_DEBUG + qDebug() << "QSymbianSocketEnginePrivate::nativeSelect: select status" << selectStat.Int() << (int)selectFlags(); + #endif + err = selectStat.Int(); + } + + if (!err && (selectFlags() & KSockSelectExcept)) { + nativeSocket.GetOpt(KSOSelectLastError, KSOLSocket, err); +#ifdef QNATIVESOCKETENGINE_DEBUG + qDebug() << "QSymbianSocketEnginePrivate::nativeSelect: select last error" << err; +#endif + } + if (err) { + //TODO: avoidable cast? + //set the error here, because read won't always return the same error again as select. + const_cast<QSymbianSocketEnginePrivate*>(this)->setError(err); + //restart asynchronous notifier (only one IOCTL allowed at a time) + if (asyncSelect) + asyncSelect->IssueRequest(); //TODO: in error case should we restart or not? + return err; + } + if (checkRead && (selectFlags() & KSockSelectRead)) { + Q_ASSERT(selectForRead); + *selectForRead = true; + } + if (checkWrite && (selectFlags() & KSockSelectWrite)) { + Q_ASSERT(selectForWrite); + *selectForWrite = true; + } + //restart asynchronous notifier (only one IOCTL allowed at a time) + if (asyncSelect) + asyncSelect->IssueRequest(); + return 1; +} + +bool QSymbianSocketEngine::joinMulticastGroup(const QHostAddress &groupAddress, + const QNetworkInterface &iface) +{ + Q_D(QSymbianSocketEngine); + Q_CHECK_VALID_SOCKETLAYER(QSymbianSocketEngine::joinMulticastGroup(), false); + Q_CHECK_STATE(QSymbianSocketEngine::joinMulticastGroup(), QAbstractSocket::BoundState, false); + Q_CHECK_TYPE(QSymbianSocketEngine::joinMulticastGroup(), QAbstractSocket::UdpSocket, false); + return d->multicastGroupMembershipHelper(groupAddress, iface, KSoIp6JoinGroup); +} + +bool QSymbianSocketEngine::leaveMulticastGroup(const QHostAddress &groupAddress, + const QNetworkInterface &iface) +{ + Q_D(QSymbianSocketEngine); + Q_CHECK_VALID_SOCKETLAYER(QSymbianSocketEngine::leaveMulticastGroup(), false); + Q_CHECK_STATE(QSymbianSocketEngine::leaveMulticastGroup(), QAbstractSocket::BoundState, false); + Q_CHECK_TYPE(QSymbianSocketEngine::leaveMulticastGroup(), QAbstractSocket::UdpSocket, false); + return d->multicastGroupMembershipHelper(groupAddress, iface, KSoIp6LeaveGroup); +} + +bool QSymbianSocketEnginePrivate::multicastGroupMembershipHelper(const QHostAddress &groupAddress, + const QNetworkInterface &iface, + TUint operation) +{ +#if defined (QNATIVESOCKETENGINE_DEBUG) + qDebug() << "QSymbianSocketEnginePrivate::multicastGroupMembershipHelper" << groupAddress << iface << operation; +#endif + //translate address + TPckgBuf<TIp6Mreq> option; + if (groupAddress.protocol() == QAbstractSocket::IPv6Protocol) { + Q_IPV6ADDR ip6 = groupAddress.toIPv6Address(); + memcpy(option().iAddr.u.iAddr8, ip6.c, 16); + } else { + TInetAddr wrapped; + wrapped.SetAddress(groupAddress.toIPv4Address()); + wrapped.ConvertToV4Mapped(); + option().iAddr = wrapped.Ip6Address(); + } + option().iInterface = iface.index(); + //join or leave group + TInt err = nativeSocket.SetOpt(operation, KSolInetIp, option); +#if defined (QNATIVESOCKETENGINE_DEBUG) + qDebug() << "address" << qt_prettyDebug((const char *)(option().iAddr.u.iAddr8), 16, 16); + qDebug() << "interface" << option().iInterface; + qDebug() << "error" << err; +#endif + if (err) { + setError(err); + } + return (KErrNone == err); +} + +QNetworkInterface QSymbianSocketEngine::multicastInterface() const +{ + //TODO + const Q_D(QSymbianSocketEngine); + Q_CHECK_VALID_SOCKETLAYER(QSymbianSocketEngine::multicastInterface(), QNetworkInterface()); + Q_CHECK_TYPE(QSymbianSocketEngine::multicastInterface(), QAbstractSocket::UdpSocket, QNetworkInterface()); + return QNetworkInterface(); +} + +bool QSymbianSocketEngine::setMulticastInterface(const QNetworkInterface &iface) +{ + //TODO - this is possibly a unix'ism as the RConnection on which the socket was created is probably controlling this + Q_D(QSymbianSocketEngine); + Q_CHECK_VALID_SOCKETLAYER(QSymbianSocketEngine::setMulticastInterface(), false); + Q_CHECK_TYPE(QSymbianSocketEngine::setMulticastInterface(), QAbstractSocket::UdpSocket, false); + return false; +} + +bool QSymbianSocketEnginePrivate::checkProxy(const QHostAddress &address) +{ + if (address == QHostAddress::LocalHost || address == QHostAddress::LocalHostIPv6) + return true; + +#if !defined(QT_NO_NETWORKPROXY) + QObject *parent = q_func()->parent(); + QNetworkProxy proxy; + if (QAbstractSocket *socket = qobject_cast<QAbstractSocket *>(parent)) { + proxy = socket->proxy(); + } else if (QTcpServer *server = qobject_cast<QTcpServer *>(parent)) { + proxy = server->proxy(); + } else { + // no parent -> no proxy + return true; + } + + if (proxy.type() == QNetworkProxy::DefaultProxy) + proxy = QNetworkProxy::applicationProxy(); + + if (proxy.type() != QNetworkProxy::DefaultProxy && + proxy.type() != QNetworkProxy::NoProxy) { + // QSymbianSocketEngine doesn't do proxies + setError(QAbstractSocket::UnsupportedSocketOperationError, + InvalidProxyTypeString); + return false; + } +#endif + + return true; +} + +// FIXME this is also in QNativeSocketEngine, unify it +/*! \internal + + Sets the error and error string if not set already. The only + interesting error is the first one that occurred, and not the last + one. +*/ +void QSymbianSocketEnginePrivate::setError(QAbstractSocket::SocketError error, ErrorString errorString) const +{ + if (hasSetSocketError) { + // Only set socket errors once for one engine; expect the + // socket to recreate its engine after an error. Note: There's + // one exception: SocketError(11) bypasses this as it's purely + // a temporary internal error condition. + // Another exception is the way the waitFor*() functions set + // an error when a timeout occurs. After the call to setError() + // they reset the hasSetSocketError to false + return; + } + if (error != QAbstractSocket::SocketError(11)) + hasSetSocketError = true; + + socketError = error; + + switch (errorString) { + case NonBlockingInitFailedErrorString: + socketErrorString = QSymbianSocketEngine::tr("Unable to initialize non-blocking socket"); + break; + case BroadcastingInitFailedErrorString: + socketErrorString = QSymbianSocketEngine::tr("Unable to initialize broadcast socket"); + break; + case NoIpV6ErrorString: + socketErrorString = QSymbianSocketEngine::tr("Attempt to use IPv6 socket on a platform with no IPv6 support"); + break; + case RemoteHostClosedErrorString: + socketErrorString = QSymbianSocketEngine::tr("The remote host closed the connection"); + break; + case TimeOutErrorString: + socketErrorString = QSymbianSocketEngine::tr("Network operation timed out"); + break; + case ResourceErrorString: + socketErrorString = QSymbianSocketEngine::tr("Out of resources"); + break; + case OperationUnsupportedErrorString: + socketErrorString = QSymbianSocketEngine::tr("Unsupported socket operation"); + break; + case ProtocolUnsupportedErrorString: + socketErrorString = QSymbianSocketEngine::tr("Protocol type not supported"); + break; + case InvalidSocketErrorString: + socketErrorString = QSymbianSocketEngine::tr("Invalid socket descriptor"); + break; + case HostUnreachableErrorString: + socketErrorString = QSymbianSocketEngine::tr("Host unreachable"); + break; + case NetworkUnreachableErrorString: + socketErrorString = QSymbianSocketEngine::tr("Network unreachable"); + break; + case AccessErrorString: + socketErrorString = QSymbianSocketEngine::tr("Permission denied"); + break; + case ConnectionTimeOutErrorString: + socketErrorString = QSymbianSocketEngine::tr("Connection timed out"); + break; + case ConnectionRefusedErrorString: + socketErrorString = QSymbianSocketEngine::tr("Connection refused"); + break; + case AddressInuseErrorString: + socketErrorString = QSymbianSocketEngine::tr("The bound address is already in use"); + break; + case AddressNotAvailableErrorString: + socketErrorString = QSymbianSocketEngine::tr("The address is not available"); + break; + case AddressProtectedErrorString: + socketErrorString = QSymbianSocketEngine::tr("The address is protected"); + break; + case DatagramTooLargeErrorString: + socketErrorString = QSymbianSocketEngine::tr("Datagram was too large to send"); + break; + case SendDatagramErrorString: + socketErrorString = QSymbianSocketEngine::tr("Unable to send a message"); + break; + case ReceiveDatagramErrorString: + socketErrorString = QSymbianSocketEngine::tr("Unable to receive a message"); + break; + case WriteErrorString: + socketErrorString = QSymbianSocketEngine::tr("Unable to write"); + break; + case ReadErrorString: + socketErrorString = QSymbianSocketEngine::tr("Network error"); + break; + case PortInuseErrorString: + socketErrorString = QSymbianSocketEngine::tr("Another socket is already listening on the same port"); + break; + case NotSocketErrorString: + socketErrorString = QSymbianSocketEngine::tr("Operation on non-socket"); + break; + case InvalidProxyTypeString: + socketErrorString = QSymbianSocketEngine::tr("The proxy type is invalid for this operation"); + break; + case InvalidAddressErrorString: + socketErrorString = QSymbianSocketEngine::tr("The address is invalid for this operation"); + break; + case SessionNotOpenErrorString: + socketErrorString = QSymbianSocketEngine::tr("The specified network session is not opened"); + break; + case UnknownSocketErrorString: + socketErrorString = QSymbianSocketEngine::tr("Unknown error"); + break; + } +} + +void QSymbianSocketEnginePrivate::setError(TInt symbianError) +{ + switch (symbianError) { + case KErrDisconnected: + case KErrEof: + case KErrConnectionTerminated: //interface stopped externally - RConnection::Stop(EStopAuthoritative) + setError(QAbstractSocket::RemoteHostClosedError, + QSymbianSocketEnginePrivate::RemoteHostClosedErrorString); + break; + case KErrNetUnreach: + setError(QAbstractSocket::NetworkError, + QSymbianSocketEnginePrivate::NetworkUnreachableErrorString); + break; + case KErrHostUnreach: + setError(QAbstractSocket::NetworkError, + QSymbianSocketEnginePrivate::HostUnreachableErrorString); + break; + case KErrNoProtocolOpt: + setError(QAbstractSocket::NetworkError, + QSymbianSocketEnginePrivate::ProtocolUnsupportedErrorString); + break; + case KErrInUse: + setError(QAbstractSocket::AddressInUseError, AddressInuseErrorString); + break; + case KErrPermissionDenied: + setError(QAbstractSocket::SocketAccessError, AccessErrorString); + break; + case KErrNotSupported: + setError(QAbstractSocket::UnsupportedSocketOperationError, OperationUnsupportedErrorString); + break; + case KErrNoMemory: + setError(QAbstractSocket::SocketResourceError, ResourceErrorString); + break; + case KErrCouldNotConnect: + setError(QAbstractSocket::ConnectionRefusedError, ConnectionRefusedErrorString); + break; + case KErrTimedOut: + setError(QAbstractSocket::NetworkError, ConnectionTimeOutErrorString); + break; + case KErrBadName: + setError(QAbstractSocket::NetworkError, InvalidAddressErrorString); + break; + default: + socketError = QAbstractSocket::NetworkError; + socketErrorString = QSystemError(symbianError, QSystemError::NativeError).toString(); + break; + } + hasSetSocketError = true; +} + +void QSymbianSocketEngine::startNotifications() +{ + Q_D(QSymbianSocketEngine); +#ifdef QNATIVESOCKETENGINE_DEBUG + qDebug() << "QSymbianSocketEngine::startNotifications" << d->readNotificationsEnabled << d->writeNotificationsEnabled << d->exceptNotificationsEnabled; +#endif + if (!d->asyncSelect && (d->readNotificationsEnabled || d->writeNotificationsEnabled || d->exceptNotificationsEnabled)) { + Q_CHECK_VALID_SOCKETLAYER(QSymbianSocketEngine::startNotifications(), Q_VOID); + if (d->threadData->eventDispatcher) { + d->asyncSelect = q_check_ptr(new QAsyncSelect( + static_cast<QEventDispatcherSymbian*> (d->threadData->eventDispatcher), d->nativeSocket, + this)); + } else { + // call again when event dispatcher has been created + QMetaObject::invokeMethod(this, "startNotifications", Qt::QueuedConnection); + } + } + if (d->asyncSelect) + d->asyncSelect->IssueRequest(); +} + +bool QSymbianSocketEngine::isReadNotificationEnabled() const +{ + Q_D(const QSymbianSocketEngine); + return d->readNotificationsEnabled; +} + +void QSymbianSocketEngine::setReadNotificationEnabled(bool enable) +{ + Q_D(QSymbianSocketEngine); +#ifdef QNATIVESOCKETENGINE_DEBUG + qDebug() << "QSymbianSocketEngine::setReadNotificationEnabled" << enable << "socket" << d->socketDescriptor; +#endif + d->readNotificationsEnabled = enable; + startNotifications(); +} + +bool QSymbianSocketEngine::isWriteNotificationEnabled() const +{ + Q_D(const QSymbianSocketEngine); + return d->writeNotificationsEnabled; +} + +void QSymbianSocketEngine::setWriteNotificationEnabled(bool enable) +{ + Q_D(QSymbianSocketEngine); +#ifdef QNATIVESOCKETENGINE_DEBUG + qDebug() << "QSymbianSocketEngine::setWriteNotificationEnabled" << enable << "socket" << d->socketDescriptor; +#endif + d->writeNotificationsEnabled = enable; + startNotifications(); +} + +bool QSymbianSocketEngine::isExceptionNotificationEnabled() const +{ + Q_D(const QSymbianSocketEngine); + return d->exceptNotificationsEnabled; + return false; +} + +void QSymbianSocketEngine::setExceptionNotificationEnabled(bool enable) +{ + Q_D(QSymbianSocketEngine); +#ifdef QNATIVESOCKETENGINE_DEBUG + qDebug() << "QSymbianSocketEngine::setExceptionNotificationEnabled" << enable << "socket" << d->socketDescriptor; +#endif + d->exceptNotificationsEnabled = enable; + startNotifications(); +} + +bool QSymbianSocketEngine::waitForRead(int msecs, bool *timedOut) +{ + Q_D(const QSymbianSocketEngine); + Q_CHECK_VALID_SOCKETLAYER(QSymbianSocketEngine::waitForRead(), false); + Q_CHECK_NOT_STATE(QSymbianSocketEngine::waitForRead(), + QAbstractSocket::UnconnectedState, false); + + if (timedOut) + *timedOut = false; + + int ret = d->nativeSelect(msecs, true); + if (ret == 0) { + if (timedOut) + *timedOut = true; + d->setError(QAbstractSocket::SocketTimeoutError, + d->TimeOutErrorString); + d->hasSetSocketError = false; // A timeout error is temporary in waitFor functions + return false; + } else if (state() == QAbstractSocket::ConnectingState) { + connectToHost(d->peerAddress, d->peerPort); + } + + return ret > 0; +} + +bool QSymbianSocketEngine::waitForWrite(int msecs, bool *timedOut) +{ + Q_D(QSymbianSocketEngine); + Q_CHECK_VALID_SOCKETLAYER(QSymbianSocketEngine::waitForWrite(), false); + Q_CHECK_NOT_STATE(QSymbianSocketEngine::waitForWrite(), + QAbstractSocket::UnconnectedState, false); + + if (timedOut) + *timedOut = false; + + int ret = d->nativeSelect(msecs, false); + + if (ret == 0) { + if (timedOut) + *timedOut = true; + d->setError(QAbstractSocket::SocketTimeoutError, + d->TimeOutErrorString); + d->hasSetSocketError = false; // A timeout error is temporary in waitFor functions + return false; + } else if (state() == QAbstractSocket::ConnectingState) { + connectToHost(d->peerAddress, d->peerPort); + } + + return ret > 0; +} + +bool QSymbianSocketEngine::waitForReadOrWrite(bool *readyToRead, bool *readyToWrite, + bool checkRead, bool checkWrite, + int msecs, bool *timedOut) +{ + Q_D(QSymbianSocketEngine); + Q_CHECK_VALID_SOCKETLAYER(QSymbianSocketEngine::waitForWrite(), false); + Q_CHECK_NOT_STATE(QSymbianSocketEngine::waitForReadOrWrite(), + QAbstractSocket::UnconnectedState, false); + + int ret = d->nativeSelect(msecs, checkRead, checkWrite, readyToRead, readyToWrite); + + if (ret == 0) { + if (timedOut) + *timedOut = true; + d->setError(QAbstractSocket::SocketTimeoutError, + d->TimeOutErrorString); + d->hasSetSocketError = false; // A timeout error is temporary in waitFor functions + return false; + } else if (state() == QAbstractSocket::ConnectingState) { + connectToHost(d->peerAddress, d->peerPort); + } + + return ret > 0; +} + +qint64 QSymbianSocketEngine::bytesToWrite() const +{ + // This is what the QNativeSocketEngine does + return 0; +} + +bool QSymbianSocketEngine::event(QEvent* ev) +{ + Q_D(QSymbianSocketEngine); + if (ev->type() == QEvent::ThreadChange) { +#ifdef QNATIVESOCKETENGINE_DEBUG + qDebug() << "QSymbianSocketEngine::event - ThreadChange" << d->readNotificationsEnabled << d->writeNotificationsEnabled << d->exceptNotificationsEnabled; +#endif + if (d->asyncSelect) { + delete d->asyncSelect; + d->asyncSelect = 0; + // recreate select in new thread (because it is queued, the method is called in the new thread context) + QMetaObject::invokeMethod(this, "startNotifications", Qt::QueuedConnection); + } + d->selectTimer.Close(); + return true; + } + return QAbstractSocketEngine::event(ev); +} + +QAsyncSelect::QAsyncSelect(QEventDispatcherSymbian *dispatcher, RSocket& sock, QSymbianSocketEngine *parent) + : QActiveObject(CActive::EPriorityStandard, dispatcher), + m_inSocketEvent(false), + m_deleteLater(false), + m_socket(sock), + m_selectFlags(0), + engine(parent) +{ + CActiveScheduler::Add(this); +} + +QAsyncSelect::~QAsyncSelect() +{ + Cancel(); +} + +void QAsyncSelect::DoCancel() +{ + m_socket.CancelIoctl(); +} + +void QAsyncSelect::RunL() +{ + QT_TRYCATCH_LEAVING(run()); +} + +//RunError is called by the active scheduler if RunL leaves. +//Typically this will happen if a std::bad_alloc propagates down from the application +TInt QAsyncSelect::RunError(TInt aError) +{ + if (engine) { + QT_TRY { + engine->d_func()->setError(aError); + if (engine->isExceptionNotificationEnabled()) + engine->exceptionNotification(); + if (engine->isReadNotificationEnabled()) + engine->readNotification(); + } + QT_CATCH(...) {} + } +#ifdef QNATIVESOCKETENGINE_DEBUG + qDebug() << "QAsyncSelect::RunError" << aError; +#endif + return KErrNone; +} + +void QAsyncSelect::run() +{ + //when event loop disabled socket events, defer until later + if (maybeDeferSocketEvent()) + return; +#if defined (QNATIVESOCKETENGINE_DEBUG) + qDebug() << "QAsyncSelect::run" << m_selectBuf() << m_selectFlags; +#endif + m_inSocketEvent = true; + m_selectBuf() &= m_selectFlags; //the select ioctl reports everything, so mask to only what we requested + //KSockSelectReadContinuation is for reading datagrams in a mode that doesn't discard when the + //datagram is larger than the read buffer - Qt doesn't need to use this. + if (engine && engine->isReadNotificationEnabled() + && ((m_selectBuf() & KSockSelectRead) || iStatus != KErrNone)) { + engine->readNotification(); + } + if (engine && engine->isWriteNotificationEnabled() + && ((m_selectBuf() & KSockSelectWrite) || iStatus != KErrNone)) { + if (engine->state() == QAbstractSocket::ConnectingState) + engine->connectionNotification(); + else + engine->writeNotification(); + } + if (engine && engine->isExceptionNotificationEnabled() + && ((m_selectBuf() & KSockSelectExcept) || iStatus != KErrNone)) { + engine->exceptionNotification(); + } + m_inSocketEvent = false; + if (m_deleteLater) { + delete this; + return; + } + // select again (unless disabled by one of the callbacks) + IssueRequest(); +} + +void QAsyncSelect::deleteLater() +{ + if (m_inSocketEvent) { + engine = 0; + m_deleteLater = true; + } else { + delete this; + } +} + +void QAsyncSelect::IssueRequest() +{ + if (m_inSocketEvent) + return; //prevent thrashing during a callback - socket engine enables/disables multiple notifiers + TUint selectFlags = 0; + if (engine->isReadNotificationEnabled()) + selectFlags |= KSockSelectRead; + if (engine->isWriteNotificationEnabled()) + selectFlags |= KSockSelectWrite; + if (engine->isExceptionNotificationEnabled()) + selectFlags |= KSockSelectExcept; + if (selectFlags != m_selectFlags) { +#ifdef QNATIVESOCKETENGINE_DEBUG + qDebug() << "QAsyncSelect::IssueRequest() - select flags" << m_selectFlags << "->" << selectFlags; +#endif + Cancel(); + m_selectFlags = selectFlags; + } + if (m_selectFlags && !IsActive()) { + //always request errors (write notification does not complete on connect errors) + m_selectBuf() = m_selectFlags | KSockSelectExcept; + m_socket.Ioctl(KIOctlSelect, iStatus, &m_selectBuf, KSOLSocket); + SetActive(); + } +#ifdef QNATIVESOCKETENGINE_DEBUG + qDebug() << "QAsyncSelect::IssueRequest() - IsActive" << IsActive(); +#endif +} + +QT_END_NAMESPACE diff --git a/src/network/socket/qsymbiansocketengine_p.h b/src/network/socket/qsymbiansocketengine_p.h new file mode 100644 index 0000000..81156fc --- /dev/null +++ b/src/network/socket/qsymbiansocketengine_p.h @@ -0,0 +1,258 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtNetwork 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$ +** +****************************************************************************/ + +#ifndef QSYMBIANSOCKETENGINE_P_H +#define QSYMBIANSOCKETENGINE_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of the QLibrary class. This header file may change from +// version to version without notice, or even be removed. +// +// We mean it. +// +#include "QtNetwork/qhostaddress.h" +#include "private/qabstractsocketengine_p.h" +#include "qplatformdefs.h" + +#include <private/qeventdispatcher_symbian_p.h> +#include <unistd.h> +#include <es_sock.h> +#include <in_sock.h> + +QT_BEGIN_NAMESPACE + + +class QSymbianSocketEnginePrivate; +class QNetworkInterface; + +class Q_AUTOTEST_EXPORT QSymbianSocketEngine : public QAbstractSocketEngine +{ + Q_OBJECT + friend class QAsyncSelect; +public: + QSymbianSocketEngine(QObject *parent = 0); + ~QSymbianSocketEngine(); + + bool initialize(QAbstractSocket::SocketType type, QAbstractSocket::NetworkLayerProtocol protocol = QAbstractSocket::IPv4Protocol); + bool initialize(int socketDescriptor, QAbstractSocket::SocketState socketState = QAbstractSocket::ConnectedState); + + int socketDescriptor() const; + + bool isValid() const; + + bool connectToHost(const QHostAddress &address, quint16 port); + bool connectToHostByName(const QString &name, quint16 port); + bool bind(const QHostAddress &address, quint16 port); + bool listen(); + int accept(); + void close(); + + bool joinMulticastGroup(const QHostAddress &groupAddress, + const QNetworkInterface &iface); + bool leaveMulticastGroup(const QHostAddress &groupAddress, + const QNetworkInterface &iface); + QNetworkInterface multicastInterface() const; + bool setMulticastInterface(const QNetworkInterface &iface); + + qint64 bytesAvailable() const; + + qint64 read(char *data, qint64 maxlen); + qint64 write(const char *data, qint64 len); + + qint64 readDatagram(char *data, qint64 maxlen, QHostAddress *addr = 0, + quint16 *port = 0); + qint64 writeDatagram(const char *data, qint64 len, const QHostAddress &addr, + quint16 port); + bool hasPendingDatagrams() const; + qint64 pendingDatagramSize() const; + + qint64 bytesToWrite() const; + + qint64 receiveBufferSize() const; + void setReceiveBufferSize(qint64 bufferSize); + + qint64 sendBufferSize() const; + void setSendBufferSize(qint64 bufferSize); + + int option(SocketOption option) const; + bool setOption(SocketOption option, int value); + + bool waitForRead(int msecs = 30000, bool *timedOut = 0); + bool waitForWrite(int msecs = 30000, bool *timedOut = 0); + bool waitForReadOrWrite(bool *readyToRead, bool *readyToWrite, + bool checkRead, bool checkWrite, + int msecs = 30000, bool *timedOut = 0); + + bool isReadNotificationEnabled() const; + void setReadNotificationEnabled(bool enable); + bool isWriteNotificationEnabled() const; + void setWriteNotificationEnabled(bool enable); + bool isExceptionNotificationEnabled() const; + void setExceptionNotificationEnabled(bool enable); + + bool event(QEvent* ev); + + Q_INVOKABLE void startNotifications(); + +public Q_SLOTS: + // TODO: Why do we do this? This is private Qt implementation stuff anyway, no need for it + // non-virtual override; + void connectionNotification(); + +private: + Q_DECLARE_PRIVATE(QSymbianSocketEngine) + Q_DISABLE_COPY(QSymbianSocketEngine) +}; + +class QSocketNotifier; + +class QReadNotifier; +class QWriteNotifier; +class QExceptionNotifier; +class QAsyncSelect : public QActiveObject +{ +public: + QAsyncSelect(QEventDispatcherSymbian *dispatcher, RSocket& sock, QSymbianSocketEngine *parent); + ~QAsyncSelect(); + + void deleteLater(); + void IssueRequest(); + + void refresh(); + +protected: + void DoCancel(); + void RunL(); + void run(); + TInt RunError(TInt aError); + +private: + bool m_inSocketEvent; + bool m_deleteLater; + RSocket &m_socket; + + TUint m_selectFlags; + TPckgBuf<TUint> m_selectBuf; //in & out IPC buffer + QSymbianSocketEngine *engine; +}; + +class QSymbianSocketEnginePrivate : public QAbstractSocketEnginePrivate +{ + Q_DECLARE_PUBLIC(QSymbianSocketEngine) +public: + QSymbianSocketEnginePrivate(); + ~QSymbianSocketEnginePrivate(); + + int socketDescriptor; + mutable RSocket nativeSocket; + // From QtCore: + RSocketServ& socketServer; + mutable RTimer selectTimer; + + bool readNotificationsEnabled; + bool writeNotificationsEnabled; + bool exceptNotificationsEnabled; + QAsyncSelect* asyncSelect; + + mutable QByteArray receivedDataBuffer; + mutable bool hasReceivedBufferedDatagram; + // FIXME this is duplicated from qnativesocketengine_p.h + enum ErrorString { + NonBlockingInitFailedErrorString, + BroadcastingInitFailedErrorString, + NoIpV6ErrorString, + RemoteHostClosedErrorString, + TimeOutErrorString, + ResourceErrorString, + OperationUnsupportedErrorString, + ProtocolUnsupportedErrorString, + InvalidSocketErrorString, + HostUnreachableErrorString, + NetworkUnreachableErrorString, + AccessErrorString, + ConnectionTimeOutErrorString, + ConnectionRefusedErrorString, + AddressInuseErrorString, + AddressNotAvailableErrorString, + AddressProtectedErrorString, + DatagramTooLargeErrorString, + SendDatagramErrorString, + ReceiveDatagramErrorString, + WriteErrorString, + ReadErrorString, + PortInuseErrorString, + NotSocketErrorString, + InvalidProxyTypeString, + //symbian specific + InvalidAddressErrorString, + SessionNotOpenErrorString, + + UnknownSocketErrorString = -1 + }; + void setError(QAbstractSocket::SocketError error, ErrorString errorString) const; + + void getPortAndAddress(const TInetAddr& a, quint16 *port, QHostAddress *addr); + void setPortAndAddress(TInetAddr& nativeAddr, quint16 port, const QHostAddress &addr); + void setError(TInt symbianError); + + int nativeSelect(int timeout, bool selectForRead) const; + int nativeSelect(int timeout, bool checkRead, bool checkWrite, + bool *selectForRead, bool *selectForWrite) const; + + bool createNewSocket(QAbstractSocket::SocketType socketType, + QAbstractSocket::NetworkLayerProtocol socketProtocol); + + bool checkProxy(const QHostAddress &address); + bool fetchConnectionParameters(); + + bool multicastGroupMembershipHelper(const QHostAddress &groupAddress, + const QNetworkInterface &iface, + TUint operation); + static bool translateSocketOption(QAbstractSocketEngine::SocketOption opt, TUint &n, TUint &level); +}; + +QT_END_NAMESPACE + +#endif // QSYMBIANSOCKETENGINE_P_H diff --git a/src/network/socket/qtcpserver.cpp b/src/network/socket/qtcpserver.cpp index 5a52084..ccdc50e 100644 --- a/src/network/socket/qtcpserver.cpp +++ b/src/network/socket/qtcpserver.cpp @@ -105,7 +105,7 @@ #include "qhostaddress.h" #include "qlist.h" #include "qpointer.h" -#include "qnativesocketengine_p.h" +#include "qabstractsocketengine_p.h" #include "qtcpserver.h" #include "qtcpsocket.h" #include "qnetworkproxy.h" @@ -292,6 +292,10 @@ bool QTcpServer::listen(const QHostAddress &address, quint16 port) d->serverSocketErrorString = tr("Operation on socket is not supported"); return false; } +#ifndef QT_NO_BEARERMANAGEMENT + //copy network session down to the socket engine (if it has been set) + d->socketEngine->setProperty("_q_networksession", property("_q_networksession")); +#endif if (!d->socketEngine->initialize(QAbstractSocket::TcpSocket, proto)) { d->serverSocketError = d->socketEngine->error(); d->serverSocketErrorString = d->socketEngine->errorString(); @@ -412,6 +416,15 @@ bool QTcpServer::setSocketDescriptor(int socketDescriptor) if (d->socketEngine) delete d->socketEngine; d->socketEngine = QAbstractSocketEngine::createSocketEngine(socketDescriptor, this); + if (!d->socketEngine) { + d->serverSocketError = QAbstractSocket::UnsupportedSocketOperationError; + d->serverSocketErrorString = tr("Operation on socket is not supported"); + return false; + } +#ifndef QT_NO_BEARERMANAGEMENT + //copy network session down to the socket engine (if it has been set) + d->socketEngine->setProperty("_q_networksession", property("_q_networksession")); +#endif if (!d->socketEngine->initialize(socketDescriptor, QAbstractSocket::ListeningState)) { d->serverSocketError = d->socketEngine->error(); d->serverSocketErrorString = d->socketEngine->errorString(); diff --git a/src/network/socket/qudpsocket.cpp b/src/network/socket/qudpsocket.cpp index e3480b1..6bc93dd 100644 --- a/src/network/socket/qudpsocket.cpp +++ b/src/network/socket/qudpsocket.cpp @@ -77,14 +77,23 @@ \snippet doc/src/snippets/code/src_network_socket_qudpsocket.cpp 0 + QUdpSocket also supports UDP multicast. Use joinMulticastGroup() and + leaveMulticastGroup() to control group membership, and + QAbstractSocket::MulticastTtlOption and + QAbstractSocket::MulticastLoopbackOption to set the TTL and loopback socket + options. Use setMulticastInterface() to control the outgoing interface for + multicast datagrams, and multicastInterface() to query it. + With QUdpSocket, you can also establish a virtual connection to a UDP server using connectToHost() and then use read() and write() to exchange datagrams without specifying the receiver for each datagram. - The \l{network/broadcastsender}{Broadcast Sender} and - \l{network/broadcastreceiver}{Broadcast Receiver} examples - illustrate how to use QUdpSocket in applications. + The \l{network/broadcastsender}{Broadcast Sender}, + \l{network/broadcastreceiver}{Broadcast Receiver}, + \l{network/multicastsender}{Multicast Sender}, and + \l{network/multicastreceiver}{Multicast Receiver} examples illustrate how + to use QUdpSocket in applications. \section1 Symbian Platform Security Requirements @@ -145,6 +154,7 @@ */ #include "qhostaddress.h" +#include "qnetworkinterface.h" #include "qabstractsocket_p.h" #include "qudpsocket.h" @@ -192,7 +202,7 @@ bool QUdpSocketPrivate::doEnsureInitialized(const QHostAddress &bindAddress, qui #endif // now check if the socket engine is initialized and to the right type - if (!socketEngine || !socketEngine->isValid() || socketEngine->protocol() != proto) { + if (!socketEngine || !socketEngine->isValid()) { resolveProxy(remoteAddress.toString(), bindPort); if (!initSocketLayer(address->protocol())) return false; @@ -328,6 +338,116 @@ bool QUdpSocket::bind(quint16 port, BindMode mode) return bind(QHostAddress::Any, port, mode); } +#ifndef QT_NO_NETWORKINTERFACE + +/*! + \since 4.8 + + Joins the the multicast group specified by \a groupAddress on the default + interface chosen by the operating system. The socket must be in BoundState, + otherwise an error occurs. + + This function returns true if successful; otherwise it returns false + and sets the socket error accordingly. + + \sa leaveMulticastGroup() +*/ +bool QUdpSocket::joinMulticastGroup(const QHostAddress &groupAddress) +{ + return joinMulticastGroup(groupAddress, QNetworkInterface()); +} + +/*! + \since 4.8 + \overload + + Joins the multicast group address \a groupAddress on the interface \a + iface. + + \sa leaveMulticastGroup() +*/ +bool QUdpSocket::joinMulticastGroup(const QHostAddress &groupAddress, + const QNetworkInterface &iface) +{ + Q_D(QUdpSocket); + QT_CHECK_BOUND("QUdpSocket::joinMulticastGroup()", false); + return d->socketEngine->joinMulticastGroup(groupAddress, iface); +} + +/*! + \since 4.8 + + Leaves the multicast group specified by \a groupAddress on the default + interface chosen by the operating system. The socket must be in BoundState, + otherwise an error occurs. + + This function returns true if successful; otherwise it returns false and + sets the socket error accordingly. + + \sa joinMulticastGroup() +*/ +bool QUdpSocket::leaveMulticastGroup(const QHostAddress &groupAddress) +{ + return leaveMulticastGroup(groupAddress, QNetworkInterface()); +} + +/*! + \since 4.8 + \overload + + Leaves the multicast group specified by \a groupAddress on the interface \a + iface. + + \sa joinMulticastGroup() +*/ +bool QUdpSocket::leaveMulticastGroup(const QHostAddress &groupAddress, + const QNetworkInterface &iface) +{ + QT_CHECK_BOUND("QUdpSocket::leaveMulticastGroup()", false); + return d_func()->socketEngine->leaveMulticastGroup(groupAddress, iface); +} + +/*! + \since 4.8 + + Returns the interface for the outgoing interface for multicast datagrams. + This corresponds to the IP_MULTICAST_IF socket option for IPv4 sockets and + the IPV6_MULTICAST_IF socket option for IPv6 sockets. If no interface has + been previously set, this function returns an invalid QNetworkInterface. + The socket must be in BoundState, otherwise an invalid QNetworkInterface is + returned. + + \sa setMulticastInterface() +*/ +QNetworkInterface QUdpSocket::multicastInterface() const +{ + Q_D(const QUdpSocket); + QT_CHECK_BOUND("QUdpSocket::multicastInterface()", QNetworkInterface()); + return d->socketEngine->multicastInterface(); +} + +/*! + \since 4.8 + + Sets the outgoing interface for multicast datagrams to the interface \a + iface. This corresponds to the IP_MULTICAST_IF socket option for IPv4 + sockets and the IPV6_MULTICAST_IF socket option for IPv6 sockets. The + socket must be in BoundState, otherwise this function does nothing. + + \sa multicastInterface(), joinMulticastGroup(), leaveMulticastGroup() +*/ +void QUdpSocket::setMulticastInterface(const QNetworkInterface &iface) +{ + Q_D(QUdpSocket); + if (!isValid()) { + qWarning("QUdpSocket::setMulticastInterface() called on a QUdpSocket when not in QUdpSocket::BoundState"); + return; + } + d->socketEngine->setMulticastInterface(iface); +} + +#endif // QT_NO_NETWORKINTERFACE + /*! Returns true if at least one datagram is waiting to be read; otherwise returns false. @@ -388,16 +508,6 @@ qint64 QUdpSocket::writeDatagram(const char *data, qint64 size, const QHostAddre return -1; qint64 sent = d->socketEngine->writeDatagram(data, size, address, port); -#ifdef Q_OS_SYMBIAN - if( QSysInfo::s60Version() <= QSysInfo::SV_S60_5_0 ) { - // This is evil hack, but for some reason native RSocket::SendTo returns 0, - // for large datagrams (such as 600 bytes). Based on comments from Open C team - // this should happen only in platforms <= S60 5.0. - // As an workaround, we just set sent = size - if( sent == 0 ) - sent = size; - } -#endif d->cachedSocketDescriptor = d->socketEngine->socketDescriptor(); if (sent >= 0) { diff --git a/src/network/socket/qudpsocket.h b/src/network/socket/qudpsocket.h index aafef5c..c477abd 100644 --- a/src/network/socket/qudpsocket.h +++ b/src/network/socket/qudpsocket.h @@ -53,6 +53,7 @@ QT_MODULE(Network) #ifndef QT_NO_UDPSOCKET +class QNetworkInterface; class QUdpSocketPrivate; class Q_NETWORK_EXPORT QUdpSocket : public QAbstractSocket @@ -76,6 +77,18 @@ public: bool bind(quint16 port, BindMode mode); // ### Qt 5: Merge the bind functions +#ifndef QT_NO_NETWORKINTERFACE + bool joinMulticastGroup(const QHostAddress &groupAddress); + bool joinMulticastGroup(const QHostAddress &groupAddress, + const QNetworkInterface &iface); + bool leaveMulticastGroup(const QHostAddress &groupAddress); + bool leaveMulticastGroup(const QHostAddress &groupAddress, + const QNetworkInterface &iface); + + QNetworkInterface multicastInterface() const; + void setMulticastInterface(const QNetworkInterface &iface); +#endif + bool hasPendingDatagrams() const; qint64 pendingDatagramSize() const; qint64 readDatagram(char *data, qint64 maxlen, QHostAddress *host = 0, quint16 *port = 0); diff --git a/src/network/socket/socket.pri b/src/network/socket/socket.pri index 2bafe13..ac90012 100644 --- a/src/network/socket/socket.pri +++ b/src/network/socket/socket.pri @@ -1,7 +1,6 @@ # Qt network socket HEADERS += socket/qabstractsocketengine_p.h \ - socket/qnativesocketengine_p.h \ socket/qhttpsocketengine_p.h \ socket/qsocks5socketengine_p.h \ socket/qabstractsocket.h \ @@ -15,7 +14,6 @@ HEADERS += socket/qabstractsocketengine_p.h \ socket/qlocalsocket_p.h SOURCES += socket/qabstractsocketengine.cpp \ - socket/qnativesocketengine.cpp \ socket/qhttpsocketengine.cpp \ socket/qsocks5socketengine.cpp \ socket/qabstractsocket.cpp \ @@ -25,9 +23,26 @@ SOURCES += socket/qabstractsocketengine.cpp \ socket/qlocalsocket.cpp \ socket/qlocalserver.cpp -unix:SOURCES += socket/qnativesocketengine_unix.cpp \ +# On Symbian we use QSymbianSocketEngine +symbian:SOURCES += socket/qsymbiansocketengine.cpp +symbian:HEADERS += socket/qsymbiansocketengine_p.h +# On others we use QNativeSocketEngine +!symbian:SOURCES += socket/qnativesocketengine.cpp +!symbian:HEADERS += socket/qnativesocketengine_p.h + +unix:!symbian: { + SOURCES += socket/qnativesocketengine_unix.cpp \ socket/qlocalsocket_unix.cpp \ socket/qlocalserver_unix.cpp +} + +symbian: { + SOURCES += socket/qlocalsocket_tcp.cpp \ + socket/qlocalserver_tcp.cpp + + DEFINES += QT_LOCALSOCKET_TCP +} + unix:HEADERS += \ socket/qnet_unix_p.h @@ -43,3 +58,13 @@ wince*: { DEFINES += QT_LOCALSOCKET_TCP } + +integrity: { + SOURCES -= socket/qlocalsocket_unix.cpp \ + socket/qlocalserver_unix.cpp + SOURCES += socket/qlocalsocket_tcp.cpp \ + socket/qlocalserver_tcp.cpp \ + socket/qnativesocketengine_unix.cpp + + DEFINES += QT_LOCALSOCKET_TCP +} |