File indexing completed on 2023-09-24 04:04:41
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"