|
1 // Copyright (c) 2004-2009 Nokia Corporation and/or its subsidiary(-ies). |
|
2 // All rights reserved. |
|
3 // This component and the accompanying materials are made available |
|
4 // under the terms of "Eclipse Public License v1.0" |
|
5 // which accompanies this distribution, and is available |
|
6 // at the URL "http://www.eclipse.org/legal/epl-v10.html". |
|
7 // |
|
8 // Initial Contributors: |
|
9 // Nokia Corporation - initial contribution. |
|
10 // |
|
11 // Contributors: |
|
12 // |
|
13 // Description: |
|
14 // resolver.cpp - name resolver |
|
15 // |
|
16 |
|
17 #include <in_sock.h> |
|
18 #include <timeout.h> |
|
19 #include "listener.h" |
|
20 #include "resolver.h" |
|
21 #include "engine.h" |
|
22 #include "hosts.h" |
|
23 #include <networking/dnd_err.h> |
|
24 #include "dns_ext.h" |
|
25 #include "inet6log.h" |
|
26 |
|
27 #ifdef DND_DCM_EXTENSION |
|
28 #include "dnd.hrh" // EDndFlush |
|
29 #endif |
|
30 |
|
31 #ifdef EXCLUDE_SYMBIAN_DNS_PUNYCODE |
|
32 #undef SYMBIAN_DNS_PUNYCODE |
|
33 #endif //EXCLUDE_SYMBIAN_DNS_PUNYCODE |
|
34 |
|
35 struct SQueryStep |
|
36 { |
|
37 EDnsQType iQType; // the query type |
|
38 }; |
|
39 |
|
40 struct SQueryExtension |
|
41 { |
|
42 const TDesC *iPrefix; |
|
43 SQueryStep iQuery; |
|
44 }; |
|
45 |
|
46 // ...for GetByAddr, perform only PTR query (for each server) |
|
47 const SQueryStep KGetByAddress[1] = |
|
48 { |
|
49 { EDnsQType_PTR } |
|
50 }; |
|
51 |
|
52 // ...for GetByName, try first AAAA and then A (for each server) |
|
53 const SQueryStep KGetByName1[2] = |
|
54 { |
|
55 { EDnsQType_AAAA }, |
|
56 { EDnsQType_A } |
|
57 }; |
|
58 |
|
59 // ...for GetByName, try first A and then AAAA (for each server) |
|
60 const SQueryStep KGetByName2[2] = |
|
61 { |
|
62 { EDnsQType_A }, |
|
63 { EDnsQType_AAAA } |
|
64 }; |
|
65 |
|
66 // Define the search order of the sources |
|
67 const TDnsServerScope KSourceOrder[] = |
|
68 { |
|
69 EDnsServerScope_HOSTFILE, // 1. Search hosts file first |
|
70 EDnsServerScope_UC_GLOBAL, // 2. Search global DNS |
|
71 #ifdef LLMNR_ENABLED |
|
72 EDnsServerScope_MC_LOCAL, // 3. Try Link Local Multicast |
|
73 #endif |
|
74 }; |
|
75 |
|
76 const SQueryExtension KQueryExtension[] = |
|
77 { |
|
78 #if 0 |
|
79 { &KDnsExtQType_A, EDnsQType_A }, |
|
80 { &KDnsExtQType_AAAA, EDnsQType_AAAA }, |
|
81 { &KDnsExtQType_MX, EDnsQType_MX }, |
|
82 { &KDnsExtQType_NS, EDnsQType_NS }, |
|
83 { &KDnsExtQType_SOA, EDnsQType_SOA }, |
|
84 { &KDnsExtQType_CNAME, EDnsQType_CNAME }, |
|
85 { &KDnsExtQType_PTR, EDnsQType_PTR }, |
|
86 { &KDnsExtQType_SRV, EDnsQType_SRV }, |
|
87 { &KDnsExtQType_NAPTR, EDnsQType_NAPTR }, |
|
88 { &KDnsExtQType_ANY, EDnsQType_ANY } |
|
89 #else |
|
90 // Workaround for ARMV5 compiler bug? Above declaration always |
|
91 // produces 80 bytes of writable global data. The following |
|
92 // ugly definition does not. |
|
93 { (const TDesC *)&KDnsExtQType_A.iTypeLength, EDnsQType_A }, |
|
94 { (const TDesC *)&KDnsExtQType_AAAA.iTypeLength, EDnsQType_AAAA }, |
|
95 { (const TDesC *)&KDnsExtQType_MX.iTypeLength, EDnsQType_MX }, |
|
96 { (const TDesC *)&KDnsExtQType_NS.iTypeLength, EDnsQType_NS }, |
|
97 { (const TDesC *)&KDnsExtQType_SOA.iTypeLength, EDnsQType_SOA }, |
|
98 { (const TDesC *)&KDnsExtQType_CNAME.iTypeLength, EDnsQType_CNAME }, |
|
99 { (const TDesC *)&KDnsExtQType_PTR.iTypeLength, EDnsQType_PTR }, |
|
100 { (const TDesC *)&KDnsExtQType_SRV.iTypeLength, EDnsQType_SRV }, |
|
101 { (const TDesC *)&KDnsExtQType_NAPTR.iTypeLength, EDnsQType_NAPTR }, |
|
102 { (const TDesC *)&KDnsExtQType_ANY.iTypeLength, EDnsQType_ANY } |
|
103 #endif |
|
104 }; |
|
105 |
|
106 // KMaxQuerySessions |
|
107 // ***************** |
|
108 /** |
|
109 // The maximum number of query sessions for single resolver, |
|
110 // for example, supporting A and AAAA query, requires this |
|
111 // to be at least 2. |
|
112 // (if someone ever adds A6 to the list, it needs to be 3). |
|
113 */ |
|
114 const TUint KMaxQuerySessions = 2; |
|
115 |
|
116 // KMaxDeprecated |
|
117 // ************** |
|
118 /** |
|
119 // The size of the deprecated answers buffer. When processing |
|
120 // the replies for GetByName, the resolver will first return |
|
121 // those A and AAAA answers, for which there is an existing |
|
122 // route and source address in the current system. Other answers |
|
123 // are put into deprecated buffer, and returned after the |
|
124 // "good" answers. |
|
125 */ |
|
126 const TUint KMaxDeprecated = 20; |
|
127 |
|
128 class CDndResolver; |
|
129 |
|
130 /** |
|
131 // The resolver state automaton for one DNS query. |
|
132 // |
|
133 // This executes the state automaton for a single DNS query |
|
134 // (for example A, AAAA, MX, etc.). The processing includes |
|
135 // automatic retransmissions (potentially to multiple DNS |
|
136 // servers) and timeout handling |
|
137 // |
|
138 // The query process is started by Start() and it will |
|
139 // then proceed independently to the conclusion. |
|
140 // When the query has completed or failed, |
|
141 // CDndResolver::GetNextAnswer() is called. |
|
142 // |
|
143 // In some cases an error is considered to be non-recoverable, |
|
144 // and instead of calling GetNextAnswer(), CDndResolver::QueryDone(error) |
|
145 // is called instead. This cancel the main query processing and |
|
146 // return the indicated error directly to the client process |
|
147 // (RHostResolver). |
|
148 // |
|
149 */ |
|
150 class TQuerySession: public MDnsResolver |
|
151 { |
|
152 friend class TQuerySessionTimeoutLinkage; |
|
153 public: |
|
154 // Default constructor |
|
155 TQuerySession(); |
|
156 // Supplement default constructor |
|
157 void Init(CDndResolver *const aResolver); |
|
158 // Open a new session to the DNS handler |
|
159 void Open(); |
|
160 // Close the session with the DNS handler |
|
161 void Close(); |
|
162 // Start a DNS query on the session |
|
163 TBool Start(EDnsQType aQType); |
|
164 |
|
165 MDnsSession *iSession; //< Session handle with DndDnsclient |
|
166 TInt iStatus; //< Session status / next record to retrieve (if >= 0) |
|
167 EDnsQType iQType; //< the query type |
|
168 private: |
|
169 static inline TInt Max(const TInt a, const TInt b) { return a > b ? a : b;} |
|
170 |
|
171 // A callback method when the timeout expires |
|
172 void Timeout(const TTime &aNow); |
|
173 // Request a call to Timeout after specified time (seconds) |
|
174 void SetTimer(TUint aTimeout); |
|
175 // A callback method when the request to DNS handler completes |
|
176 void ReplyCallback(const TInt aErr); |
|
177 |
|
178 // Cancel pending operations |
|
179 void Cancel(); |
|
180 // Activate a query in the DNS handler |
|
181 void SendDnsQuery(); |
|
182 |
|
183 // Internal, handles the common part of timeout and reply callbacks |
|
184 void Event(TBool aTimeout); |
|
185 |
|
186 CDndResolver *iResolver; //< The back pointer to resolver |
|
187 |
|
188 TUint8 iQueryRetry; //< Current retry index (for the current step) |
|
189 TUint8 iQueryTimeouts; //< Number of true timeouts (used by some resolver modes) |
|
190 TUint8 iServerCount; //< used in "spray mode" to count the servers. |
|
191 TUint iWaitingQuerySent:1; //< used in detecting and timing out interface startup. |
|
192 TUint iWaitingAfterSpray:1; //< used only in "spray mode" |
|
193 TUint iSendingQuery:1; //< set to 1, when DoQueryL is being called (recursion safeguard in SendDnsQuery) |
|
194 TUint iPendingQuery:1; //< set to 1, when DoQueryL is required (recursion safeguard in SendDnsQuery) |
|
195 TUint iQueryTimeoutValue; //< The current timeout for waiting the reply AFTER packet is sent |
|
196 public: // Needed for gcc! |
|
197 RTimeout iTimeout; |
|
198 #ifdef _LOG |
|
199 TBuf<2> iName; |
|
200 #endif |
|
201 }; |
|
202 |
|
203 // This ungainly manoevure is forced on us because the offset is not evaluated early enough by GCC3.4 to be |
|
204 // passed as a template parameter |
|
205 #if defined(__X86GCC__) || defined(__GCCE__) |
|
206 #define KQuerySessionTimeoutLinkageOffset 28 |
|
207 __ASSERT_COMPILE(KQuerySessionTimeoutLinkageOffset == _FOFF(TQuerySession, iTimeout)); |
|
208 #else |
|
209 #define KQuerySessionTimeoutLinkageOffset _FOFF(TQuerySession, iTimeout) |
|
210 #endif |
|
211 |
|
212 class TQuerySessionTimeoutLinkage : public TimeoutLinkage<TQuerySession, KQuerySessionTimeoutLinkageOffset> |
|
213 { |
|
214 public: |
|
215 static void Timeout(RTimeout &aLink, const TTime &aNow, TAny * /*aPtr*/) |
|
216 { |
|
217 Object(aLink)->Timeout(aNow); |
|
218 } |
|
219 }; |
|
220 |
|
221 /** |
|
222 // A structure to remember an answer to be retrieved later. |
|
223 */ |
|
224 class TDeprecatedAnswer |
|
225 { |
|
226 public: |
|
227 inline TDeprecatedAnswer() {} |
|
228 inline TDeprecatedAnswer(TUint8 aStep, TUint8 aIndex) : iStep(aStep), iIndex(aIndex) {} |
|
229 |
|
230 TUint8 iStep; //< query step # (selects session) |
|
231 TUint8 iIndex; //< answer index within the query reply |
|
232 }; |
|
233 |
|
234 /** |
|
235 The real implementation of the MDndResolver. |
|
236 |
|
237 Function: |
|
238 |
|
239 @li Accepts the request (see TDnsRequestBase) from the application for name resolving. |
|
240 @li Tries to resolve the request from host files or DNS sources |
|
241 @li Once the request is over (response obtained or time out), |
|
242 it sends back the response (again TDnsRequestBase) and/or error code |
|
243 to the application which requested it. |
|
244 |
|
245 See also \ref resolvers |
|
246 |
|
247 This class is designed so that it can be reused for serving |
|
248 different clients. It has the following simplified state diagram: |
|
249 |
|
250 @verbatim |
|
251 _________________________ |
|
252 / reuse \ |
|
253 V | |
|
254 construct -> Start() ---> WaitQuery() --> Stop() --> destruct |
|
255 / \ ^ |
|
256 | | | |
|
257 \ / / |
|
258 QueryDone() ___/ |
|
259 |
|
260 @endverbatim |
|
261 @li Start() |
|
262 serving a specific RHostResolver session. This method is called |
|
263 from CDndListener, after it has received a new query request |
|
264 from the stack. |
|
265 @li WaitQuery() |
|
266 the resolver is waiting for next incoming request |
|
267 from the RHostResolver. |
|
268 @li QueryDone() |
|
269 queue a result to be returned to the RHostResolver. |
|
270 @li Stop() |
|
271 serving the current rquest. This may be called either |
|
272 internally or from CDndListener. |
|
273 */ |
|
274 class CDndResolver : public CBase, public MDndResolver, public MDnsResolver |
|
275 { |
|
276 friend class TQuerySession; |
|
277 public: |
|
278 CDndResolver(const TInt aId, CDndEngine &aControl, MDndListener &aListener, |
|
279 MDnsSource &aDnsclient); |
|
280 |
|
281 void Start(const TDnsMessageBuf &aMsg); // Starts the resolver |
|
282 const TDesC8 &ReplyMessage(); // Return reference to the reply buffer. |
|
283 TUint16 Session() const; // Return session id of the current query. |
|
284 void Stop(); // Stop the resolver |
|
285 virtual ~CDndResolver(); |
|
286 |
|
287 static inline TInt Max(const TInt a, const TInt b) { return a > b ? a : b;} |
|
288 |
|
289 const TTime &RequestTime() const { return iRequestTime; } |
|
290 // only used for GetHostName |
|
291 void ReplyCallback(const TInt aResult); |
|
292 |
|
293 private: |
|
294 // Return a result to the application |
|
295 void QueryDone(TInt aResult); |
|
296 // Wait for the next query from the application |
|
297 void WaitQuery(); |
|
298 // Select next source for the name resolving (HOSTS, DNS, LLMNR) |
|
299 void NextSource(); |
|
300 // Cancel all activity and resources being used for the current source |
|
301 void StopSource(); |
|
302 TBool StartSource(); |
|
303 // Return next result from HOSTS file source |
|
304 void HOSTFILE_Next(); |
|
305 |
|
306 #ifdef _LOG |
|
307 // Print out debugging information |
|
308 void ShowQuery(const TDnsMessage &aName, TInt aReply); |
|
309 #endif |
|
310 |
|
311 // Retrieve next answer, possibly starting retrieval of additional information from source |
|
312 void GetNextAnswer(); |
|
313 // Used to find alternate DNS servers (in some operating modes) |
|
314 void ProbeServers(); |
|
315 private: |
|
316 const TInt iId; //< Resolver instance id (for debugging use) |
|
317 TTime iRequestTime; //< Time at which the request was received from the application |
|
318 |
|
319 // The Query buffer for the reply |
|
320 TDnsMessageBuf iBuffer; |
|
321 // The current orignal query being processed. |
|
322 TDnsMessageBuf iCurrentQuery; |
|
323 |
|
324 const TDnsServerScope *iSourceList; //< Information sources |
|
325 TDnsServerScope iSourceNow; //< The current source |
|
326 TUint iQueryStepMaxTime; //< Maximum time allowed for single query step to complete |
|
327 TUint iQueryStepMinTime; //< Minimum time for query step (usage depends on mode) |
|
328 TUint8 iQueryStepRetries; //< Additional retries after the primary attempt |
|
329 TInt iSourceCount; //< Remaining number of sources. |
|
330 TInt iNext; //< Non-zero, if executing Next operation |
|
331 |
|
332 // Specify Current Query state |
|
333 |
|
334 const SQueryStep *iQueryStep; //< The Current Query Process (what queries to make) |
|
335 SQueryStep iSpecificQuery; //< Used in implementing the Specific Single Queries |
|
336 TQuerySession iSession[KMaxQuerySessions]; //< Currently active queries or completed results |
|
337 TDeprecatedAnswer iDeprecate[KMaxDeprecated]; //< Buffer to store the delayed answers |
|
338 TUint iPhase; |
|
339 TUint8 iDeprecatedTop; //< Number of delayed top answers in iDeprecate[] |
|
340 TUint8 iDeprecatedBottom; //< Number of delayed bottom answers in iDeprecate[] |
|
341 TUint8 iQuerySteps; //< Number of Query Steps in the process |
|
342 TUint8 iStepCount; //< Number of Query Steps activated so far |
|
343 TUint8 iSessionCount; //< Number of Query Sessions currently active |
|
344 /* |
|
345 // The scope level of the current query. |
|
346 // |
|
347 // Currently the level for GetByName and Query is always "NETWORK". |
|
348 // But, if implemented, it could be "LINK LOCAL", based on the |
|
349 // queried name, for example, if it ends with ".local". |
|
350 // |
|
351 // The level of GetByAddress is the scope level of the address being |
|
352 // queried. |
|
353 // |
|
354 // The query scope limits the sources available to the query: the scope |
|
355 // of the source must be less or equal to the query scope. (For example, |
|
356 // GetByAddress for a link local address is only asked from link local |
|
357 // name resolvers). |
|
358 */ |
|
359 TUint8 iQueryScope; |
|
360 TUint iQueryActive:1; //< set to 1, when a query is being processed |
|
361 TUint iQueryIsAddr:1; //< set to 1, when current query is GetByAddr, = 0, otherwise |
|
362 TUint iQueryIsSpecial:1; //< set to 1, when current query is special (for exact DNS query type) |
|
363 TUint iReadyForAnswer:1; //< set to 1, when an answer can be delivered back to RHostResolver. |
|
364 TUint iQueryDoneWait:1; //< Tells what active object is currently waiting: = 0, if WaitQuery, = 1, if QueryDone |
|
365 TUint iQueryComplete:1; //< set to 1, when query is completed fully (no Next allowed) |
|
366 TUint32 iQueryFlags; //< Flags to the NewQuery (KDnsMofidier_RD, ...) |
|
367 |
|
368 TQuerySession iProbe; //< Use for server probing |
|
369 CDndEngine &iControl; //< The control context (for sending errors etc.) |
|
370 MDndListener &iListener; //< Only for calling KeepRunning() |
|
371 MDnsSource &iDnsclient; //< DNS protocol handler |
|
372 }; |
|
373 |
|
374 |
|
375 // CDndResolverBase::New |
|
376 // ********************* |
|
377 MDndResolver *MDndResolver::New(const TInt aId, CDndEngine &aControl, MDndListener &aListener, MDnsSource &aDnsClient) |
|
378 /** |
|
379 * Create a new instance of CDndResolver. |
|
380 * |
|
381 * This static function creates the real object instance of |
|
382 * the class that has been derived from CDndResolverBase. |
|
383 * |
|
384 * This way, the real implementation of the class is hidden |
|
385 * from any other module (the real class CDndResolver is only |
|
386 * declared and known within resolver.cpp module). |
|
387 * |
|
388 * @param aId identifies the resolver instance (only for debuggind purpose) |
|
389 * @param aControl provides access to the DND environment |
|
390 * @param aListener |
|
391 * provides access to the socket listener, which manages the |
|
392 * pool of CDndResolver instances and delivers the requests |
|
393 * to the resolvers. |
|
394 * @param aDnsClient |
|
395 * provides access to the DNS protocol driver, which implements |
|
396 * the DNS protocol details, and manages the associated UDP and |
|
397 * TCP sockets. |
|
398 * @return |
|
399 * @li NULL, if object could not be created (out of memory) |
|
400 * @li non-NULL, is a pointer to the newly constructed CDndResolver instance |
|
401 */ |
|
402 { |
|
403 return new(ELeave) CDndResolver(aId, aControl, aListener, aDnsClient); |
|
404 } |
|
405 |
|
406 // Constructor |
|
407 CDndResolver::CDndResolver(const TInt aId, CDndEngine &aControl, MDndListener &aListener, MDnsSource &aDnsclient) |
|
408 : iId(aId), iControl(aControl), iListener(aListener), |
|
409 iDnsclient(aDnsclient) |
|
410 { |
|
411 // Because array elements cannot have other than default constructors, |
|
412 // need to initialize the iSession[] and iProbe here.. |
|
413 for (TInt i = KMaxQuerySessions; --i >= 0; ) |
|
414 { |
|
415 iSession[i].Init(this); |
|
416 LOG(iSession[i].iName.Format(_L("%d"), i)); |
|
417 } |
|
418 iProbe.Init(this); |
|
419 LOG(iProbe.iName = _L("*")); |
|
420 } |
|
421 |
|
422 // Destructor |
|
423 CDndResolver::~CDndResolver() |
|
424 { |
|
425 // Need to cancel TQuerySession's (if any active) |
|
426 for (TInt i = KMaxQuerySessions; --i >= 0; ) |
|
427 iSession[i].Close(); |
|
428 } |
|
429 |
|
430 |
|
431 #ifdef _LOG |
|
432 // CDndResolver::ShowQuery(aName) |
|
433 // ****************************** |
|
434 void CDndResolver::ShowQuery(const TDnsMessage &aPacket, TInt aReply) |
|
435 /** |
|
436 * Displays the current query or reply (only a debugging tool). |
|
437 * |
|
438 * @param aPacket Query or Reply data |
|
439 * @param aReply Query vs. reply |
|
440 */ |
|
441 { |
|
442 _LIT(KReply, " Reply "); |
|
443 _LIT(KQuery, " Query "); |
|
444 |
|
445 const TDesC &mode = aReply ? KReply : KQuery; |
|
446 |
|
447 switch (aPacket.iType) |
|
448 { |
|
449 case KDnsRequestType_GetByName: |
|
450 case KDnsRequestType_GetByAddress: |
|
451 { |
|
452 const TNameRecord &rec = aPacket.NameRecord(); |
|
453 TBuf<50> tmp; |
|
454 TInetAddr::Cast(rec.iAddr).OutputWithScope(tmp); |
|
455 if (aReply) |
|
456 |
|
457 Log::Printf(_L("\tresolver[%d] SESSION %d Reply [%d], %S, [%S] result=%d"), iId, aPacket.iSession, rec.iFlags, &rec.iName, &tmp, aPacket.iNext); |
|
458 else |
|
459 Log::Printf(_L("\tresolver[%d] SESSION %d Query (%d), %S, [%S]"), iId, aPacket.iSession, aPacket.iNext, &rec.iName, &tmp); |
|
460 } |
|
461 break; |
|
462 case KDnsRequestType_TDnsQuery: |
|
463 { |
|
464 if (aReply) |
|
465 { |
|
466 const TDndReply &reply = aPacket.Reply(); |
|
467 Log::Printf(_L("\tresolver[%d] SESSION %d Special Reply, type=%d result=%d"), iId, aPacket.iSession, (TInt)reply.RRType(), aPacket.iNext); |
|
468 } |
|
469 else |
|
470 { |
|
471 const TDnsQuery &query = aPacket.Query(); |
|
472 Log::Printf(_L8("\tresolver[%d] SESSION %d Special Query (%d), %S type=%d"), iId, aPacket.iSession, aPacket.iNext, &query.Data(), (TInt)query.Type()); |
|
473 } |
|
474 } |
|
475 break; |
|
476 case KDnsRequestType_SetHostName: |
|
477 Log::Printf(_L("\tresolver[%d] SESSION %d SetHostName(%S)%S%d"), iId, aPacket.iSession, &aPacket.HostName(), &mode, aPacket.iNext); |
|
478 break; |
|
479 case KDnsRequestType_GetHostName: |
|
480 Log::Printf(_L("\tresolver[%d] SESSION %d GetHostName(%S)%S%d"), iId, aPacket.iSession, &aPacket.HostName(), &mode, aPacket.iNext); |
|
481 break; |
|
482 default: |
|
483 Log::Printf(_L("\tresolver[%d] SESSION %d Bad TDnsMessagePacket%S(%d,%d)"), iId, aPacket.iSession, &mode, aPacket.iType, aPacket.iNext); |
|
484 break; |
|
485 } |
|
486 } |
|
487 #endif |
|
488 |
|
489 // CDndResolver::Stop |
|
490 // ****************** |
|
491 void CDndResolver::Stop() |
|
492 /** |
|
493 * Stop serving the current request. |
|
494 * |
|
495 * Terminate all pending activity (if any) and mark the |
|
496 * CDndResolver instace available for a new session. |
|
497 */ |
|
498 { |
|
499 iReadyForAnswer = 0; // ...just to be safe. |
|
500 StopSource(); // stop all DNS activity related to this client, if any |
|
501 if (iQueryActive) |
|
502 { |
|
503 iQueryActive = 0; |
|
504 iDnsclient.QueryEnd(); |
|
505 } |
|
506 LOG(Log::Printf(_L("\tresolver[%d] SESSION %d Stopped"), iId, iCurrentQuery().iSession)); |
|
507 iCurrentQuery().iSession = 0; |
|
508 iQueryDoneWait = 0; |
|
509 } |
|
510 |
|
511 |
|
512 // CDndResolver::WaitQuery |
|
513 // *********************** |
|
514 void CDndResolver::WaitQuery() |
|
515 /** |
|
516 * Put the CDndResolver to wait a next request from the RHostResolver. |
|
517 * |
|
518 * The next request could be a Next, or a new GetByName, GetByAddr or |
|
519 * Query. |
|
520 */ |
|
521 { |
|
522 iReadyForAnswer = 0; |
|
523 if (iQueryActive) |
|
524 { |
|
525 // There could be active sessions |
|
526 // doing some queries while we wait for |
|
527 // client to request something (doing |
|
528 // parallel queries). |
|
529 // Leave "query active" on, if any found. |
|
530 // The information is retrieved during this |
|
531 // time is/will be available only for |
|
532 // the Next() query. |
|
533 for (TInt i = 0;; ++i) |
|
534 { |
|
535 if (i == iSessionCount) |
|
536 { |
|
537 iQueryActive = 0; |
|
538 iDnsclient.QueryEnd(); |
|
539 break; |
|
540 } |
|
541 if (iSession[i].iStatus == KRequestPending) |
|
542 break; |
|
543 }; |
|
544 } |
|
545 iQueryDoneWait = 0; |
|
546 } |
|
547 |
|
548 // CDndResolver::QueryDone |
|
549 // *********************** |
|
550 void CDndResolver::QueryDone(TInt aResult) |
|
551 /** |
|
552 * A query has completed, return a result to the RHostResolver. |
|
553 * |
|
554 * Prepare for the reply into iBuffer for writing to the |
|
555 * "gateway socket". When the reply sender of the listener |
|
556 * is ready to start the transfer, it calls ReplyMessage() |
|
557 * for the buffer to be sent. |
|
558 * |
|
559 * @param aResult |
|
560 * the result code of the query. If KErrNone, the buffer |
|
561 * will contain one answer (TNameRecord or some |
|
562 * TDnsQryRespBase variant). |
|
563 * |
|
564 * If the reply is an error (< 0), this stops all resolving |
|
565 * activity that might be still going on. An error reply is |
|
566 * always final, continuation with a Next() is not possible. |
|
567 */ |
|
568 { |
|
569 if (!iReadyForAnswer) |
|
570 return; // Gateway is not expecting answer now... |
|
571 iReadyForAnswer = 0; |
|
572 iQueryDoneWait = 1; |
|
573 iBuffer().iNext = aResult; |
|
574 if (aResult < 0) |
|
575 { |
|
576 StopSource(); |
|
577 iBuffer.SetLength(iBuffer().HeaderSize()); |
|
578 } |
|
579 LOG(ShowQuery(iBuffer(), 1)); |
|
580 iListener.PostReply(); |
|
581 } |
|
582 |
|
583 // CDndResolver::ReplyMessage |
|
584 // ************************** |
|
585 const TDesC8 &CDndResolver::ReplyMessage() |
|
586 /** |
|
587 * Return the reply message buffer. |
|
588 * |
|
589 * Called by listener sender when it is ready to send the |
|
590 * reply message to the gateway socket. If this resolver |
|
591 * has a reply to send, this must return the buffer. |
|
592 * If no reply is to be sent for this resolver, this must |
|
593 * return an empty buffer reference. |
|
594 * |
|
595 * @return The reply buffer (empty = no reply to send). |
|
596 */ |
|
597 { |
|
598 _LIT8(KNoReply, ""); |
|
599 if (iQueryDoneWait) |
|
600 { |
|
601 const TInt result = iBuffer().iNext; |
|
602 // Only set iQueryComplete, *never* clear it, if already set! |
|
603 if (result != KErrCompletion && result < 0) |
|
604 { |
|
605 iQueryComplete = 1; |
|
606 } |
|
607 iQueryDoneWait = 0; |
|
608 return iBuffer; |
|
609 } |
|
610 else |
|
611 return KNoReply; |
|
612 } |
|
613 |
|
614 // CDndResolver::Session |
|
615 // ********************* |
|
616 TUint16 CDndResolver::Session() const |
|
617 /** |
|
618 * Return the current session id. |
|
619 * |
|
620 * This function is used by the listener to find out the |
|
621 * session id of the request that is currently being |
|
622 * processed by this resolver. |
|
623 * |
|
624 * If the resolver is idle (no request under process), then |
|
625 * this must return 0. All valid session ids are non-zero. |
|
626 * |
|
627 * @return The session (or 0, if none). |
|
628 */ |
|
629 { |
|
630 return iCurrentQuery().iSession; |
|
631 } |
|
632 |
|
633 void CDndResolver::GetNextAnswer() |
|
634 /** |
|
635 * Collect, sort and return answers to the application. |
|
636 * |
|
637 * Collecting answers from DNS. The answers are sorted into |
|
638 * three classes: |
|
639 * |
|
640 * @li (1) |
|
641 * usable addresses (current system has a route and source address for them) |
|
642 * @li (2) |
|
643 * addresses, for which no route exists are put into "TOP" category (if application |
|
644 * uses one of these, then most likely, a new interface needs to be activated). These |
|
645 * are returned after all usable addresses in class (1) have been returned.. |
|
646 * @li (3) |
|
647 * other (non-address) answers to the query |
|
648 * |
|
649 * If a GetByName request needs two queries (for example A and AAAA), |
|
650 * the queries may be performed in parallel or one after the other. |
|
651 * If queries are performed one at time, this method will automaticly |
|
652 * start the second query, after the answers (if any) from the first query |
|
653 * have been processed. |
|
654 * |
|
655 * If a query produces any answer belonging to the class (1), they are |
|
656 * returned to the application as soon as they become available. |
|
657 * If queries are not done in parallel, and application is satisfied |
|
658 * with the first answer (does not issue Next()), then the second |
|
659 * query is never sent. |
|
660 * |
|
661 * If a query produces no answers that sort into class (1), both queries |
|
662 * are always executed fully, before any answers are returned (from class |
|
663 * (2) or (3)) to the application. |
|
664 */ |
|
665 { |
|
666 if (!iReadyForAnswer) |
|
667 return; // Gateway not expecting answer now... |
|
668 |
|
669 iBuffer = iCurrentQuery; // Initialize reply to orginal query |
|
670 TInt pending = 0; |
|
671 TInt notfound = 0; |
|
672 |
|
673 // Initialize query buffer flags. |
|
674 iBuffer().NameRecord().iFlags = 0; |
|
675 |
|
676 if (iPhase == 0) |
|
677 { |
|
678 // |
|
679 // Collect Phase |
|
680 // |
|
681 for (TInt i = 0; i < iSessionCount; ++i) |
|
682 { |
|
683 TQuerySession &session = iSession[i]; |
|
684 |
|
685 if (session.iStatus < 0) |
|
686 { |
|
687 // Either pending or no results available (all processed or some error state) |
|
688 // Detect pending queries (only possible to happen if doing queries in parallel) |
|
689 pending |= (session.iStatus == KRequestPending); |
|
690 notfound |= (session.iStatus == KErrDndBadName); |
|
691 continue; |
|
692 } |
|
693 for (;;) |
|
694 { |
|
695 // |
|
696 // Values iSession[i].iStatus >= 0 [0..n] is used for remembering |
|
697 // the number of the next answer to retrieve from the record |
|
698 const TInt ret = session.iSession->DoNext(iBuffer, session.iStatus); |
|
699 if (ret != KErrNone) |
|
700 { |
|
701 // All available answers have been retrieved. |
|
702 session.iStatus = ret; |
|
703 break; |
|
704 } |
|
705 // NOTE: session.iStatus++!!! |
|
706 const TDeprecatedAnswer answer((TUint8)(i), (TUint8)(session.iStatus++)); |
|
707 |
|
708 if (iQueryIsSpecial) |
|
709 { |
|
710 // All answers are returned as is for special query |
|
711 // (no sorting or any other processing is done) |
|
712 QueryDone(KErrNone); |
|
713 return; |
|
714 } |
|
715 |
|
716 const TInetAddr &addr = TInetAddr::Cast(iBuffer().NameRecord().iAddr); |
|
717 if (addr.IsUnspecified() || (addr.Family() != KAfInet && addr.Family() != KAfInet6)) |
|
718 { |
|
719 // ...things like CNAME's fall into here. If running out of |
|
720 // space, then just ignore these (Top entries are more important) |
|
721 if (iDeprecatedTop < iDeprecatedBottom) |
|
722 { |
|
723 iDeprecate[--iDeprecatedBottom] = answer; |
|
724 } |
|
725 } |
|
726 else if (iDnsclient.CheckAddress(addr) != KErrNone) |
|
727 { |
|
728 // There is no route for the address, don't return this yet |
|
729 if (iDeprecatedTop < KMaxDeprecated) |
|
730 { |
|
731 iDeprecate[iDeprecatedTop++] = answer; |
|
732 // If running out of space, overwrite deprecated bottom entries |
|
733 // as needed... |
|
734 if (iDeprecatedTop > iDeprecatedBottom) |
|
735 iDeprecatedBottom = iDeprecatedTop; |
|
736 } |
|
737 } |
|
738 else |
|
739 { |
|
740 // This is a usable address as is, return immediately |
|
741 QueryDone(KErrNone); |
|
742 return; |
|
743 } |
|
744 } |
|
745 } |
|
746 // |
|
747 // RR records from the current query have been processed. |
|
748 // |
|
749 if (pending) |
|
750 { |
|
751 LOG(Log::Printf(_L("\tresolver[%d] SESSION %d Request(s) pending"), iId, iCurrentQuery().iSession)); |
|
752 // At least one query session is still in progress, continue waiting |
|
753 // Should set some timeout? what? |
|
754 return; |
|
755 } |
|
756 else if (!notfound && iStepCount < iQuerySteps) |
|
757 { |
|
758 // ... but there are alternate steps left, try the next step... |
|
759 // |
|
760 // If all DNS servers worked right, there should be no reason to probe |
|
761 // servers with A, if the probe for AAAA fails. However, sadly, there |
|
762 // broken things out there, which timeout on AAAA, but do reply to A. |
|
763 // Thus, if probing is active, restart it for new query type. |
|
764 if (iProbe.iSession) |
|
765 (void)iProbe.iSession->PickDefaultServer(); |
|
766 |
|
767 TQuerySession &session = iSession[iSessionCount++]; |
|
768 session.Open(); |
|
769 if (session.Start(iQueryStep[iStepCount++].iQType)) |
|
770 return; |
|
771 } |
|
772 } |
|
773 // |
|
774 // Return TOP deprecated |
|
775 // |
|
776 while (iPhase < iDeprecatedTop) |
|
777 { |
|
778 const MDnsSession *session = iSession[iDeprecate[iPhase].iStep].iSession; |
|
779 const TInt ret = session->DoNext(iBuffer, iDeprecate[iPhase].iIndex); |
|
780 // There is no route for the address, add info for application. |
|
781 iBuffer().NameRecord().iFlags |= EDnsNoRoute; |
|
782 iPhase++; |
|
783 if (ret == KErrNone) |
|
784 { |
|
785 QueryDone(KErrNone); |
|
786 return; |
|
787 } |
|
788 } |
|
789 // |
|
790 // Return BOTTOM deprecated |
|
791 // (only for GetByAddr or for Next() after GetByName) |
|
792 // |
|
793 iPhase = KMaxDeprecated; |
|
794 if (iQueryIsAddr || iNext) |
|
795 { |
|
796 while (iDeprecatedBottom < KMaxDeprecated) |
|
797 { |
|
798 const MDnsSession *session = iSession[iDeprecate[iDeprecatedBottom].iStep].iSession; |
|
799 const TInt ret = session->DoNext(iBuffer, iDeprecate[iDeprecatedBottom].iIndex); |
|
800 iDeprecatedBottom++; |
|
801 if (ret == KErrNone) |
|
802 { |
|
803 QueryDone(KErrNone); |
|
804 return; |
|
805 } |
|
806 } |
|
807 } |
|
808 if (iNext) |
|
809 QueryDone(iQueryIsAddr ? KErrDndAddrNotFound : KErrDndNameNotFound); |
|
810 else |
|
811 // No answers returned from current source, try next |
|
812 NextSource(); |
|
813 } |
|
814 |
|
815 // CDndResolver::ProbeServers |
|
816 // ************************** |
|
817 void CDndResolver::ProbeServers() |
|
818 /** |
|
819 * Probing servers. |
|
820 * |
|
821 * Send a dummy duplicate query to the next alternate |
|
822 * server. Used in some resolver modes. The result is ignored |
|
823 * by resolver, but the side effect is that the DNS protocol |
|
824 * handler will detect a good server, and make it the default |
|
825 * for the next try. |
|
826 */ |
|
827 { |
|
828 if (iSessionCount == 0) |
|
829 return; |
|
830 if (iProbe.iSession == NULL) |
|
831 { |
|
832 // Use the first primary session (iSession[0]) as a template |
|
833 // for the "probing". |
|
834 if (iSession[0].iSession == NULL) |
|
835 return; // ...oops!? |
|
836 iProbe.iQType = iSession[0].iQType; |
|
837 iProbe.iSession = iDnsclient.OpenSession(&iProbe); |
|
838 if (iProbe.iSession == NULL) |
|
839 return; |
|
840 LOG(Log::Printf(_L("\tresolver[%d] SESSION %d Probing QType=%d (%S) -- Assigned DNS session [%u]"), |
|
841 iId, (TInt)iCurrentQuery().iSession, (TInt)iProbe.iQType, &iProbe.iName, (TInt)iProbe.iSession->Instance())); |
|
842 // Use the current query as a probe starting point |
|
843 //(void)iProbe.iSession->NewQuery(*iSession[0].iSession); |
|
844 (void)iProbe.iSession->NewQuery(iCurrentQuery(),iSourceNow,iQueryFlags | KDnsModifier_PQ); |
|
845 |
|
846 // initialize server (which is not tried) |
|
847 (void)iProbe.iSession->PickDefaultServer(); |
|
848 } |
|
849 // Start query, but only if there really is another server address |
|
850 if (iProbe.iSession->PickNewServer()) |
|
851 { |
|
852 // Note: if DoQueryL fails, we do nothing. This is just probing... |
|
853 TRAP_IGNORE(iProbe.iSession->DoQueryL(iRequestTime, iProbe.iQType)); |
|
854 } |
|
855 } |
|
856 |
|
857 |
|
858 // CDndResolver::StopSource |
|
859 // ************************ |
|
860 void CDndResolver::StopSource() |
|
861 /** |
|
862 * Stop all resolving activity from current source. |
|
863 * |
|
864 * The answer can be searched from different sources (such as hosts file, |
|
865 * global DNS or link local name resolution). Answers from different sources are |
|
866 * never mixed, and the first source that gives an answer is used. |
|
867 * |
|
868 * StopSource() cancels all activity (if any) from current source. |
|
869 */ |
|
870 { |
|
871 // Release sessions, if they were created |
|
872 while (iSessionCount > 0) |
|
873 iSession[--iSessionCount].Close(); |
|
874 iProbe.Close(); |
|
875 } |
|
876 |
|
877 |
|
878 // CDndResolver::NextSource |
|
879 // ************************ |
|
880 void CDndResolver::NextSource() |
|
881 /** |
|
882 * Activate search from next source. |
|
883 * |
|
884 * The answer can be searched from different sources (such as hosts file, |
|
885 * global DNS or link local name resolution). Answers from different sources are |
|
886 * never mixed, and the first source that gives an answer is used. |
|
887 * |
|
888 * NextSource activates the next query source or terminates the |
|
889 * query, if all sources have been tried. |
|
890 */ |
|
891 { |
|
892 |
|
893 while (iSourceCount > 0) |
|
894 { |
|
895 StopSource(); // Cleanup from previous source, if any |
|
896 |
|
897 // Replies from different source are never mixed |
|
898 // Restart collecting answers too. |
|
899 iPhase = 0; |
|
900 iDeprecatedTop = 0; |
|
901 iDeprecatedBottom = KMaxDeprecated; |
|
902 iSourceNow = *iSourceList++; |
|
903 iSourceCount -= 1; |
|
904 |
|
905 // Is this source valid for the query scope (e.g. if source is global |
|
906 // DNS, one should not try to query site or link local queries from it!) |
|
907 // On the other hand, it is legal to make global scope query to link |
|
908 // local or site scope servers. |
|
909 const TUint level = (TUint)Abs((TInt)iSourceNow); |
|
910 if (iQueryScope < level) |
|
911 continue; // Query scope is less than servers scope, try another source! |
|
912 |
|
913 if (StartSource()) |
|
914 return; |
|
915 } |
|
916 |
|
917 // The name could not be resolved by any source |
|
918 QueryDone(iQueryIsAddr ? KErrDndAddrNotFound : KErrDndNameNotFound); |
|
919 } |
|
920 |
|
921 |
|
922 // CDndResolver::StartSource |
|
923 // ************************* |
|
924 TBool CDndResolver::StartSource() |
|
925 /** |
|
926 * Initiate a source. |
|
927 * |
|
928 * Start/Activate retrieval of query answers from the current source, |
|
929 * as defined by CDndResolver::iSourceNow. Three classes of |
|
930 * sources are supported: |
|
931 * |
|
932 * @li HOST file (= 0) |
|
933 * @li Unicast DNS server ( < 0) |
|
934 * @li Multicast name resolution service ( > 0) |
|
935 * |
|
936 * The absolute value of iSourceNow is the scope |
|
937 * level of the name resolution source (2 = link local, etc..) |
|
938 * |
|
939 * @returns |
|
940 * @li TRUE, if process has been started (or answer already resolved). |
|
941 * @li FALSE, if the source is not available or usable |
|
942 */ |
|
943 { |
|
944 // If you define more complex queries, needing more than current max of |
|
945 // steps, then bump up the KMaxQuerySessions! Panic on debug, on release |
|
946 // just ignore the extra steps. |
|
947 ASSERT(iQuerySteps <= KMaxQuerySessions); |
|
948 if (iQuerySteps > KMaxQuerySessions) |
|
949 iQuerySteps = KMaxQuerySessions; |
|
950 |
|
951 |
|
952 const TDndConfigParameters &cf = iControl.GetConfig(); |
|
953 |
|
954 TUint query_order = cf.iQueryOrder; |
|
955 |
|
956 if (iSourceNow == 0) |
|
957 { |
|
958 // HOST file or hostname source |
|
959 if (!iQueryIsSpecial) |
|
960 { |
|
961 // Check if the query is for local host name or my own address. |
|
962 TInt res; |
|
963 if (iQueryIsAddr) |
|
964 res = iDnsclient.GetByAddress(iBuffer().iId, TInetAddr::Cast(iBuffer().NameRecord().iAddr), iNext, iBuffer().NameRecord().iName); |
|
965 else |
|
966 res = iDnsclient.GetByName(iBuffer().iId, iBuffer().NameRecord().iName, iNext, TInetAddr::Cast(iBuffer().NameRecord().iAddr)); |
|
967 if (res == KErrNone) |
|
968 { |
|
969 LOG(Log::Printf(_L("\tresolver[%d] SESSION %d matched local hostname/address"), iId, iCurrentQuery().iSession)); |
|
970 iBuffer().NameRecord().iFlags = EDnsHostName; |
|
971 QueryDone(KErrNone); |
|
972 return TRUE; |
|
973 } |
|
974 // ..and now the HOSTS file |
|
975 LOG(Log::Printf(_L("\tresolver[%d] SESSION %d SEARCHING hosts file"), iId, iCurrentQuery().iSession)); |
|
976 TRequestStatus tmp; |
|
977 if (iQueryIsAddr) |
|
978 iControl.iHostsFile.GetByAddress(iBuffer().NameRecord(), tmp); |
|
979 else |
|
980 iControl.iHostsFile.GetByName(iBuffer().NameRecord(), tmp); |
|
981 if (tmp.Int() == KErrNone) |
|
982 { |
|
983 iBuffer().NameRecord().iFlags = EDnsHostsFile; |
|
984 QueryDone(KErrNone); |
|
985 return TRUE; |
|
986 } |
|
987 } |
|
988 |
|
989 if (iBuffer().iId == 0) |
|
990 { |
|
991 // If there is no scope is specified, then only hosts file |
|
992 // is looked. |
|
993 QueryDone(KErrCompletion); |
|
994 return TRUE; |
|
995 } |
|
996 // No Answer from the host file, move on to the next |
|
997 return FALSE; |
|
998 } |
|
999 else if (iSourceNow < 0) |
|
1000 { |
|
1001 // |
|
1002 // Unicast DNS source |
|
1003 // |
|
1004 LOG(Log::Printf(_L("\tresolver[%d] SESSION %d SEARCHING unicast DNS"), iId, iCurrentQuery().iSession)); |
|
1005 // Setup parameters for basic query step (steps are done one after another) |
|
1006 iQueryStepMaxTime = Max(cf.iMinTime, cf.iMaxTime / iQuerySteps); |
|
1007 iQueryStepMinTime = cf.iMinTime; |
|
1008 iQueryStepRetries = (TUint8)cf.iRetries; |
|
1009 } |
|
1010 else |
|
1011 { |
|
1012 // |
|
1013 // Multicast DNS source |
|
1014 // |
|
1015 // Setup parameters for basic query step |
|
1016 LOG(Log::Printf(_L("\tresolver[%d] SESSION %d SEARCHING multicast DNS"), iId, iCurrentQuery().iSession)); |
|
1017 query_order |= KQueryOrder_Parallel; // Multicast steps are always done in parallel |
|
1018 #ifdef LLMNR_ENABLED |
|
1019 // Assumed: "multicast DNS" == LLMNR! |
|
1020 iQueryStepMaxTime = Max(cf.iLlmnrMinTime, cf.iLlmnrMaxTime); |
|
1021 iQueryStepMinTime = cf.iLlmnrMinTime; |
|
1022 iQueryStepRetries = (TUint8)cf.iLlmnrRetries; |
|
1023 #else |
|
1024 iQueryStepMaxTime = Max(cf.iMinTime, cf.iMaxTime); |
|
1025 iQueryStepMinTime = cf.iMinTime; |
|
1026 iQueryStepRetries = (TUint8)cf.iRetries; |
|
1027 #endif |
|
1028 } |
|
1029 // |
|
1030 // Start the query/queries |
|
1031 // |
|
1032 TBool started = FALSE; |
|
1033 iSessionCount = 0; |
|
1034 iStepCount = 0; |
|
1035 while (iStepCount < iQuerySteps) |
|
1036 { |
|
1037 TQuerySession &session = iSession[iSessionCount++]; |
|
1038 session.Open(); |
|
1039 if (session.iSession == NULL) |
|
1040 { |
|
1041 // If the required session cannot be allocated, |
|
1042 // abort the request totally. |
|
1043 QueryDone(session.iStatus); |
|
1044 return TRUE; |
|
1045 } |
|
1046 started |= session.Start(iQueryStep[iStepCount++].iQType); |
|
1047 if (started && (query_order & KQueryOrder_Parallel) == 0) |
|
1048 break; |
|
1049 } |
|
1050 return started; |
|
1051 } |
|
1052 |
|
1053 // CDndResolver::HOSTFILE_Next |
|
1054 // *************************** |
|
1055 void CDndResolver::HOSTFILE_Next() |
|
1056 /** |
|
1057 * Return next answer from HOST file. |
|
1058 * |
|
1059 * The HOST file answers are not currently sorted in any way. The |
|
1060 * answers are returned as is, in whatever order the host file |
|
1061 * module returns them. |
|
1062 * |
|
1063 * Only TNameRecord based queries are asked from the HOST file |
|
1064 * (e.g. the standart RHostResolver::GetByName and RHostResolver::GetByAddr). |
|
1065 * No special queries. |
|
1066 */ |
|
1067 { |
|
1068 // Should never get called with special queries, but if that |
|
1069 // happens, just return KErrEof as a safety measure... |
|
1070 TRequestStatus tmp(KErrEof); |
|
1071 if (!iQueryIsSpecial) |
|
1072 { |
|
1073 // Temp. kludge: next indicator for hosts file reader is passed in iFlags! |
|
1074 iBuffer().NameRecord().iFlags = iNext; |
|
1075 if (iQueryIsAddr) |
|
1076 iControl.iHostsFile.GetByAddress(iBuffer().NameRecord(), tmp); |
|
1077 else |
|
1078 iControl.iHostsFile.GetByName(iBuffer().NameRecord(), tmp); |
|
1079 // Note: iHostFile returns the result code in TRequestStatus. |
|
1080 iBuffer().NameRecord().iFlags = EDnsHostsFile; |
|
1081 } |
|
1082 QueryDone(tmp.Int()); |
|
1083 } |
|
1084 |
|
1085 // CDndResolver::Start |
|
1086 // ******************* |
|
1087 void CDndResolver::Start(const TDnsMessageBuf &aMsg) |
|
1088 /** |
|
1089 * Process a new request from listener/RHostResolver. |
|
1090 * |
|
1091 * @param aMsg The request message |
|
1092 */ |
|
1093 { |
|
1094 const TDndConfigParameters &cf = iControl.GetConfig(); |
|
1095 |
|
1096 // Sanity checks -- determine the min size of a proper request |
|
1097 const TInt min_size = aMsg().HeaderSize() + |
|
1098 TInt(aMsg().iType == KDnsRequestType_TDnsQuery ? sizeof(TDnsQuery) : |
|
1099 aMsg().iType == KDnsRequestType_GetByName ? sizeof(TNameRecord) : |
|
1100 aMsg().iType == KDnsRequestType_GetByAddress ? sizeof(TNameRecord) : |
|
1101 sizeof(THostName)); |
|
1102 if (aMsg.Length() < min_size) // If short query => treat as cancel! |
|
1103 { |
|
1104 LOG(Log::Printf(_L("\tresolver[%d] SESSION %d Cancelled by client (len=%d)"), iId, iCurrentQuery().iSession, aMsg.Length())); |
|
1105 Stop(); |
|
1106 return; |
|
1107 } |
|
1108 |
|
1109 // Stack should never start new request before delivering the response. |
|
1110 // Exception is the Cancel-request which is treated above. |
|
1111 ASSERT(iQueryDoneWait == 0); |
|
1112 |
|
1113 iBuffer = aMsg; |
|
1114 // |
|
1115 // A new command/query from the application |
|
1116 // |
|
1117 LOG(ShowQuery(iBuffer(), 0)); |
|
1118 |
|
1119 // If query is not active, it will be after this |
|
1120 if (iQueryActive == 0) |
|
1121 { |
|
1122 iQueryActive = 1; |
|
1123 iDnsclient.QueryBegin(); |
|
1124 } |
|
1125 |
|
1126 iReadyForAnswer = 1; // an answer can now be returned... |
|
1127 iRequestTime.UniversalTime(); // Set the time of request |
|
1128 |
|
1129 iNext = iBuffer().iNext; |
|
1130 if (iNext) |
|
1131 { |
|
1132 // This is Next() processing, retrieving |
|
1133 // the additional records. |
|
1134 if (iQueryComplete) |
|
1135 { |
|
1136 // An application/something is stubbornly calling Next() after |
|
1137 // receiving an error (query complete) or the query was "one shot" |
|
1138 // that returned "KErrNone", and was complete at that (no Next). |
|
1139 // This error should terminate the Next() loop in app! |
|
1140 QueryDone(KErrNotFound); |
|
1141 return; |
|
1142 } |
|
1143 iBuffer = iCurrentQuery; // Reset to orginal query (Next() cannot change the query) |
|
1144 if (iSourceNow == 0) |
|
1145 HOSTFILE_Next(); |
|
1146 else |
|
1147 GetNextAnswer(); |
|
1148 } |
|
1149 else |
|
1150 { |
|
1151 // |
|
1152 // Start processing of new query, not Next() |
|
1153 // |
|
1154 iQueryComplete = 0; |
|
1155 iCurrentQuery = iBuffer; // Remember current original query |
|
1156 iQueryFlags = KDnsModifier_RD; // By default, request recursive querying from the server(s). |
|
1157 StopSource(); |
|
1158 switch (iBuffer().iType) |
|
1159 { |
|
1160 case KDnsRequestType_GetByName: |
|
1161 case KDnsRequestType_GetByAddress: |
|
1162 { |
|
1163 TNameRecord &rec = iCurrentQuery().NameRecord(); |
|
1164 |
|
1165 if (iCurrentQuery().iType == KDnsRequestType_GetByAddress) |
|
1166 { |
|
1167 iQueryIsAddr = 1; |
|
1168 // The scope of GetByAddress query is determined from the scope level |
|
1169 // of the address being queried. |
|
1170 iQueryScope = (TUint8)TInetAddr::Cast(rec.iAddr).Ip6Address().Scope(); |
|
1171 iQueryIsSpecial = 0; |
|
1172 iQueryStep = KGetByAddress; |
|
1173 iQuerySteps = sizeof(KGetByAddress) / sizeof(KGetByAddress[0]); |
|
1174 } |
|
1175 else |
|
1176 { |
|
1177 iQueryIsAddr = 0; |
|
1178 // All getbyname queries are global for now. As a future feature |
|
1179 // It might be considered, that single label or names ending with |
|
1180 // ".local" would have have local scope -- msa |
|
1181 iQueryScope = KIp6AddrScopeNetwork; |
|
1182 |
|
1183 #ifdef DND_DCM_EXTENSION |
|
1184 iQueryIsSpecial = 0; |
|
1185 TInt i; |
|
1186 while ((i = rec.iName.Locate('?')) >= 0) |
|
1187 { |
|
1188 const TPtrC prefix = rec.iName.Left(i+1); |
|
1189 if (prefix.Compare(KDnsNonrecursive) == 0) |
|
1190 { |
|
1191 iQueryFlags &= ~KDnsModifier_RD; // Reset recursive flag. |
|
1192 } |
|
1193 else if (cf.iQueryHack) |
|
1194 { |
|
1195 // Special Query |
|
1196 iQueryIsSpecial = 1; |
|
1197 TInt q = sizeof(KQueryExtension) / sizeof(KQueryExtension[0]); |
|
1198 while (--q >= 0 && prefix.Compare(*KQueryExtension[q].iPrefix) != 0) |
|
1199 /* NOTHING */; |
|
1200 if (q < 0) |
|
1201 { |
|
1202 // Requested query is not supported |
|
1203 QueryDone(KErrNotSupported); |
|
1204 return; |
|
1205 } |
|
1206 iQueryStep = &KQueryExtension[q].iQuery; |
|
1207 iQuerySteps = 1; |
|
1208 } |
|
1209 else |
|
1210 { |
|
1211 QueryDone(KErrNotSupported); |
|
1212 return; |
|
1213 } |
|
1214 rec.iName.Delete(0,i+1); |
|
1215 } |
|
1216 |
|
1217 if (!iQueryIsSpecial) |
|
1218 { |
|
1219 // Standard GetByName |
|
1220 if ((cf.iQueryOrder & KQueryOrder_PreferA) == 0) |
|
1221 { |
|
1222 iQueryStep = KGetByName1; |
|
1223 iQuerySteps = sizeof(KGetByName1) / sizeof(KGetByName1[0]); |
|
1224 } |
|
1225 else |
|
1226 { |
|
1227 iQueryStep = KGetByName2; |
|
1228 iQuerySteps = sizeof(KGetByName2) / sizeof(KGetByName2[0]); |
|
1229 } |
|
1230 // If query is to be limited to only one (either A or AAAA), do |
|
1231 // the preferred query only (the first in the chosen list). |
|
1232 if (cf.iQueryOrder & KQueryOrder_OneQuery) |
|
1233 iQuerySteps = 1; |
|
1234 } |
|
1235 #else |
|
1236 const TInt i = rec.iName.Locate('?'); |
|
1237 |
|
1238 if (i < 0) |
|
1239 { |
|
1240 // Standard GetByName |
|
1241 iQueryIsSpecial = 0; |
|
1242 if ((cf.iQueryOrder & KQueryOrder_PreferA) == 0) |
|
1243 { |
|
1244 iQueryStep = KGetByName1; |
|
1245 iQuerySteps = sizeof(KGetByName1) / sizeof(KGetByName1[0]); |
|
1246 } |
|
1247 else |
|
1248 { |
|
1249 iQueryStep = KGetByName2; |
|
1250 iQuerySteps = sizeof(KGetByName2) / sizeof(KGetByName2[0]); |
|
1251 } |
|
1252 // If query is to be limited to only one (either A or AAAA), do |
|
1253 // the preferred query only (the first in the chosen list). |
|
1254 if (cf.iQueryOrder & KQueryOrder_OneQuery) |
|
1255 iQuerySteps = 1; |
|
1256 } |
|
1257 else if (cf.iQueryHack) |
|
1258 { |
|
1259 // Special Query |
|
1260 iQueryIsSpecial = 1; |
|
1261 const TPtrC prefix = rec.iName.Left(i+1); |
|
1262 TInt q = sizeof(KQueryExtension) / sizeof(KQueryExtension[0]); |
|
1263 while (--q >= 0 && prefix.Compare(*KQueryExtension[q].iPrefix) != 0) |
|
1264 /* NOTHING */; |
|
1265 rec.iName.Delete(0,i+1); |
|
1266 if (q < 0) |
|
1267 { |
|
1268 // Requested query is not supported |
|
1269 QueryDone(KErrNotSupported); |
|
1270 return; |
|
1271 } |
|
1272 iQueryStep = &KQueryExtension[q].iQuery; |
|
1273 iQuerySteps = 1; |
|
1274 } |
|
1275 else |
|
1276 { |
|
1277 QueryDone(KErrNotSupported); |
|
1278 return; |
|
1279 } |
|
1280 #endif |
|
1281 } |
|
1282 } |
|
1283 break; |
|
1284 case KDnsRequestType_TDnsQuery: |
|
1285 // Special Query |
|
1286 iQueryIsSpecial = 1; |
|
1287 iQueryIsAddr = 0; |
|
1288 iQueryScope = KIp6AddrScopeNetwork; |
|
1289 iSpecificQuery.iQType = EDnsQType(iBuffer().Query().Type()); |
|
1290 iQueryStep = &iSpecificQuery; |
|
1291 iQuerySteps = 1; |
|
1292 #ifdef DND_DCM_EXTENSION |
|
1293 if (iBuffer().Query().Type() == KDnsQTypeCacheClear) |
|
1294 { |
|
1295 TRAPD(err, iListener.HandleCommandL(EDndFlush)); |
|
1296 iQueryComplete = 1; // "Next()" is not allowed! |
|
1297 QueryDone(err); |
|
1298 return; |
|
1299 } |
|
1300 #endif |
|
1301 break; |
|
1302 case KDnsRequestType_SetHostName: |
|
1303 iQueryComplete = 1; // "Next()" is not allowed (only needed for ret == KErrNone case) |
|
1304 QueryDone(iDnsclient.SetHostName(iBuffer().iId, iBuffer().HostName())); |
|
1305 return; |
|
1306 case KDnsRequestType_GetHostName: |
|
1307 { |
|
1308 const TInt ret = iDnsclient.GetHostName(iBuffer().iId, iBuffer().HostName(), *this); |
|
1309 if (ret <= 0) |
|
1310 { |
|
1311 iQueryComplete = 1; // "Next()" is not allowed (only needed for ret == KErrNone case) |
|
1312 QueryDone(ret); // GetHostName completed, we are done! |
|
1313 } |
|
1314 // GetHostName is pending on uniquenes test (ret == 1) |
|
1315 return; |
|
1316 } |
|
1317 default: |
|
1318 QueryDone(KErrNotSupported); |
|
1319 return; |
|
1320 } |
|
1321 // |
|
1322 // Setup NS source (now fixed, make configurable later) |
|
1323 // |
|
1324 iSourceList = KSourceOrder; |
|
1325 iSourceCount = sizeof(KSourceOrder) / sizeof(KSourceOrder[0]); |
|
1326 NextSource(); |
|
1327 } |
|
1328 } |
|
1329 |
|
1330 // Only used for GetHostName |
|
1331 void CDndResolver::ReplyCallback(const TInt aResult) |
|
1332 { |
|
1333 iBuffer = iCurrentQuery; |
|
1334 QueryDone(aResult); |
|
1335 } |
|
1336 |
|
1337 // |
|
1338 // |
|
1339 |
|
1340 TQuerySession::TQuerySession() : iTimeout(TQuerySessionTimeoutLinkage::Timeout) |
|
1341 { |
|
1342 } |
|
1343 |
|
1344 // TQuerySession::Init |
|
1345 // ******************* |
|
1346 void TQuerySession::Init(CDndResolver *const aResolver) |
|
1347 /** |
|
1348 * Initialize structure. |
|
1349 * |
|
1350 * A separate Init is required, because arrays cannot have non-defaulf |
|
1351 * constructors. Need to patch the iResolver link "manually". |
|
1352 */ |
|
1353 { |
|
1354 iResolver = aResolver; |
|
1355 // ...for other members, the default constructor is sufficient |
|
1356 } |
|
1357 |
|
1358 |
|
1359 // TQuerySession::Open |
|
1360 // ******************* |
|
1361 void TQuerySession::Open() |
|
1362 /** |
|
1363 * Activate a query session. |
|
1364 * |
|
1365 * Assume CDndResolver::iCurrentQuery holds the request from the client. |
|
1366 * Establish a session with the DNS protocol provides and initialize |
|
1367 * it with the current question (NewQuery). |
|
1368 */ |
|
1369 { |
|
1370 iSession = iResolver->iDnsclient.OpenSession(this); |
|
1371 if (iSession == NULL) |
|
1372 { |
|
1373 // Cannot allocate a session |
|
1374 LOG(Log::Printf(_L("\tresolver[%d] SESSION %d QType=%d (%S) -- OUT OF DNS SESSIONS!"), |
|
1375 iResolver->iId, iResolver->iCurrentQuery().iSession, (TInt)iQType, &iName)); |
|
1376 iStatus = KErrNoMemory; |
|
1377 } |
|
1378 else |
|
1379 { |
|
1380 LOG(Log::Printf(_L("\tresolver[%d] SESSION %d QType=%d (%S) -- Assigned DNS session [%u]"), |
|
1381 iResolver->iId, iResolver->iCurrentQuery().iSession, (TInt)iQType, &iName, (TInt)iSession->Instance())); |
|
1382 // ..if NewQuery returns an error, it will be the result of the query. Otherwise |
|
1383 // use the KErrEof as initial value. |
|
1384 iStatus = iSession->NewQuery(iResolver->iCurrentQuery(), iResolver->iSourceNow, iResolver->iQueryFlags); |
|
1385 if (iStatus == KErrNone) |
|
1386 iStatus = KErrEof; // No content yet. |
|
1387 #ifdef SYMBIAN_DNS_PUNYCODE |
|
1388 else // in case the Query String is UTF-16 encoded |
|
1389 { |
|
1390 iResolver->QueryDone(iStatus); |
|
1391 } |
|
1392 #endif //SYMBIAN_DNS_PUNYCODE |
|
1393 } |
|
1394 } |
|
1395 |
|
1396 // TQuerySession::SetTimer |
|
1397 // *********************** |
|
1398 void TQuerySession::SetTimer(TUint aTimeout) |
|
1399 /** |
|
1400 * Request a Timeout() call after the specied number of seconds. |
|
1401 */ |
|
1402 { |
|
1403 LOG(Log::Printf(_L("\tresolver[%d] SESSION %d QType=%d (%S) -- set timeout = %ds"), |
|
1404 iResolver->iId, iResolver->iCurrentQuery().iSession, (TInt)iQType, &iName, (TInt)aTimeout)); |
|
1405 iResolver->iControl.Timer().Set(iTimeout, aTimeout); |
|
1406 } |
|
1407 |
|
1408 // TQuerySession::Start |
|
1409 // ******************** |
|
1410 TBool TQuerySession::Start(EDnsQType aQType) |
|
1411 /** |
|
1412 * Start the query process for a specific query type. |
|
1413 * |
|
1414 * @param aQType The query type (A, AAAA, MX, etc.) |
|
1415 */ |
|
1416 { |
|
1417 iQType = aQType; |
|
1418 LOG(Log::Printf(_L("\tresolver[%d] SESSION %d QType=%d (%S) -- Start"), |
|
1419 iResolver->iId, iResolver->iCurrentQuery().iSession, (TInt)iQType, &iName)); |
|
1420 iServerCount = 1; |
|
1421 iQueryRetry = 0; |
|
1422 iQueryTimeouts = 0; |
|
1423 |
|
1424 if (!iSession) |
|
1425 return FALSE; |
|
1426 |
|
1427 iStatus = KErrEof; |
|
1428 |
|
1429 if (iSession->PickDefaultServer()) |
|
1430 { |
|
1431 SendDnsQuery(); |
|
1432 return TRUE; |
|
1433 } |
|
1434 return FALSE; |
|
1435 } |
|
1436 |
|
1437 // TQuerySession::Cancel |
|
1438 // ********************* |
|
1439 void TQuerySession::Cancel() |
|
1440 /** |
|
1441 * Cancel and stop all activity (if any) with this session. |
|
1442 * |
|
1443 * This cancels set timers and any DNS queries that are in |
|
1444 * progress. |
|
1445 */ |
|
1446 { |
|
1447 iTimeout.Cancel(); |
|
1448 |
|
1449 if (!iSession) |
|
1450 return; |
|
1451 |
|
1452 iSession->CancelQuery(); |
|
1453 if (iStatus == KRequestPending) |
|
1454 { |
|
1455 LOG(Log::Printf(_L("\tresolver[%d] SESSION %d QType=%d (%S) -- CANCELED"), |
|
1456 iResolver->iId, iResolver->iCurrentQuery().iSession, (TInt)iQType, &iName)); |
|
1457 iStatus = KErrCancel; |
|
1458 } |
|
1459 } |
|
1460 |
|
1461 |
|
1462 // TQuerySession::Close |
|
1463 // ******************** |
|
1464 void TQuerySession::Close() |
|
1465 /** |
|
1466 * Close session. |
|
1467 * |
|
1468 * First calls Cancel() to call all activity, and then also |
|
1469 * closes the session with DNS protocol provider. |
|
1470 */ |
|
1471 { |
|
1472 iPendingQuery = 0; // Cancel pending queries too! |
|
1473 Cancel(); |
|
1474 if (!iSession) |
|
1475 return; |
|
1476 iSession->Close(); |
|
1477 LOG(Log::Printf(_L("\tresolver[%d] SESSION %d QType=%d (%S) -- DNS [%u] closed"), |
|
1478 iResolver->iId, iResolver->iCurrentQuery().iSession, (TInt)iQType, &iName, (TInt)iSession)); |
|
1479 iSession = NULL; |
|
1480 } |
|
1481 |
|
1482 |
|
1483 // TQuerySession::RecvReply |
|
1484 // *********************** |
|
1485 void TQuerySession::ReplyCallback(TInt aErr) |
|
1486 /** |
|
1487 * Callback from DNS client. |
|
1488 * |
|
1489 * DNS client has completed something. |
|
1490 * |
|
1491 * @param aErr The completion result or some notify. |
|
1492 */ |
|
1493 { |
|
1494 if (iStatus != KRequestPending) |
|
1495 { |
|
1496 LOG(Log::Printf(_L("\tresolver[%d] SESSION %d QType=%d (%S) -- RecvReply(%d) Ignore event"), |
|
1497 iResolver->iId, iResolver->iCurrentQuery().iSession, (TInt)iQType, &iName, aErr)); |
|
1498 return; // Unexpected event, ignore! |
|
1499 } |
|
1500 |
|
1501 if (aErr > 0) |
|
1502 { |
|
1503 // Notify Messages, just ignore if not recognised. |
|
1504 if (aErr == KDnsNotify_QUERY_SENT) |
|
1505 { |
|
1506 if (iWaitingQuerySent) |
|
1507 { |
|
1508 iWaitingQuerySent = 0; // got it... |
|
1509 LOG(Log::Printf(_L("\tresolver[%d] SESSION %d QType=%d (%S) -- RecvReply(%d) Query Sent"), |
|
1510 iResolver->iId, iResolver->iCurrentQuery().iSession, (TInt)iQType, &iName, aErr)); |
|
1511 SetTimer(iQueryTimeoutValue); |
|
1512 } |
|
1513 } |
|
1514 else if (aErr == KDnsNotify_HAVE_SERVERLIST) |
|
1515 { |
|
1516 // Currently does nothing with this. |
|
1517 LOG(Log::Printf(_L("\tresolver[%d] SESSION %d QType=%d (%S) -- RecvReply(%d) Has serverlist"), |
|
1518 iResolver->iId, iResolver->iCurrentQuery().iSession, (TInt)iQType, &iName, aErr)); |
|
1519 } |
|
1520 else if (aErr == KDnsNotify_USING_TCP) |
|
1521 { |
|
1522 iQueryRetry = iResolver->iQueryStepRetries; |
|
1523 iQueryTimeoutValue = iResolver->iQueryStepMaxTime; |
|
1524 iWaitingQuerySent = 0; |
|
1525 LOG(Log::Printf(_L("\tresolver[%d] SESSION %d QType=%d (%S) -- RecvReply(%d) Query using TCP, disable retransmit"), |
|
1526 iResolver->iId, iResolver->iCurrentQuery().iSession, (TInt)iQType, &iName, aErr)); |
|
1527 SetTimer(iQueryTimeoutValue); |
|
1528 } |
|
1529 } |
|
1530 else if (aErr != KErrNone && (aErr > KErrDndNameNotFound || aErr < KErrDndServerUnusable)) |
|
1531 { |
|
1532 // The error is not query specific DND error code, assume it is |
|
1533 // something general and abort the query totally. |
|
1534 LOG(Log::Printf(_L("\tresolver[%d] SESSION %d QType=%d (%S) -- RecvReply(%d) Query completed"), |
|
1535 iResolver->iId, iResolver->iCurrentQuery().iSession, (TInt)iQType, &iName, aErr)); |
|
1536 iResolver->QueryDone(aErr); |
|
1537 } |
|
1538 else |
|
1539 { |
|
1540 iStatus = aErr; |
|
1541 iTimeout.Cancel(); |
|
1542 LOG(Log::Printf(_L("\tresolver[%d] SESSION %d QType=%d (%S) -- RecvReply(%d) Handle event"), |
|
1543 iResolver->iId, iResolver->iCurrentQuery().iSession, (TInt)iQType, &iName, aErr)); |
|
1544 Event(FALSE); |
|
1545 } |
|
1546 } |
|
1547 |
|
1548 // TQuerySession::Timeout |
|
1549 // ********************** |
|
1550 void TQuerySession::Timeout(const TTime &aNow) |
|
1551 /** |
|
1552 * No response from the DNS handler within specified time, handle timeout. |
|
1553 * |
|
1554 * @param aNow The current time |
|
1555 */ |
|
1556 { |
|
1557 LOG(Log::Printf(_L("-->\tresolver[%d] SESSION %d QType=%d (%S) -- timeout start"), |
|
1558 iResolver->iId, iResolver->iCurrentQuery().iSession, (TInt)iQType, &iName)); |
|
1559 if (iWaitingQuerySent) |
|
1560 { |
|
1561 // |
|
1562 // Don't wait forever, abort resolving if max setup time exceeded |
|
1563 // |
|
1564 const TDndConfigParameters &cf = iResolver->iControl.GetConfig(); |
|
1565 TTimeIntervalSeconds seconds; |
|
1566 if (aNow.SecondsFrom(iResolver->iRequestTime, seconds) != KErrNone || |
|
1567 seconds.Int() > STATIC_CAST(TInt, cf.iSetupTime)) |
|
1568 { |
|
1569 iStatus = KErrDndServerUnusable; |
|
1570 LOG(Log::Printf(_L("\tresolver[%d] SESSION %d QType=%d (%S) -- Timeout, max wait expired [%d > %d]: %d"), |
|
1571 iResolver->iId, iResolver->iCurrentQuery().iSession, (TInt)iQType, &iName, seconds.Int(), cf.iSetupTime, iStatus)); |
|
1572 } |
|
1573 else |
|
1574 { |
|
1575 LOG(Log::Printf(_L("\tresolver[%d] SESSION %d QType=%d (%S) -- Timeout, has waited %ds [max %d]"), |
|
1576 iResolver->iId, iResolver->iCurrentQuery().iSession, (TInt)iQType, &iName, seconds.Int(), cf.iSetupTime)); |
|
1577 } |
|
1578 } |
|
1579 else |
|
1580 { |
|
1581 LOG(Log::Printf(_L("\tresolver[%d] SESSION %d QType=%d (%S) -- timeout"), |
|
1582 iResolver->iId, iResolver->iCurrentQuery().iSession, (TInt)iQType, &iName)); |
|
1583 } |
|
1584 // Note that Timeout alone does not touch the iStatus, nor does it |
|
1585 // cancel the request, if any is active. It's up to Event to decide |
|
1586 // on these issues. |
|
1587 Event(TRUE); |
|
1588 LOG(Log::Printf(_L("<--\tresolver[%d] SESSION %d QType=%d (%S) -- timeout exit"), |
|
1589 iResolver->iId, iResolver->iCurrentQuery().iSession, (TInt)iQType, &iName)); |
|
1590 } |
|
1591 |
|
1592 |
|
1593 // TQuerySession::Event |
|
1594 // ******************** |
|
1595 void TQuerySession::Event(TBool aTimeOut) |
|
1596 /** |
|
1597 * Shared handling of session events: timeouts and DNS query completions. |
|
1598 * |
|
1599 * @param aTimeOut is TRUE, if this is a timeout event; and FALSE, if |
|
1600 * event caused by DNS completion. |
|
1601 */ |
|
1602 { |
|
1603 const TDndConfigParameters &cf = iResolver->iControl.GetConfig(); |
|
1604 |
|
1605 // Handle events from DNS or timeout |
|
1606 // ********************************* |
|
1607 // TIMEOUT or REPLY to a pending query has been received |
|
1608 // |
|
1609 const TBool server_unusable = (iStatus == KErrDndRefused || iStatus == KErrDndServerUnusable); |
|
1610 if (!aTimeOut && !server_unusable) |
|
1611 { |
|
1612 // This query has been completed with some definite result, notify |
|
1613 // the main resolver to handle the reply. |
|
1614 // |
|
1615 iResolver->GetNextAnswer(); |
|
1616 return; |
|
1617 } |
|
1618 |
|
1619 |
|
1620 if (iWaitingQuerySent && !server_unusable) |
|
1621 { |
|
1622 // Still waiting for the first query packet to be sent. Request a |
|
1623 // resend attempt of the same query again (without counting it as |
|
1624 // retry). This is a wait for getting interfaces up (normal timeouts |
|
1625 // do not apply yet). |
|
1626 |
|
1627 // Reload the query too (this is only sometimes needed with PTR |
|
1628 // queries if the filter iLockId cannot be determined due to |
|
1629 // missing interfaces. And only happens if query address was |
|
1630 // without scope id. |
|
1631 (void)iSession->NewQuery(iResolver->iCurrentQuery(), iResolver->iSourceNow, iResolver->iQueryFlags); |
|
1632 SendDnsQuery(); |
|
1633 return; |
|
1634 } |
|
1635 |
|
1636 if (aTimeOut) |
|
1637 iQueryTimeouts++; // Timeout occurred while resolving the DNS Query |
|
1638 |
|
1639 |
|
1640 // Careful with iStatus: if timeout occurs, it is probably KRequestPending. |
|
1641 // Needs to be changed to something else, if GetNextAnswer is called (otherwise |
|
1642 // it will think that there are pending query and may do nothing!) |
|
1643 // |
|
1644 if (cf.iSprayMode || iResolver->iSourceNow > 0 /* == multicast resolution mode */) |
|
1645 { |
|
1646 if (iWaitingAfterSpray) |
|
1647 { |
|
1648 LOG(Log::Printf(_L("\tresolver[%d] SESSION %d QType=%d (%S) -- Delay after spray completed"), |
|
1649 iResolver->iId, iResolver->iCurrentQuery().iSession, (TInt)iQType, &iName)); |
|
1650 // |
|
1651 // The delay after "spray" has expired, now start a new round (a retry) |
|
1652 // (unless maximum retries has been reached) |
|
1653 // |
|
1654 iWaitingAfterSpray = 0; |
|
1655 iQueryTimeouts = 0; |
|
1656 (void)iSession->PickDefaultServer(); |
|
1657 iStatus = KErrTimedOut; |
|
1658 iServerCount = 1; |
|
1659 ++iQueryRetry; |
|
1660 if (iQueryRetry > iResolver->iQueryStepRetries) |
|
1661 iResolver->GetNextAnswer(); |
|
1662 else |
|
1663 SendDnsQuery(); |
|
1664 } |
|
1665 else if (iSession->PickNewServer()) |
|
1666 { |
|
1667 LOG(Log::Printf(_L("\tresolver[%d] SESSION %d QType=%d (%S) -- More servers to spray"), |
|
1668 iResolver->iId, iResolver->iCurrentQuery().iSession, (TInt)iQType, &iName)); |
|
1669 // |
|
1670 // Yet another server to spray, replicate query |
|
1671 // |
|
1672 iWaitingAfterSpray = 0; |
|
1673 ++iServerCount; |
|
1674 SendDnsQuery(); // Replicate query to another server |
|
1675 } |
|
1676 else if (iQueryTimeouts > 0) |
|
1677 { |
|
1678 // All servers have been sprayed and at least one of them |
|
1679 // has not answered, now wait maxtime - (servers * mintime), |
|
1680 // before starting the next spraying round |
|
1681 // |
|
1682 LOG(Log::Printf(_L("\tresolver[%d] SESSION %d QType=%d (%S) -- All servers sprayed, now wait results"), |
|
1683 iResolver->iId, iResolver->iCurrentQuery().iSession, (TInt)iQType, &iName)); |
|
1684 iWaitingAfterSpray = 1; |
|
1685 const TInt maxtime = Max(iResolver->iQueryStepMinTime, iResolver->iQueryStepMaxTime / (iResolver->iQueryStepRetries+1)); |
|
1686 SetTimer(Max(iResolver->iQueryStepMinTime, maxtime - iResolver->iQueryStepMinTime * iServerCount)); |
|
1687 } |
|
1688 else |
|
1689 { |
|
1690 LOG(Log::Printf(_L("\tresolver[%d] SESSION %d QType=%d (%S) -- All servers replied not found"), |
|
1691 iResolver->iId, iResolver->iCurrentQuery().iSession, (TInt)iQType, &iName)); |
|
1692 // All servers sprayed, and none of the short spray timeouts expired |
|
1693 // (make a heuristic GUESS, that all servers replied, but all were either |
|
1694 // unusable or "skip not found" is active. Just goto to the next step or |
|
1695 // return not found. |
|
1696 iResolver->GetNextAnswer(); |
|
1697 } |
|
1698 } |
|
1699 else |
|
1700 { |
|
1701 ++iQueryRetry; // This is a true retry |
|
1702 if (iQueryRetry > iResolver->iQueryStepRetries || |
|
1703 (server_unusable && !iSession->PickNewServer())) |
|
1704 { |
|
1705 iStatus = KErrTimedOut; |
|
1706 iResolver->GetNextAnswer();// Activates next query step or returns not found. |
|
1707 } |
|
1708 else |
|
1709 { |
|
1710 iResolver->ProbeServers(); // ...keep probing for alternate servers in parallel (at each timeout) |
|
1711 SendDnsQuery(); |
|
1712 } |
|
1713 } |
|
1714 } |
|
1715 |
|
1716 |
|
1717 // TQuerySession::SendDnsQuery |
|
1718 // *************************** |
|
1719 void TQuerySession::SendDnsQuery() |
|
1720 /** |
|
1721 * Activate a new DNS query to be sent. |
|
1722 */ |
|
1723 { |
|
1724 if (iSendingQuery) |
|
1725 { |
|
1726 // Doing everyhing in callbacks forces extra work |
|
1727 // to be done to prevent deep recursion... |
|
1728 LOG(Log::Printf(_L("\tresolver[%d] SESSION %d QType=%d (%S) -- NEW QUERY PENDING"), |
|
1729 iResolver->iId, iResolver->iCurrentQuery().iSession, (TInt)iQType, &iName)); |
|
1730 |
|
1731 iPendingQuery = 1; |
|
1732 return; |
|
1733 } |
|
1734 // Create an UDP message to the DNS |
|
1735 do |
|
1736 { |
|
1737 LOG(Log::Printf(_L("\tresolver[%d] SESSION %d QType=%d (%S) -- SENDING DNS QUERY"), |
|
1738 iResolver->iId, iResolver->iCurrentQuery().iSession, (TInt)iQType, &iName)); |
|
1739 iStatus = KRequestPending; |
|
1740 iSendingQuery = 1; // ..to detect dangerous recursion |
|
1741 iPendingQuery = 0; // ..to detect that a new query should be sent |
|
1742 iWaitingQuerySent = 1; // Mark: waiting for QUERY_SENT |
|
1743 TRAPD(err, (void)iSession->DoQueryL(iResolver->iRequestTime, iQType)); |
|
1744 iSendingQuery = 0; |
|
1745 if (err != KErrNone) |
|
1746 { |
|
1747 // Assume some serious error situation and forget about |
|
1748 // possible pending query, cancel everything with error. |
|
1749 iResolver->QueryDone(err); |
|
1750 return; |
|
1751 } |
|
1752 } |
|
1753 while (iPendingQuery); |
|
1754 |
|
1755 if (iStatus == KRequestPending) |
|
1756 { |
|
1757 const TDndConfigParameters &cf = iResolver->iControl.GetConfig(); |
|
1758 const TUint maxtime = iResolver->iQueryStepMaxTime; |
|
1759 |
|
1760 // Compute the timeout for the next query (starts ticking |
|
1761 // when the packet has been sent (after QUERY_SENT) |
|
1762 // |
|
1763 if (cf.iSprayMode || iResolver->iSourceNow > 0) |
|
1764 iQueryTimeoutValue = iResolver->iQueryStepMinTime; // Send to all servers using mintime |
|
1765 else if (cf.iRetries == 0) |
|
1766 iQueryTimeoutValue = maxtime; // No retries, just single query |
|
1767 else if (iQueryRetry == 0) |
|
1768 iQueryTimeoutValue = iResolver->iQueryStepMinTime; // This is the first send -- use min time |
|
1769 else |
|
1770 { |
|
1771 // This a retransmission (divide the remaining time even with the retries) |
|
1772 iQueryTimeoutValue = Max(iResolver->iQueryStepMinTime, (maxtime - iResolver->iQueryStepMinTime) / iResolver->iQueryStepRetries); |
|
1773 } |
|
1774 LOG(Log::Printf(_L("\tresolver[%d] SESSION %d QType=%d (%S) -- WAITING FOR SEND"), |
|
1775 iResolver->iId, iResolver->iCurrentQuery().iSession, (TInt)iQType, &iName)); |
|
1776 // Setup "poll" timer, if the |
|
1777 // DNS side does not send the QUERY_SENT notification. |
|
1778 // (retry count is not incremented, so it will "poll" |
|
1779 // as long as notification/reply arrives or user cancels |
|
1780 // the resolving). |
|
1781 if (iWaitingQuerySent) |
|
1782 SetTimer(cf.iSetupPoll); |
|
1783 } |
|
1784 else |
|
1785 { |
|
1786 LOG(Log::Printf(_L("\tresolver[%d] SESSION %d QType=%d (%S) -- COMPLETED WITHIN"), |
|
1787 iResolver->iId, iResolver->iCurrentQuery().iSession, (TInt)iQType, &iName)); |
|
1788 } |
|
1789 } |