1 /****************************************************************************
2 **
3 ** Copyright (C) 2015 The Qt Company Ltd.
4 ** Contact: http://www.qt.io/licensing/
5 **
6 ** This file is part of the QtNetwork module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see http://www.qt.io/terms-conditions. For further
15 ** information use the contact form at http://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 or version 3 as published by the Free
20 ** Software Foundation and appearing in the file LICENSE.LGPLv21 and
21 ** LICENSE.LGPLv3 included in the packaging of this file. Please review the
22 ** following information to ensure the GNU Lesser General Public License
23 ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
24 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 **
26 ** As a special exception, The Qt Company gives you certain additional
27 ** rights. These rights are described in The Qt Company LGPL Exception
28 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 **
30 ** GNU General Public License Usage
31 ** Alternatively, this file may be used under the terms of the GNU
32 ** General Public License version 3.0 as published by the Free Software
33 ** Foundation and appearing in the file LICENSE.GPL included in the
34 ** packaging of this file.  Please review the following information to
35 ** ensure the GNU General Public License version 3.0 requirements will be
36 ** met: http://www.gnu.org/copyleft/gpl.html.
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41 
42 //#define QHOSTINFO_DEBUG
43 
44 // Qt Headers
45 #include <QByteArray>
46 #include <QUrl>
47 #include <QList>
48 
49 #include "qplatformdefs.h"
50 
51 #include "qhostinfo_p.h"
52 #include <private/qcore_symbian_p.h>
53 #include <private/qsystemerror_p.h>
54 #include <private/qnetworksession_p.h>
55 #include <private/qhostaddress_p.h>
56 
57 // Header does not exist in the S60 5.0 SDK
58 //#include <networking/dnd_err.h>
59 const TInt KErrDndNameNotFound = -5120; // Returned when no data found for GetByName
60 const TInt KErrDndAddrNotFound = -5121; // Returned when no data found for GetByAddr
61 
62 QT_BEGIN_NAMESPACE
63 
setError_helper(QHostInfo & info,TInt symbianError)64 static void setError_helper(QHostInfo &info, TInt symbianError)
65 {
66     switch (symbianError) {
67     case KErrDndNameNotFound:
68     case KErrDndAddrNotFound:
69     case KErrNotFound:
70     case KErrEof:
71         // various "no more results" error codes
72         info.setError(QHostInfo::HostNotFound);
73         info.setErrorString(QObject::tr("Host not found"));
74         break;
75     default:
76         // Unknown error
77         info.setError(QHostInfo::UnknownError);
78         info.setErrorString(QSystemError(symbianError, QSystemError::NativeError).toString());
79         break;
80     }
81 }
82 
fromName(const QString & hostName,QSharedPointer<QNetworkSession> networkSession)83 QHostInfo QHostInfoAgent::fromName(const QString &hostName, QSharedPointer<QNetworkSession> networkSession)
84 {
85     QHostInfo results;
86 
87     // Connect to ESOCK
88     RSocketServ socketServ(qt_symbianGetSocketServer());
89     RHostResolver hostResolver;
90 
91 
92     int err;
93     if (networkSession)
94         err = QNetworkSessionPrivate::nativeOpenHostResolver(*networkSession, hostResolver, KAfInet, KProtocolInetUdp);
95     else
96         err = hostResolver.Open(socketServ, KAfInet, KProtocolInetUdp);
97     if (err) {
98         setError_helper(results, err);
99         return results;
100     }
101 
102     TNameEntry nameResult;
103 
104 #if defined(QHOSTINFO_DEBUG)
105     qDebug("QHostInfoAgent::fromName(%s) looking up...",
106            hostName.toLatin1().constData());
107 #endif
108 
109     QHostAddress address;
110     if (address.setAddress(hostName)) {
111         // Reverse lookup
112 #if defined(QHOSTINFO_DEBUG)
113         qDebug("(reverse lookup)");
114 #endif
115         TInetAddr IpAdd;
116         IpAdd.Input(qt_QString2TPtrC(hostName));
117 
118         // Synchronous request. nameResult returns Host Name.
119         err = hostResolver.GetByAddress(IpAdd, nameResult);
120         if (err) {
121             //for behavioural compatibility with Qt 4.7 and unix/windows
122             //backends: don't report error, return ip address as host name
123             results.setHostName(address.toString());
124         } else {
125             results.setHostName(qt_TDesC2QString(nameResult().iName));
126         }
127         results.setAddresses(QList<QHostAddress>() << address);
128         return results;
129     }
130 
131     // IDN support
132     QByteArray aceHostname = QUrl::toAce(hostName);
133     results.setHostName(hostName);
134     if (aceHostname.isEmpty()) {
135         results.setError(QHostInfo::HostNotFound);
136         results.setErrorString(hostName.isEmpty() ?
137                                QCoreApplication::translate("QHostInfoAgent", "No host name given") :
138                                QCoreApplication::translate("QHostInfoAgent", "Invalid hostname"));
139         return results;
140     }
141 
142 
143     // Call RHostResolver::GetByAddress, and place all IPv4 addresses at the start and
144     // the IPv6 addresses at the end of the address list in results.
145 
146     // Synchronous request.
147     err = hostResolver.GetByName(qt_QString2TPtrC(QString::fromLatin1(aceHostname)), nameResult);
148     if (err) {
149         setError_helper(results, err);
150         return results;
151     }
152 
153     QList<QHostAddress> hostAddresses;
154 
155     TInetAddr hostAdd = nameResult().iAddr;
156 
157     if (!(nameResult().iFlags & TNameRecord::EAlias) && !(hostAdd.IsUnspecified()))
158         hostAddresses.append(qt_QHostAddressFromTInetAddr(hostAdd));
159 
160     // Check if there's more than one IP address linkd to this name
161     while (hostResolver.Next(nameResult) == KErrNone) {
162         hostAdd = nameResult().iAddr;
163 
164         // Ensure that record is valid (not an alias and with length greater than 0)
165         if (!(nameResult().iFlags & TNameRecord::EAlias) && !(hostAdd.IsUnspecified()))
166             hostAddresses.append(qt_QHostAddressFromTInetAddr(hostAdd));
167     }
168 
169     hostResolver.Close();
170 
171     results.setAddresses(hostAddresses);
172     return results;
173 }
174 
fromName(const QString & hostName)175 QHostInfo QHostInfoAgent::fromName(const QString &hostName)
176 {
177     // null shared pointer
178     QSharedPointer<QNetworkSession> networkSession;
179     return fromName(hostName, networkSession);
180 }
181 
localHostName()182 QString QHostInfo::localHostName()
183 {
184     // Connect to ESOCK
185     RSocketServ socketServ(qt_symbianGetSocketServer());
186     RHostResolver hostResolver;
187 
188     // RConnection not required to get the host name
189     int err = hostResolver.Open(socketServ, KAfInet, KProtocolInetUdp);
190     if (err)
191         return QString();
192 
193     THostName hostName;
194     err = hostResolver.GetHostName(hostName);
195     if (err)
196         return QString();
197 
198     hostResolver.Close();
199 
200     return qt_TDesC2QString(hostName);
201 }
202 
localDomainName()203 QString QHostInfo::localDomainName()
204 {
205     // This concept does not exist on Symbian OS because the device can be on
206     // multiple networks with multiple "local domain" names.
207     // For now, return a null string.
208     return QString();
209 }
210 
211 
QSymbianHostResolver(const QString & hostName,int identifier,QSharedPointer<QNetworkSession> networkSession)212 QSymbianHostResolver::QSymbianHostResolver(const QString &hostName, int identifier, QSharedPointer<QNetworkSession> networkSession)
213     : CActive(CActive::EPriorityStandard), iHostName(hostName),
214       iSocketServ(qt_symbianGetSocketServer()), iNetworkSession(networkSession), iResults(identifier)
215 {
216     CActiveScheduler::Add(this);
217 }
218 
~QSymbianHostResolver()219 QSymbianHostResolver::~QSymbianHostResolver()
220 {
221 #if defined(QHOSTINFO_DEBUG)
222     qDebug() << "QSymbianHostInfoLookupManager::~QSymbianHostResolver" << id();
223 #endif
224     Cancel();
225     iHostResolver.Close();
226 }
227 
228 // Async equivalent to QHostInfoAgent::fromName()
requestHostLookup()229 void QSymbianHostResolver::requestHostLookup()
230 {
231 
232 #if defined(QHOSTINFO_DEBUG)
233     qDebug("QSymbianHostResolver::requestHostLookup(%s) looking up... (id = %d)",
234         iHostName.toLatin1().constData(), id());
235 #endif
236 
237     QSymbianHostInfoLookupManager *manager = QSymbianHostInfoLookupManager::globalInstance();
238     if (manager->cache.isEnabled()) {
239         //check if name has been put in the cache while this request was queued
240         bool valid;
241         QHostInfo cachedResult = manager->cache.get(iHostName, &valid);
242         if (valid) {
243 #if defined(QHOSTINFO_DEBUG)
244             qDebug("...found in cache");
245 #endif
246             iResults = cachedResult;
247             iState = ECompleteFromCache;
248             SetActive();
249             TRequestStatus* stat = &iStatus;
250             User::RequestComplete(stat, KErrNone);
251             return;
252         }
253     }
254 
255     int err;
256     if (iNetworkSession) {
257         err = QNetworkSessionPrivate::nativeOpenHostResolver(*iNetworkSession, iHostResolver, KAfInet, KProtocolInetUdp);
258 #if defined(QHOSTINFO_DEBUG)
259         qDebug("using resolver from session (err = %d)", err);
260 #endif
261     } else {
262         err = iHostResolver.Open(iSocketServ, KAfInet, KProtocolInetUdp);
263 #if defined(QHOSTINFO_DEBUG)
264         qDebug("using default resolver (err = %d)", err);
265 #endif
266     }
267     if (err) {
268         setError_helper(iResults, err);
269     } else {
270 
271         if (iAddress.setAddress(iHostName)) {
272             // Reverse lookup
273             IpAdd.Input(qt_QString2TPtrC(iHostName));
274 
275             // Asynchronous request.
276             iHostResolver.GetByAddress(IpAdd, iNameResult, iStatus); // <---- ASYNC
277             iState = EGetByAddress;
278 
279         } else {
280 
281             // IDN support
282             QByteArray aceHostname = QUrl::toAce(iHostName);
283             iResults.setHostName(iHostName);
284             if (aceHostname.isEmpty()) {
285                 iResults.setError(QHostInfo::HostNotFound);
286                 iResults.setErrorString(iHostName.isEmpty() ?
287                                        QCoreApplication::translate("QHostInfoAgent", "No host name given") :
288                                        QCoreApplication::translate("QHostInfoAgent", "Invalid hostname"));
289 
290                 err = KErrArgument;
291             } else {
292                 iEncodedHostName = QString::fromLatin1(aceHostname);
293                 iHostNamePtr.Set(qt_QString2TPtrC(iEncodedHostName));
294 
295                 // Asynchronous request.
296                 iHostResolver.GetByName(iHostNamePtr, iNameResult, iStatus);
297                 iState = EGetByName;
298             }
299         }
300     }
301     SetActive();
302     if (err) {
303         iHostResolver.Close();
304 
305         //self complete so that RunL can inform manager without causing recursion
306         iState = EError;
307         TRequestStatus* stat = &iStatus;
308         User::RequestComplete(stat, err);
309     }
310 }
311 
abortHostLookup()312 void QSymbianHostResolver::abortHostLookup()
313 {
314     if (resultEmitter.thread() == QThread::currentThread()) {
315 #ifdef QHOSTINFO_DEBUG
316         qDebug("QSymbianHostResolver::abortHostLookup - deleting %d", id());
317 #endif
318         //normal case, abort from same thread it was started
319         delete this; //will cancel outstanding request
320     } else {
321 #ifdef QHOSTINFO_DEBUG
322         qDebug("QSymbianHostResolver::abortHostLookup - detaching %d", id());
323 #endif
324         //abort from different thread, carry on but don't report the results
325         resultEmitter.disconnect();
326     }
327 }
328 
DoCancel()329 void QSymbianHostResolver::DoCancel()
330 {
331 #if defined(QHOSTINFO_DEBUG)
332     qDebug() << "QSymbianHostResolver::DoCancel" << QThread::currentThreadId() << id() << (int)iState << this;
333 #endif
334     if (iState == EGetByAddress || iState == EGetByName) {
335         //these states have made an async request to host resolver
336         iHostResolver.Cancel();
337     } else {
338         //for the self completing states there is nothing to cancel
339         Q_ASSERT(iState == EError || iState == ECompleteFromCache);
340     }
341 }
342 
RunL()343 void QSymbianHostResolver::RunL()
344 {
345     QT_TRYCATCH_LEAVING(run());
346 }
347 
run()348 void QSymbianHostResolver::run()
349 {
350     switch (iState) {
351     case EGetByName:
352         processNameResult();
353         break;
354     case EGetByAddress:
355         processAddressResult();
356         break;
357     case ECompleteFromCache:
358     case EError:
359         returnResults();
360         break;
361     default:
362         qWarning("QSymbianHostResolver internal error, bad state in run()");
363         iResults.setError(QHostInfo::UnknownError);
364         iResults.setErrorString(QSystemError(KErrCorrupt,QSystemError::NativeError).toString());
365         returnResults();
366     }
367 }
368 
returnResults()369 void QSymbianHostResolver::returnResults()
370 {
371 #if defined(QHOSTINFO_DEBUG)
372     qDebug() << "QSymbianHostResolver::returnResults" << iResults.error() << iResults.errorString();
373     foreach (QHostAddress addr, iResults.addresses())
374         qDebug() << addr;
375 #endif
376     iState = EIdle;
377 
378     QSymbianHostInfoLookupManager *manager = QSymbianHostInfoLookupManager::globalInstance();
379     if (manager->cache.isEnabled()) {
380         manager->cache.put(iHostName, iResults);
381     }
382     manager->lookupFinished(this);
383 
384     resultEmitter.emitResultsReady(iResults);
385 
386     delete this;
387 }
388 
RunError(TInt aError)389 TInt QSymbianHostResolver::RunError(TInt aError)
390 {
391     QT_TRY {
392         iState = EIdle;
393 
394         QSymbianHostInfoLookupManager *manager = QSymbianHostInfoLookupManager::globalInstance();
395         manager->lookupFinished(this);
396 
397         setError_helper(iResults, aError);
398 
399         resultEmitter.emitResultsReady(iResults);
400     }
401     QT_CATCH(...) {}
402 
403     delete this;
404 
405     return KErrNone;
406 }
407 
processNameResult()408 void QSymbianHostResolver::processNameResult()
409 {
410     if (iStatus.Int() == KErrNone) {
411         TInetAddr hostAdd = iNameResult().iAddr;
412 
413         // Ensure that record is valid (not an alias and with length greater than 0)
414         if (!(iNameResult().iFlags & TNameRecord::EAlias) && !(hostAdd.IsUnspecified())) {
415             iHostAddresses.append(qt_QHostAddressFromTInetAddr(hostAdd));
416         }
417 
418         iState = EGetByName;
419         iHostResolver.Next(iNameResult, iStatus);
420         SetActive();
421     }
422     else {
423         // No more addresses, so return the results (or an error if there aren't any).
424 #if defined(QHOSTINFO_DEBUG)
425         qDebug() << "QSymbianHostResolver::processNameResult with err=" << iStatus.Int() << "count=" << iHostAddresses.count();
426 #endif
427         if (iHostAddresses.count() > 0) {
428             iResults.setAddresses(iHostAddresses);
429         } else {
430             iState = EError;
431             setError_helper(iResults, iStatus.Int());
432         }
433         returnResults();
434     }
435 }
436 
processAddressResult()437 void QSymbianHostResolver::processAddressResult()
438 {
439     TInt err = iStatus.Int();
440 
441     if (err < 0) {
442         //For behavioural compatibility with Qt 4.7, don't report errors on reverse lookup,
443         //return the address as a string (same as unix/windows backends)
444         iResults.setHostName(iAddress.toString());
445     } else {
446         iResults.setHostName(qt_TDesC2QString(iNameResult().iName));
447     }
448     iResults.setAddresses(QList<QHostAddress>() << iAddress);
449     returnResults();
450 }
451 
452 
id()453 int QSymbianHostResolver::id()
454 {
455     return iResults.lookupId();
456 }
457 
QSymbianHostInfoLookupManager()458 QSymbianHostInfoLookupManager::QSymbianHostInfoLookupManager()
459 {
460 }
461 
~QSymbianHostInfoLookupManager()462 QSymbianHostInfoLookupManager::~QSymbianHostInfoLookupManager()
463 {
464 }
465 
clear()466 void QSymbianHostInfoLookupManager::clear()
467 {
468     QMutexLocker locker(&mutex);
469 #if defined(QHOSTINFO_DEBUG)
470     qDebug() << "QSymbianHostInfoLookupManager::clear" << QThread::currentThreadId();
471 #endif
472     foreach (QSymbianHostResolver *hr, iCurrentLookups)
473         hr->abortHostLookup();
474     iCurrentLookups.clear();
475     qDeleteAll(iScheduledLookups);
476     cache.clear();
477 }
478 
lookupFinished(QSymbianHostResolver * r)479 void QSymbianHostInfoLookupManager::lookupFinished(QSymbianHostResolver *r)
480 {
481     QMutexLocker locker(&mutex);
482 
483 #if defined(QHOSTINFO_DEBUG)
484     qDebug() << "QSymbianHostInfoLookupManager::lookupFinished" << QThread::currentThreadId() << r->id() << "current" << iCurrentLookups.count() << "queued" << iScheduledLookups.count();
485 #endif
486     // remove finished lookup from array and destroy
487     TInt count = iCurrentLookups.count();
488     for (TInt i = 0; i < count; i++) {
489         if (iCurrentLookups[i]->id() == r->id()) {
490             iCurrentLookups.removeAt(i);
491             break;
492         }
493     }
494 
495     runNextLookup();
496 }
497 
runNextLookup()498 void QSymbianHostInfoLookupManager::runNextLookup()
499 {
500 #if defined(QHOSTINFO_DEBUG)
501     qDebug() << "QSymbianHostInfoLookupManager::runNextLookup" << QThread::currentThreadId() << "current" << iCurrentLookups.count() << "queued" << iScheduledLookups.count();
502 #endif
503     // check to see if there are any scheduled lookups
504     for (int i=0; i<iScheduledLookups.count(); i++) {
505         QSymbianHostResolver* hostResolver = iScheduledLookups.at(i);
506         if (hostResolver->resultEmitter.thread() == QThread::currentThread()) {
507             // if so, move one to the current lookups and run it
508             iCurrentLookups.append(hostResolver);
509             iScheduledLookups.removeAt(i);
510             hostResolver->requestHostLookup();
511             // if spare capacity, try to start another one
512             if (iCurrentLookups.count() >= KMaxConcurrentLookups)
513                 break;
514             i--; //compensate for removeAt
515         }
516     }
517 }
518 
519 // called from QHostInfo
scheduleLookup(QSymbianHostResolver * r)520 void QSymbianHostInfoLookupManager::scheduleLookup(QSymbianHostResolver* r)
521 {
522     QMutexLocker locker(&mutex);
523 
524 #if defined(QHOSTINFO_DEBUG)
525     qDebug() << "QSymbianHostInfoLookupManager::scheduleLookup" << QThread::currentThreadId() << r->id() << "current" << iCurrentLookups.count() << "queued" << iScheduledLookups.count();
526 #endif
527     // Check to see if we have space on the current lookups pool.
528     bool defer = false;
529     if (iCurrentLookups.count() >= KMaxConcurrentLookups) {
530         // busy, defer unless there are no request in this thread
531         // at least one active request per thread with queued requests is needed
532         for (int i=0; i < iCurrentLookups.count();i++) {
533             if (iCurrentLookups.at(i)->resultEmitter.thread() == QThread::currentThread()) {
534                 defer = true;
535                 break;
536             }
537         }
538     }
539     if (defer) {
540         // If no, schedule for later.
541         iScheduledLookups.append(r);
542 #if defined(QHOSTINFO_DEBUG)
543     qDebug(" - scheduled");
544 #endif
545         return;
546     } else {
547         // If yes, add it to the current lookups.
548         iCurrentLookups.append(r);
549 
550         // ... and trigger the async call.
551         r->requestHostLookup();
552     }
553 }
554 
abortLookup(int id)555 void QSymbianHostInfoLookupManager::abortLookup(int id)
556 {
557     QMutexLocker locker(&mutex);
558 
559 #if defined(QHOSTINFO_DEBUG)
560     qDebug() << "QSymbianHostInfoLookupManager::abortLookup" << QThread::currentThreadId() << id << "current" << iCurrentLookups.count() << "queued" << iScheduledLookups.count();
561 #endif
562     int i = 0;
563     // Find the aborted lookup by ID.
564     // First in the current lookups.
565     for (i = 0; i < iCurrentLookups.count(); i++) {
566         if (id == iCurrentLookups[i]->id()) {
567             QSymbianHostResolver* r = iCurrentLookups.at(i);
568             iCurrentLookups.removeAt(i);
569             r->abortHostLookup();
570             runNextLookup();
571             return;
572         }
573     }
574     // Then in the scheduled lookups.
575     for (i = 0; i < iScheduledLookups.count(); i++) {
576         if (id == iScheduledLookups[i]->id()) {
577             QSymbianHostResolver* r = iScheduledLookups.at(i);
578             iScheduledLookups.removeAt(i);
579             delete r;
580             return;
581         }
582     }
583 }
584 
globalInstance()585 QSymbianHostInfoLookupManager* QSymbianHostInfoLookupManager::globalInstance()
586 {
587     return static_cast<QSymbianHostInfoLookupManager*>
588             (QAbstractHostInfoLookupManager::globalInstance());
589 }
590 
591 QT_END_NAMESPACE
592