File indexing completed on 2024-04-14 14:19:52

0001 /*  -*- C++ -*-
0002  *  Copyright (C) 2003 Thiago Macieira <thiago@kde.org>
0003  *
0004  *
0005  *  Permission is hereby granted, free of charge, to any person obtaining
0006  *  a copy of this software and associated documentation files (the
0007  *  "Software"), to deal in the Software without restriction, including
0008  *  without limitation the rights to use, copy, modify, merge, publish,
0009  *  distribute, sublicense, and/or sell copies of the Software, and to
0010  *  permit persons to whom the Software is furnished to do so, subject to
0011  *  the following conditions:
0012  *
0013  *  The above copyright notice and this permission notice shall be included
0014  *  in all copies or substantial portions of the Software.
0015  *
0016  *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
0017  *  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
0018  *  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
0019  *  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
0020  *  LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
0021  *  OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
0022  *  WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
0023  */
0024 
0025 #include "k3streamsocket.h"
0026 
0027 #include <config-network.h>
0028 
0029 #include <QSocketNotifier>
0030 #include <QDateTime>
0031 #include <QTimer>
0032 #include <QElapsedTimer>
0033 #include <QPointer>
0034 
0035 #include "k3socketaddress.h"
0036 #include "k3resolver.h"
0037 #include "k3socketdevice.h"
0038 
0039 using namespace KNetwork;
0040 
0041 class KNetwork::KStreamSocketPrivate
0042 {
0043 public:
0044     KResolverResults::ConstIterator local, peer;
0045     QElapsedTimer startTime;
0046     QTimer timer;
0047 
0048     int timeout;
0049 
0050     inline KStreamSocketPrivate()
0051         : timeout(0)
0052     { }
0053 };
0054 
0055 KStreamSocket::KStreamSocket(const QString &node, const QString &service,
0056                              QObject *parent)
0057     : KClientSocketBase(parent), d(new KStreamSocketPrivate)
0058 {
0059     peerResolver().setNodeName(node);
0060     peerResolver().setServiceName(service);
0061     peerResolver().setFamily(KResolver::KnownFamily);
0062     localResolver().setFamily(KResolver::KnownFamily);
0063 
0064     setSocketOptions(socketOptions() & ~Blocking);
0065 
0066     QObject::connect(&d->timer, SIGNAL(timeout()), this, SLOT(timeoutSlot()));
0067 }
0068 
0069 KStreamSocket::~KStreamSocket()
0070 {
0071     delete d;
0072     // KClientSocketBase's destructor closes the socket
0073 }
0074 
0075 int KStreamSocket::timeout() const
0076 {
0077     return d->timeout;
0078 }
0079 
0080 int KStreamSocket::remainingTimeout() const
0081 {
0082     if (state() != Connecting) {
0083         return timeout();
0084     }
0085     if (timeout() <= 0) {
0086         return 0;
0087     }
0088 
0089     return timeout() - d->startTime.elapsed();
0090 }
0091 
0092 void KStreamSocket::setTimeout(int msecs)
0093 {
0094     d->timeout = msecs;
0095 
0096     if (state() == Connecting) {
0097         d->timer.start(msecs);
0098     }
0099 }
0100 
0101 bool KStreamSocket::bind(const QString &node, const QString &service)
0102 {
0103     if (state() != Idle) {
0104         return false;
0105     }
0106 
0107     if (!node.isNull()) {
0108         localResolver().setNodeName(node);
0109     }
0110     if (!service.isNull()) {
0111         localResolver().setServiceName(service);
0112     }
0113     return true;
0114 }
0115 
0116 bool KStreamSocket::bind(const KResolverEntry &entry)
0117 {
0118     return KClientSocketBase::bind(entry);
0119 }
0120 
0121 bool KStreamSocket::connect(const QString &node, const QString &service,
0122                             OpenMode mode)
0123 {
0124     Q_UNUSED(mode);
0125     if (state() == Connected) {
0126         return true;    // already connected
0127     }
0128 
0129     if (state() > Connected) {
0130         return false;    // can't do much here
0131     }
0132 
0133     if (!node.isNull()) {
0134         peerResolver().setNodeName(node);
0135     }
0136     if (!service.isNull()) {
0137         peerResolver().setServiceName(service);
0138     }
0139 
0140     if (state() == Connecting && !blocking()) {
0141         setError(InProgress);
0142         emit gotError(InProgress);
0143         return true;      // we're already connecting
0144     }
0145 
0146     if (state() < HostFound) {
0147         // connection hasn't started yet
0148         if (!blocking()) {
0149             QObject::connect(this, SIGNAL(hostFound()), SLOT(hostFoundSlot()));
0150             return lookup();
0151         }
0152 
0153         // blocking mode
0154         if (!lookup()) {
0155             return false;    // lookup failure
0156         }
0157     }
0158 
0159     /*
0160      * lookup results are available here
0161      */
0162 
0163     if (timeout() > 0) {
0164         if (!blocking() && !d->timer.isActive()) {
0165             d->timer.setSingleShot(true);
0166             d->timer.start(timeout());
0167         } else {
0168             // blocking connection with timeout
0169             // this must be handled as a special case because it requires a
0170             // non-blocking socket
0171 
0172             d->timer.stop();  // no need for a timer here
0173 
0174             socketDevice()->setBlocking(false);
0175             while (true) {
0176                 connectionEvent();
0177                 if (state() < Connecting) {
0178                     return false;    // error connecting
0179                 }
0180                 if (state() == Connected) {
0181                     return true;    // connected!
0182                 }
0183 
0184                 if (remainingTimeout() <= 0) {
0185                     // we've timed out
0186                     timeoutSlot();
0187                     return false;
0188                 }
0189 
0190                 if (socketDevice()->error() == InProgress) {
0191                     bool timedout;
0192                     socketDevice()->poll(remainingTimeout(), &timedout);
0193                     if (timedout) {
0194                         timeoutSlot();
0195                         return false;
0196                     }
0197                 }
0198             }
0199         }
0200     }
0201 
0202     connectionEvent();
0203     return error() == NoError;
0204 }
0205 
0206 bool KStreamSocket::connect(const KResolverEntry &entry, OpenMode mode)
0207 {
0208     return KClientSocketBase::connect(entry, mode);
0209 }
0210 
0211 void KStreamSocket::hostFoundSlot()
0212 {
0213     QObject::disconnect(this, SLOT(hostFoundSlot()));
0214     if (timeout() > 0) {
0215         d->timer.setSingleShot(true);
0216         d->timer.start(timeout());
0217     }
0218     QTimer::singleShot(0, this, SLOT(connectionEvent()));
0219 }
0220 
0221 void KStreamSocket::connectionEvent()
0222 {
0223     if (state() != HostFound && state() != Connecting) {
0224         return;    // nothing to do
0225     }
0226 
0227     const KResolverResults &peer = peerResults();
0228     if (state() == HostFound) {
0229         d->startTime.start();
0230 
0231         setState(Connecting);
0232         emit stateChanged(Connecting);
0233         d->peer = peer.begin();
0234         d->local = localResults().begin(); // just to be on the safe side
0235     }
0236 
0237     while (d->peer != peer.end()) {
0238         const KResolverEntry &r = *d->peer;
0239 
0240         if (socketDevice()->socket() != -1) {
0241             // we have an existing file descriptor
0242             // this means that we've got activity in it (connection result)
0243             if (socketDevice()->connect(r) && socketDevice()->error() == NoError) {
0244                 // yes, it did connect!
0245                 connectionSucceeded(r);
0246                 return;
0247             } else if (socketDevice()->error() == InProgress)
0248                 // nope, still in progress
0249             {
0250                 return;
0251             }
0252 
0253             // no, the socket failed to connect
0254             copyError();
0255             socketDevice()->close();
0256             ++d->peer;
0257             continue;
0258         }
0259 
0260         // try to bind
0261         if (!bindLocallyFor(r)) {
0262             // could not find a matching family
0263             ++d->peer;
0264             continue;
0265         }
0266 
0267         {
0268             bool skip = false;
0269             emit aboutToConnect(r, skip);
0270             if (skip) {
0271                 ++d->peer;
0272                 continue;
0273             }
0274         }
0275 
0276         if (socketDevice()->connect(r) || socketDevice()->error() == InProgress) {
0277             // socket is attempting to connect
0278             if (socketDevice()->error() == InProgress) {
0279                 QSocketNotifier *n = socketDevice()->readNotifier();
0280                 QObject::connect(n, SIGNAL(activated(int)),
0281                                  this, SLOT(connectionEvent()));
0282                 n->setEnabled(true);
0283 
0284                 n = socketDevice()->writeNotifier();
0285                 QObject::connect(n, SIGNAL(activated(int)),
0286                                  this, SLOT(connectionEvent()));
0287                 n->setEnabled(true);
0288 
0289                 return;       // wait for activity
0290             }
0291 
0292             // socket has connected
0293             connectionSucceeded(r);
0294             return;
0295         }
0296 
0297         // connection failed
0298         // try next
0299         copyError();
0300         socketDevice()->close();
0301         ++d->peer;
0302     }
0303 
0304     // that was the last item
0305     socketDevice()->setSocketOptions(socketOptions());
0306     setState(Idle);
0307     emit stateChanged(Idle);
0308     emit gotError(error());
0309     return;
0310 }
0311 
0312 void KStreamSocket::timeoutSlot()
0313 {
0314     if (state() != Connecting) {
0315         return;
0316     }
0317 
0318     // halt the connections
0319     socketDevice()->close();  // this also kills the notifiers
0320 
0321     setError(Timeout);
0322     setState(HostFound);
0323     emit stateChanged(HostFound);
0324 
0325     QPointer<KStreamSocket> that = this;
0326     emit gotError(Timeout);
0327 
0328     if (!that.isNull()) {
0329         emit timedOut();
0330     }
0331 }
0332 
0333 bool KStreamSocket::bindLocallyFor(const KResolverEntry &peer)
0334 {
0335     const KResolverResults &local = localResults();
0336 
0337     if (local.isEmpty())
0338         // user doesn't want to bind to any specific local address
0339     {
0340         return true;
0341     }
0342 
0343     bool foundone = false;
0344     // scan the local resolution for a matching family
0345     for (d->local = local.begin(); d->local != local.end(); ++d->local)
0346         if ((*d->local).family() == peer.family()) {
0347             // found a suitable address!
0348             foundone = true;
0349 
0350             if (socketDevice()->bind(*d->local)) {
0351                 return true;
0352             }
0353         }
0354 
0355     if (!foundone) {
0356         // found nothing
0357         setError(NotSupported);
0358         emit gotError(NotSupported);
0359     } else {
0360         copyError();
0361     }
0362     return false;
0363 }
0364 
0365 void KStreamSocket::connectionSucceeded(const KResolverEntry &peer)
0366 {
0367     QObject::disconnect(socketDevice()->readNotifier(), nullptr, this, SLOT(connectionEvent()));
0368     QObject::disconnect(socketDevice()->writeNotifier(), nullptr, this, SLOT(connectionEvent()));
0369 
0370     resetError();
0371     KActiveSocketBase::open(ReadWrite | Unbuffered);
0372     setState(Connected);
0373     socketDevice()->setSocketOptions(socketOptions());
0374     d->timer.stop();
0375     emit stateChanged(Connected);
0376 
0377     if (!localResults().isEmpty()) {
0378         emit bound(*d->local);
0379     }
0380     emit connected(peer);
0381 }
0382 
0383 #include "moc_k3streamsocket.cpp"