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