|
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 // dns.cpp - the main client module of the DNS. |
|
15 // |
|
16 |
|
17 #include <e32math.h> |
|
18 #include <in_sock.h> |
|
19 #include <in6_opt.h> |
|
20 #include "dns.h" |
|
21 #include "dnd.hrh" // EDndDump |
|
22 #include <networking/dnd_err.h> |
|
23 #include "engine.h" |
|
24 #include "servers.h" |
|
25 #include "cache.h" |
|
26 #include "inet6log.h" |
|
27 #ifdef LLMNR_ENABLED |
|
28 # include "llmnrresponder.h" |
|
29 #endif |
|
30 #include "dnd_ini.h" |
|
31 |
|
32 #ifdef EXCLUDE_SYMBIAN_DNS_PUNYCODE |
|
33 #undef SYMBIAN_DNS_PUNYCODE |
|
34 #endif //EXCLUDE_SYMBIAN_DNS_PUNYCODE |
|
35 |
|
36 class CDnsService : public CDndDnsclient |
|
37 { |
|
38 public: |
|
39 CDnsService(CDndEngine &aControl, MDnsServerManager &aServerManager) : CDndDnsclient(aControl, aServerManager) {} |
|
40 void ConstructL(); |
|
41 ~CDnsService(); |
|
42 virtual void ConfigurationChanged(); |
|
43 |
|
44 TInt SetHostName(TUint32 aId, const THostName &aName); |
|
45 TInt GetHostName(TUint32 aId, THostName &aName, MDnsResolver &aCallback); |
|
46 |
|
47 TInt GetByAddress(TUint32 aId, const TInetAddr &aAddr, TInt aNext, THostName &aName); |
|
48 TInt GetByName(TUint32 aId, const THostName &aName, TInt aNext, TInetAddr &aAddr); |
|
49 private: |
|
50 #ifdef LLMNR_ENABLED |
|
51 CDndLlmnrResponder *iLlmnrResponder; //< Pointer to the LLMNR responder object |
|
52 #endif |
|
53 TUint32 GetNetId(TUint32 aIndex); |
|
54 TInetAddressInfo *GetAddressList(TInt &aN); |
|
55 }; |
|
56 |
|
57 |
|
58 void CDnsService::ConstructL() |
|
59 { |
|
60 CDndDnsclient::ConstructL(); |
|
61 #ifdef LLMNR_ENABLED |
|
62 iLlmnrResponder = new (ELeave) CDndLlmnrResponder(iControl, iServerManager, iHostNames); |
|
63 iLlmnrResponder->ConstructL(); |
|
64 #endif |
|
65 } |
|
66 |
|
67 CDnsService::~CDnsService() |
|
68 { |
|
69 #ifdef LLMNR_ENABLED |
|
70 delete iLlmnrResponder; |
|
71 #endif |
|
72 } |
|
73 |
|
74 |
|
75 void CDnsService::ConfigurationChanged() |
|
76 { |
|
77 #ifdef LLMNR_ENABLED |
|
78 iLlmnrResponder->ConfigurationChanged(); |
|
79 #endif |
|
80 } |
|
81 // |
|
82 // Gateway hostname processing to the responder |
|
83 // |
|
84 TInt CDnsService::SetHostName(TUint32 aId, const THostName &aName) |
|
85 { |
|
86 const TInt res = iHostNames.Map(aId, aName); |
|
87 #ifdef LLMNR_ENABLED |
|
88 return res == KErrNone ? iLlmnrResponder->SetHostName(aId, iHostNames.Find(aId)) : res; |
|
89 #else |
|
90 return res; |
|
91 #endif |
|
92 } |
|
93 |
|
94 TInt CDnsService::GetHostName(TUint32 aId, THostName &aName, MDnsResolver &aCallback) |
|
95 { |
|
96 aName = iHostNames.Find(aId); |
|
97 #ifdef LLMNR_ENABLED |
|
98 return iLlmnrResponder->GetHostName(aId, aCallback); |
|
99 #else |
|
100 (void)aCallback; |
|
101 return KErrNone; |
|
102 #endif |
|
103 } |
|
104 |
|
105 |
|
106 |
|
107 TUint32 CDnsService::GetNetId(TUint32 aIndex) |
|
108 { |
|
109 TPckgBuf<TSoInetIfQuery> opt; |
|
110 opt().iIndex = aIndex; |
|
111 return iControl.iSocket.GetOpt(KSoInetIfQueryByIndex, KSolInetIfQuery, opt) == KErrNone |
|
112 ? opt().iZone[15] : 0; |
|
113 } |
|
114 |
|
115 TInetAddressInfo *CDnsService::GetAddressList(TInt &aN) |
|
116 /** |
|
117 * Returns a list of all current addresses. |
|
118 * |
|
119 * @retval aN The number of addresses (if return is non-NULL). |
|
120 * @return The address list, or NULL if not available. |
|
121 * |
|
122 * The returned address list must be freed by the caller |
|
123 * as "delete[] list". |
|
124 */ |
|
125 { |
|
126 TPtr8 empty(NULL, 0); |
|
127 aN = iControl.iSocket.GetOpt(KSoInetAddressInfo, KSolInetIfQuery, empty); |
|
128 if (aN <= 0) |
|
129 return NULL; |
|
130 TInetAddressInfo *p = new TInetAddressInfo[aN]; |
|
131 if (p == NULL) |
|
132 return NULL; // No memory available. |
|
133 TPtr8 opt((TUint8 *)p, aN*sizeof(TInetAddressInfo)); |
|
134 aN = iControl.iSocket.GetOpt(KSoInetAddressInfo, KSolInetIfQuery, opt); |
|
135 if (aN < 0) |
|
136 aN = 0; |
|
137 else |
|
138 aN = opt.Length() / (TInt)sizeof(TInetAddressInfo); |
|
139 return p; |
|
140 } |
|
141 |
|
142 |
|
143 TInt CDnsService::GetByAddress(TUint32 /*aId*/, const TInetAddr &aAddr, TInt /*aNext*/, THostName &aName) |
|
144 /** |
|
145 * Returns the local hostname, if address is my own. |
|
146 */ |
|
147 { |
|
148 TPckgBuf<TSoInetIfQuery> opt; |
|
149 opt().iSrcAddr = aAddr; |
|
150 if (iControl.iSocket.GetOpt(KSoInetIfQueryBySrcAddr, KSolInetIfQuery, opt) != KErrNone) |
|
151 return KErrNotFound; |
|
152 aName = iHostNames.Find(opt().iZone[15]); // Locate hostname for network id. |
|
153 return (aName.Length() > 0) ? KErrNone : KErrNotFound; |
|
154 } |
|
155 |
|
156 TInt CDnsService::GetByName(TUint32 aId, const THostName &aName, TInt /*aNext*/, TInetAddr &aAddr) |
|
157 /** |
|
158 * Returns one of my own addresses, if the hostname is local. |
|
159 */ |
|
160 { |
|
161 const TDesC &name = iHostNames.Find(aId); |
|
162 if (aName.Length() > 0 && name.CompareF(aName) != 0) |
|
163 return KErrNotFound; // The query name is not a local host. |
|
164 // |
|
165 // Locate a local addres from some interface within the specific network. |
|
166 // |
|
167 TInt N; |
|
168 TInetAddressInfo *list = GetAddressList(N); |
|
169 if (list == NULL) |
|
170 return KErrNotFound; |
|
171 |
|
172 TUint32 iface = 0; |
|
173 TUint32 netid = 0; |
|
174 TInt scope = -1; |
|
175 TInt best = 0; |
|
176 for (TInt i = 0; i < N; ++i) |
|
177 { |
|
178 if (list[i].iState != TInetAddressInfo::EAssigned) |
|
179 continue; |
|
180 if (list[i].iInterface != iface) |
|
181 { |
|
182 iface = list[i].iInterface; |
|
183 netid = GetNetId(iface); |
|
184 } |
|
185 if (netid != aId) |
|
186 continue; // Not this address. |
|
187 if (list[i].iAddress.Scope() > scope) |
|
188 { |
|
189 scope = list[i].iAddress.Scope(); |
|
190 best = i; |
|
191 } |
|
192 } |
|
193 aAddr.SetAddress(list[best].iAddress); |
|
194 aAddr.SetScope(list[best].iScopeId); |
|
195 delete[] list; |
|
196 return scope < 0 ? KErrNotFound : KErrNone; |
|
197 } |
|
198 |
|
199 // |
|
200 // |
|
201 |
|
202 CDndDnsclient *CDndDnsclient::NewL(CDndEngine &aControl, MDnsServerManager &aServerManager) |
|
203 { |
|
204 LOG(Log::Printf(_L("CDndDnsclient::NewL()"))); |
|
205 |
|
206 CDnsService *const dns = new (ELeave) CDnsService(aControl, aServerManager); |
|
207 CleanupStack::PushL(dns); |
|
208 dns->ConstructL(); |
|
209 CleanupStack::Pop(); |
|
210 return dns; |
|
211 } |
|
212 |
|
213 CDndDnsclient::CDndDnsclient(CDndEngine &aControl, MDnsServerManager &aServerManager) |
|
214 : CDnsSocket(aControl.GetConfig().iEDNS0), iControl(aControl), iServerManager(aServerManager) |
|
215 { |
|
216 } |
|
217 |
|
218 void CDndDnsclient::ConstructL() |
|
219 { |
|
220 LOG(Log::Printf(_L("CDndDnsclient::ConstructL() size=%d"), (TInt)sizeof(*this))); |
|
221 CDnsSocket::ConstructL(); |
|
222 |
|
223 iCache = new (ELeave) CDndCache(); |
|
224 iCache->ConstructL(); |
|
225 |
|
226 TPtrC localhost; |
|
227 if(iControl.FindVar(DND_INI_HOST, DND_INI_HOSTNAME, localhost)) |
|
228 (void)iHostNames.Reset(localhost); |
|
229 else |
|
230 (void)iHostNames.Reset(KDndIni_Hostname); |
|
231 } |
|
232 |
|
233 |
|
234 |
|
235 |
|
236 void CDndDnsclient::HandleCommandL(TInt aCommand) |
|
237 { |
|
238 #ifdef _LOG |
|
239 switch(aCommand) |
|
240 { |
|
241 case EDndDump: |
|
242 if (iCache) |
|
243 iCache->Dump(iControl); |
|
244 break; |
|
245 default: |
|
246 break; |
|
247 } |
|
248 #endif |
|
249 if (aCommand == EDndFlush) |
|
250 iCache->Flush(); |
|
251 } |
|
252 |
|
253 CDndDnsclient::~CDndDnsclient() |
|
254 { |
|
255 DeactivateSocket(); |
|
256 |
|
257 // Just to be sure, cancel server list notifys (if any) |
|
258 // (this means that SERVER MANAGER MUST NOT BE DELETED |
|
259 // before this object!) |
|
260 for (TUint i = 0; i < KDndNumRequests; i++) |
|
261 iServerManager.CloseList(iDndReqData[i].iFilter); |
|
262 delete iCache; |
|
263 } |
|
264 |
|
265 // CDndDnsclient::CheckAddress |
|
266 // *************************** |
|
267 TInt CDndDnsclient::CheckAddress(const TInetAddr &aDestination) |
|
268 { |
|
269 TPckgBuf<TSoInetIfQuery> opt; |
|
270 opt().iDstAddr = aDestination; |
|
271 |
|
272 const TInt err = iControl.iSocket.GetOpt(KSoInetIfQueryByDstAddr, KSolInetIfQuery, opt); |
|
273 if (err == KErrNone && TInetAddr::Cast(opt().iSrcAddr).IsUnspecified()) |
|
274 return KErrNotFound; // No route or source address. |
|
275 return err; |
|
276 } |
|
277 |
|
278 |
|
279 |
|
280 // TDndReqData::PickDefaultServer |
|
281 // ******************************** |
|
282 TBool TDndReqData::PickDefaultServer() |
|
283 { |
|
284 TInetAddr tmp; |
|
285 TBool ret = FALSE; |
|
286 |
|
287 if (iCurrentServer == 0) |
|
288 ret = iOwner->iServerManager.OpenList(iFilter, this) > 0; |
|
289 iOwner->iCache->GetServerAddress(iOwner->iServerManager.NameSpace(iFilter, iCurrentServer), tmp); |
|
290 iFilter.iServerId = iOwner->iServerManager.ServerId(tmp); |
|
291 iCurrentServer = iOwner->iServerManager.Next(iFilter, 0); |
|
292 #ifdef _LOG |
|
293 if (iCurrentServer) |
|
294 Log::Printf(_L("\t\tDNS session [%u] default nameserver (id=%d) assigned"), (TInt)this, (TInt)iCurrentServer); |
|
295 else |
|
296 Log::Printf(_L("\t\tDNS session [%u] no nameservers available for this session"), (TInt)this); |
|
297 #endif |
|
298 return ret || iCurrentServer != 0; |
|
299 } |
|
300 |
|
301 |
|
302 // TDndReqData::PickNewServer |
|
303 // **************************** |
|
304 TBool TDndReqData::PickNewServer() |
|
305 { |
|
306 if (iCurrentServer == 0) |
|
307 iOwner->iServerManager.BuildServerList(); |
|
308 iCurrentServer = iOwner->iServerManager.Next(iFilter, iCurrentServer); |
|
309 #ifdef _LOG |
|
310 if (iCurrentServer) |
|
311 Log::Printf(_L("\t\tDNS session [%u] next nameserver (id=%d) assigned"), (TInt)this, (TInt)iCurrentServer); |
|
312 else |
|
313 Log::Printf(_L("\t\tDNS session [%u] next, no alternate nameservers to assign"), (TInt)this); |
|
314 #endif |
|
315 return iCurrentServer != 0; |
|
316 } |
|
317 |
|
318 void TDndReqData::ServerListComplete(const TDnsServerFilter &aFilter, TInt aResult) |
|
319 { |
|
320 if (&aFilter == &iFilter) |
|
321 { |
|
322 iCurrentServer = iOwner->iServerManager.Next(iFilter, 0); |
|
323 if (iIsReqPending) |
|
324 { |
|
325 if (aResult == KErrNone) |
|
326 { |
|
327 if (PickDefaultServer()) |
|
328 { |
|
329 iOwner->ReSend(*this); |
|
330 aResult = KDnsNotify_HAVE_SERVERLIST; |
|
331 } |
|
332 else |
|
333 aResult = KErrDndServerUnusable; |
|
334 } |
|
335 SendResponse(aResult); |
|
336 } |
|
337 } |
|
338 } |
|
339 |
|
340 |
|
341 // CDndDnsclient::OpenSession |
|
342 // ************************** |
|
343 /** |
|
344 // A session is internally represented by a TDndReqData. Find an unused |
|
345 // entry and assign it. Unused entry is recognized by it having no |
|
346 // callback function (= NULL). |
|
347 */ |
|
348 MDnsSession *CDndDnsclient::OpenSession(MDnsResolver *const aCallback) |
|
349 { |
|
350 TUint i; |
|
351 // |
|
352 // Locate available TDndReqData entry... |
|
353 // |
|
354 for (i = 0;; ++i) |
|
355 { |
|
356 if (i >= KDndNumRequests) |
|
357 return NULL; // No "sessions" available |
|
358 // For now, assume the slot is available, if no callback has been installed |
|
359 if (iDndReqData[i].iCallback == NULL) |
|
360 break; |
|
361 } |
|
362 TDndReqData &rq = iDndReqData[i]; |
|
363 rq.iCallback = aCallback; |
|
364 rq.iOwner = this; |
|
365 iActivityCount++; // Count each open session as "activity". |
|
366 return &rq; |
|
367 } |
|
368 |
|
369 |
|
370 void CDndDnsclient::Error(const TInetAddr &aServer, TInt /*aError*/) |
|
371 { |
|
372 const TUint server_id = iServerManager.ServerId(aServer); |
|
373 if (server_id == 0) |
|
374 return; // -- nothing to do, address not registered as a server |
|
375 |
|
376 // |
|
377 // Try to notify all sessions currently using this server |
|
378 // |
|
379 for (TUint i = 0; i < KDndNumRequests; ++i) |
|
380 { |
|
381 TDndReqData &rq = iDndReqData[i]; |
|
382 // For now, assume the slot is in used, if callback has been installed |
|
383 if (rq.iCallback == NULL) |
|
384 continue; |
|
385 if (rq.iCurrentServer == server_id) |
|
386 rq.SendResponse(KErrDndServerUnusable); |
|
387 } |
|
388 } |
|
389 |
|
390 |
|
391 |
|
392 // TDndReqData::Close |
|
393 // ****************** |
|
394 void TDndReqData::Close() |
|
395 { |
|
396 CancelQuery(); |
|
397 iCallback = NULL; |
|
398 if (iRecord) |
|
399 { |
|
400 iRecord->Unlock(this); |
|
401 iRecord = NULL; |
|
402 } |
|
403 iOwner->iServerManager.CloseList(iFilter); |
|
404 // If this was the last active session, then |
|
405 // cancel all requests |
|
406 ASSERT(iOwner->iActivityCount > 0); |
|
407 if (--iOwner->iActivityCount == 0) |
|
408 iOwner->DeactivateSocket(); |
|
409 } |
|
410 |
|
411 // TDndReqData::NewQuery |
|
412 // *********************** |
|
413 TInt TDndReqData::NewQuery(const TDnsMessage &aQuery, TDnsServerScope aServerScope, TUint32 aFlags) |
|
414 { |
|
415 iIsReqPending = FALSE; |
|
416 iIsNewQuery = TRUE; |
|
417 iIsUsingTCP = 0; |
|
418 iQdCount = 1; |
|
419 iFlags = aFlags; |
|
420 iFilter.iServerScope = aServerScope; |
|
421 iFilter.iServerId = 0; |
|
422 iNetworkId = aQuery.iId; // Get the networkId information from the Query. |
|
423 |
|
424 #ifdef SYMBIAN_DNS_PUNYCODE |
|
425 if( (aQuery.iScope & 0x80) == 0x80 ) |
|
426 { |
|
427 iIdnEnabled = 1; |
|
428 iQuestion.EnableIdn(ETrue); |
|
429 } |
|
430 else |
|
431 { |
|
432 iIdnEnabled = 0; |
|
433 iQuestion.EnableIdn(EFalse); |
|
434 } |
|
435 #endif //SYMBIAN_DNS_PUNYCODE |
|
436 |
|
437 switch (aQuery.iType) |
|
438 { |
|
439 case KDnsRequestType_GetByName: |
|
440 case KDnsRequestType_GetByAddress: |
|
441 { |
|
442 const TNameRecord &query = aQuery.NameRecord(); |
|
443 |
|
444 if (aQuery.iType == KDnsRequestType_GetByName) |
|
445 { |
|
446 iFilter.iLockId = aQuery.iId; |
|
447 iFilter.iLockType = KIp6AddrScopeNetwork; |
|
448 #ifdef SYMBIAN_DNS_PUNYCODE |
|
449 TInt err = iQuestion.SetName(query.iName); |
|
450 if( err != KErrNone) |
|
451 { |
|
452 return err; |
|
453 } |
|
454 #else |
|
455 iQuestion.SetName(query.iName); |
|
456 #endif // SYMBIAN_DNS_PUNYCODE |
|
457 } |
|
458 else |
|
459 { |
|
460 iOwner->iServerManager.LockByAddress(TInetAddr::Cast(query.iAddr), aQuery.iId, iFilter); |
|
461 #ifdef SYMBIAN_DNS_PUNYCODE |
|
462 TInt err = iQuestion.SetName(TInetAddr::Cast(query.iAddr)); |
|
463 if( err != KErrNone) |
|
464 { |
|
465 return err; |
|
466 } |
|
467 #else |
|
468 iQuestion.SetName(TInetAddr::Cast(query.iAddr)); |
|
469 #endif // SYMBIAN_DNS_PUNYCODE |
|
470 } |
|
471 } |
|
472 break; |
|
473 case KDnsRequestType_TDnsQuery: |
|
474 { |
|
475 const TDnsQuery &query = aQuery.Query(); |
|
476 iQuestion.Copy(query.Data()); |
|
477 iFilter.iLockId = aQuery.iId; |
|
478 iFilter.iLockType = KIp6AddrScopeNetwork; |
|
479 } |
|
480 break; |
|
481 default: |
|
482 return KErrNotSupported; |
|
483 } |
|
484 iCurrentServer = 0; |
|
485 // |
|
486 // For forward query, the Scope identifier of the aQuery.iAddr is the network id |
|
487 // For pointer query, if the address is not of network scope, then need to find |
|
488 // the network id based on the scope id... (fix later). -- msa |
|
489 |
|
490 iOpcode = EDnsOpcode_QUERY; // Only EStandard supported! (EInverse is not same as PTR query!!!) |
|
491 if (iRecord) |
|
492 { |
|
493 iRecord->Unlock(this); |
|
494 iRecord = NULL; |
|
495 } |
|
496 return KErrNone; |
|
497 } |
|
498 |
|
499 |
|
500 // TDndReqData::CancelQuery |
|
501 // ************************** |
|
502 void TDndReqData::CancelQuery() |
|
503 { |
|
504 iIsReqPending = FALSE; |
|
505 Cancel(); |
|
506 iOwner->iServerManager.CloseList(iFilter); |
|
507 } |
|
508 |
|
509 |
|
510 // TDndReqData::DoNext |
|
511 // ********************* |
|
512 /** |
|
513 // @retval aReply returns the requested Resource Record value, if KErrNone return |
|
514 // @param aNext the index of the value to be returned. 0 is the index of the first value |
|
515 // @returns |
|
516 // @li KErrNotFound, if there is no value at specified index |
|
517 // @li KErrDndCache, if the reply from DNS stored in cache was corrupt |
|
518 // @li KErrNone, if value successfully returned |
|
519 */ |
|
520 TInt TDndReqData::DoNext(TDnsMessageBuf &aReply, TInt aNext) const |
|
521 { |
|
522 if (iRecord == NULL) |
|
523 return KErrNotFound; |
|
524 TInt ret = iRecord->ErrorCode(); |
|
525 if (ret < 0) |
|
526 return ret; // No usable record present |
|
527 |
|
528 TDndRR tempRR(iRecord->Reply()); |
|
529 #ifdef SYMBIAN_DNS_PUNYCODE |
|
530 tempRR.iIdnEnabled = TBool(iIdnEnabled); |
|
531 #endif //SYMBIAN_DNS_PUNYCODE |
|
532 |
|
533 const TInet6HeaderDNS &hdr = iRecord->Header(); |
|
534 TInt answerCount = hdr.ANCOUNT(); |
|
535 |
|
536 ret = tempRR.FindRR(iRecord->AnswerOffset(), answerCount, iQuestion.QType(), iQuestion.QClass(), aNext); |
|
537 if (ret < 0) |
|
538 { |
|
539 if (ret == KErrDndCache) |
|
540 iRecord->Invalidate(); |
|
541 return ret; |
|
542 } |
|
543 |
|
544 TInetAddr *addr = NULL; |
|
545 |
|
546 switch (aReply().iType) |
|
547 { |
|
548 case KDnsRequestType_GetByName: |
|
549 case KDnsRequestType_GetByAddress: |
|
550 { |
|
551 TNameRecord &reply = aReply().NameRecord(); |
|
552 aReply.SetLength(aReply().HeaderSize() + sizeof(reply)); |
|
553 if (hdr.AA()) |
|
554 reply.iFlags |= EDnsAuthoritive; |
|
555 ret = GetResponse(tempRR, reply); |
|
556 reply.iFlags |= EDnsServer | (iIsFromCache ? EDnsCache : 0); |
|
557 addr = &TInetAddr::Cast(reply.iAddr); |
|
558 break; |
|
559 } |
|
560 #ifndef NO_DNS_QUERY_SUPPORT |
|
561 case KDnsRequestType_TDnsQuery: |
|
562 ret = tempRR.GetResponse(aReply, &addr); |
|
563 break; |
|
564 #endif |
|
565 default: |
|
566 return KErrNotSupported; |
|
567 } |
|
568 if (ret < 0) |
|
569 return ret; // Some error detected! |
|
570 |
|
571 if (addr) |
|
572 { |
|
573 // Reply contains a TInetAddr. This needs some special processing |
|
574 |
|
575 // Supplement IPv6 addresses with the scope value |
|
576 // (Should do the same with IPv4 after converting to IPv4 mapped?) |
|
577 if (addr->Family() == KAfInet) |
|
578 addr->ConvertToV4Mapped(); |
|
579 if (addr->Family() == KAfInet6) |
|
580 { |
|
581 const TInt scope_level = addr->Ip6Address().Scope(); |
|
582 |
|
583 // Add the scope id only for addresses with larger than node |
|
584 // local scope (this leaves the scope id as zero, if a loopback |
|
585 // address is returned from the name server (The id of the node |
|
586 // local scope is the interface index and non-zero value would |
|
587 // bind loopback destination to real interface instead of internal |
|
588 // loopback interface). |
|
589 if (scope_level > KIp6AddrScopeNodeLocal) |
|
590 addr->SetScope(iOwner->iServerManager.Scope(iRecord->Server(), *addr)); |
|
591 |
|
592 #ifdef LLMNR_ENABLED |
|
593 if(iOwner->iControl.GetConfig().iLlmnrLlOnly) // accept only linklocal replies to LLMNR queries |
|
594 if (iFilter.iServerScope == EDnsServerScope_MC_LOCAL) |
|
595 { |
|
596 // Check compliance w. link-local addressing requirements (ipv6/ipv4) |
|
597 if (scope_level != KIp6AddrScopeLinkLocal) |
|
598 return KErrNotFound; |
|
599 } |
|
600 #endif |
|
601 |
|
602 // |
|
603 // A backward compatibility hack: if address is IPv4 global address |
|
604 // and the network scope in the query matches the scope of the address, |
|
605 // then convert the returned address into old KAfInet format (and lose |
|
606 // lose the scope id). |
|
607 if (addr->IsV4Mapped() && |
|
608 addr->Scope() == aReply().iId && |
|
609 scope_level == KIp6AddrScopeNetwork) |
|
610 addr->SetAddress(addr->Address()); |
|
611 } |
|
612 } |
|
613 return KErrNone; |
|
614 } |
|
615 |
|
616 // TDndReqData::DoError |
|
617 // ********************** |
|
618 /** |
|
619 // If a session has a DNS reply in cache associated with it, then |
|
620 // set the state of this reply to indicated error code. |
|
621 // |
|
622 // @param aError the error code to be stored |
|
623 // @param aTLL the new "Time To Live" for the record (in seconds). The |
|
624 // record (and error state) will expire after this time. |
|
625 */ |
|
626 void TDndReqData::DoError(TInt aError, TUint aTTL) |
|
627 { |
|
628 if (aError >= 0) |
|
629 return; // Only errors can be set! |
|
630 |
|
631 if (iRecord == NULL) |
|
632 return; |
|
633 iRecord->FillErrorCode(aError, aTTL); |
|
634 } |
|
635 |
|
636 // TDndReqData::DoQueryL |
|
637 // *********************** |
|
638 /** |
|
639 // Activate a query of specified type for the loaded query information |
|
640 // (enable RecvReply callback). Note: the callback may occur already within call! |
|
641 // |
|
642 // @param aRequestTime is the time when the request is received from the application |
|
643 // @param aQType type of the query (all queries assume IN class) |
|
644 // @returns |
|
645 // @li < 0, serious error, (resolving process should be aborted) |
|
646 // @li = 0, reply found from cache (reply callback has been called) |
|
647 // @li = 1, DNS Query message has been queued for transmission |
|
648 // @execption |
|
649 // LEAVE on any serious error (resolving process should be aborted) |
|
650 */ |
|
651 TInt TDndReqData::DoQueryL(const TTime &aRequestTime, const EDnsQType aQType) |
|
652 { |
|
653 // -- class is now always IN, |
|
654 // -- should only look from cache if aQType is not a "wildcard" type |
|
655 // (however, if wildcard, cache should not give hits...) |
|
656 iQuestion.SetQType(aQType); |
|
657 // -- only IN class queries are supported |
|
658 iQuestion.SetQClass(EDnsQClass_IN); |
|
659 |
|
660 if (iRecord) |
|
661 { |
|
662 iRecord->Unlock(this); |
|
663 iRecord = NULL; |
|
664 } |
|
665 |
|
666 if (iCurrentServer == 0) |
|
667 { |
|
668 if (!PickDefaultServer()) |
|
669 { |
|
670 SendResponse(KErrDndServerUnusable); |
|
671 return 0; |
|
672 } |
|
673 if (iCurrentServer == 0) |
|
674 { |
|
675 // Cannot check cache nor start any query, if the |
|
676 // name space id cannot be determined (no interfaces |
|
677 // up). Return "query queued" anyway, and let the |
|
678 // resolver retry process try it again later. |
|
679 iIsReqPending = TRUE; |
|
680 return 1; |
|
681 } |
|
682 } |
|
683 TUint32 id = iOwner->iServerManager.NameSpace(iFilter, iCurrentServer); |
|
684 |
|
685 if (id == 0) |
|
686 { |
|
687 SendResponse(KErrDndServerUnusable); |
|
688 return 0; |
|
689 } |
|
690 |
|
691 // Check in the cache first. If the record does not exist |
|
692 // in the cache, it will be created now (empty with KErrNotFound). |
|
693 iRecord = iOwner->iCache->FindL( |
|
694 id, |
|
695 iQuestion, |
|
696 iQuestion.QType(), |
|
697 // *HACK WARNING* To achieve independent caching of answers which |
|
698 // are result of queries where RD=1 or RD=0 (recursion desired), |
|
699 // the Class value is made different depending on the RD state. |
|
700 (EDnsQClass)(iQuestion.QClass() | ((iFlags & KDnsModifier_RD) ? 0 : 0x80)), |
|
701 aRequestTime); |
|
702 // The above FindL must either leave of return a valid |
|
703 // record pointer. Just as a safety measure, if record |
|
704 // is not returned, then panic on in DEBUG builds, and |
|
705 // in release, leave with KErrDndNoRecord |
|
706 // (** however, this should never happen! **) |
|
707 ASSERT(iRecord != NULL); |
|
708 if (iRecord == NULL) |
|
709 User::Leave(KErrDndNoRecord); |
|
710 |
|
711 // Prevent record from being deleted while the |
|
712 // iRecord pointer exists... |
|
713 iRecord->Lock(); |
|
714 switch(iRecord->ErrorCode()) |
|
715 { |
|
716 // If no error in the record, retrieve the informations and send |
|
717 case KErrNone: |
|
718 // For certain errors, send the error code |
|
719 case KErrDndBadName: |
|
720 case KErrDndNotImplemented: |
|
721 case KErrDndRefused: |
|
722 case KErrDndNoRecord: |
|
723 iIsFromCache = TRUE; |
|
724 iIsNewQuery = FALSE; |
|
725 if (IsQueued()) |
|
726 Cancel(); |
|
727 SendResponse(iRecord->ErrorCode()); |
|
728 return 0; |
|
729 |
|
730 // For other errors, try sending the query to the DNS |
|
731 default: |
|
732 break; |
|
733 } |
|
734 |
|
735 // Automatic restart of DNS socket, if closed for some reason |
|
736 iOwner->ActivateSocketL(iNetworkId); // pass on the networkId information |
|
737 |
|
738 iIsReqPending = TRUE; |
|
739 // If the query is probe, do not AssignWork but continue.. |
|
740 if (!(iFlags & KDnsModifier_PQ) && !iRecord->AssignWork(this)) |
|
741 return 1; // just let the other worker do the job. |
|
742 |
|
743 // Try to detect retransmissions of the same query and |
|
744 // reuse old ID in such case... (don't generate a new) |
|
745 if (iIsNewQuery || |
|
746 iQuestion.QType() != aQType || |
|
747 iQuestion.QClass() != EDnsQClass_IN) |
|
748 Cancel(); // A new ID required |
|
749 |
|
750 iIsNewQuery = FALSE; |
|
751 iIsFromCache = FALSE; |
|
752 |
|
753 // If the DNS "mode" is Multicast DNS, then assume that PTR queries |
|
754 // which are generated from IP address are to be made via TCP. Test |
|
755 // this and use TCP if query is PTR query for valid IP address. |
|
756 // [specified in draft-ietf-dnsext-mdns-22.txt, but generalized here |
|
757 // also for any future Multicast DNS] |
|
758 for ( ;iFilter.IsMulticast() && iQuestion.QType() == EDnsQType_PTR;) |
|
759 { |
|
760 // Use "for" just for easy "break" exits! |
|
761 TInetAddr server; |
|
762 |
|
763 // Get correct DNS port number into 'server' (actual address is thrown away) |
|
764 if (iOwner->iServerManager.Address(iCurrentServer, server) != KErrNone) |
|
765 break; |
|
766 // Use server address type as a flag, whether Ipv4 or IPv6 is done |
|
767 const TInt is_ipv4 = server.IsV4Mapped(); |
|
768 |
|
769 if (!iQuestion.GetAddress(server) || !server.IsUnicast()) |
|
770 break; |
|
771 // Usually LLMNR has both IPv4 and IPv6 multicast addresses as "servers". |
|
772 // There is no point in doing PTR query for the same address twice, thus |
|
773 // only do it once per matching "server" address (thus, if there is no |
|
774 // IPv4 multicast "server", no IPv4 reverse queries are done either). |
|
775 if (is_ipv4 != server.IsV4Mapped()) |
|
776 { |
|
777 SendResponse(KErrDndServerUnusable); |
|
778 return 0; |
|
779 } |
|
780 |
|
781 // Supplement 'server' address with a scope id based on current server |
|
782 server.SetScope(iOwner->iServerManager.Scope(iCurrentServer, server)); |
|
783 |
|
784 // For link local multicast, use TTL = 1 (otherwise, system default is used) |
|
785 const TInt ttl = iFilter.iServerScope == EDnsServerScope_MC_LOCAL ? 1 : -1; |
|
786 if (iOwner->Queue(*this, server, -1, ttl) != KErrNone) |
|
787 break; |
|
788 iIsUsingTCP = 1; |
|
789 SendResponse(KDnsNotify_USING_TCP); |
|
790 return 1; |
|
791 } |
|
792 iOwner->ReSend(*this); // (uses old ID, if request queued already) |
|
793 return 1; |
|
794 } |
|
795 |
|
796 // TDndReqData::UpdateCacheData |
|
797 // ****************************** |
|
798 /** |
|
799 // @param aQuery the session from which the reply is updated |
|
800 // @param aMsg the reply from the DNS server |
|
801 // @param aAnswerOffset the start offset to the andwer secion in the reply |
|
802 // @param aErr the status code of the reply |
|
803 // @returns |
|
804 // @li TRUE, if cache record updated |
|
805 // @li FALSE, if not updated (error code is "transient", concerns single query) |
|
806 */ |
|
807 TBool TDndReqData::UpdateCacheData(const TMsgBuf &aMsg, const TInt aAnswerOffset, const TInt aErr) |
|
808 { |
|
809 if (iRecord == NULL) |
|
810 return FALSE; |
|
811 |
|
812 #ifdef _LOG |
|
813 THostName name; |
|
814 iQuestion.GetName(name); |
|
815 Log::Printf(_L("\t\tDNS session [%u] -- Update cache: %S (offset=%d) aErr=%d"), (TInt)this, &name, aAnswerOffset, aErr); |
|
816 #endif |
|
817 if (aErr == KErrDndDiscard) |
|
818 return FALSE; |
|
819 |
|
820 // If there is already have valid answer cached, decide whether the new |
|
821 // answer is better and should replace the old? |
|
822 const TInet6HeaderDNS &new_hdr = aMsg.Header(); |
|
823 if (iRecord->ErrorCode() == KErrNone && !new_hdr.AA()) |
|
824 { |
|
825 // If new header is not authoritative, then it will replace the value |
|
826 // only if old is non-authoritative and there is no error. |
|
827 const TInet6HeaderDNS &old_hdr = iRecord->Header(); |
|
828 if (old_hdr.AA() || aErr != KErrNone) |
|
829 return FALSE; // Not updated, keep previous valid value in cache. |
|
830 } |
|
831 |
|
832 TBool updated = FALSE; |
|
833 if (aErr == KErrNone || aErr == KErrDndBadName || aErr == KErrDndNotImplemented || aErr == KErrDndNoRecord) |
|
834 { |
|
835 // Try to locate the SOA record from the authority section and |
|
836 // use the minttl as the ttl of the negative caching. |
|
837 TInt ttl; |
|
838 TDndRR soa(aMsg); |
|
839 #ifdef SYMBIAN_DNS_PUNYCODE |
|
840 soa.iIdnEnabled = (TBool) iIdnEnabled; |
|
841 #endif //SYMBIAN_DNS_PUNYCODE |
|
842 TInt off = soa.LocateRR(aAnswerOffset, new_hdr.ANCOUNT()+new_hdr.NSCOUNT(), EDnsQType_SOA, EDnsQClass_IN, new_hdr.ANCOUNT()); |
|
843 if (off < 0) |
|
844 { |
|
845 ttl = iOwner->iControl.GetConfig().iMaxTime; |
|
846 LOG(Log::Printf(_L("\t\tDNS session [%u] -- Update cache: no SOA, default ttl = %d"), (TInt)this, ttl)); |
|
847 } |
|
848 else if ((off = aMsg.SkipName(soa.iRd)) > 0 && |
|
849 (off = aMsg.SkipName(off)) > 0 && |
|
850 off >= (TInt)soa.iRd && |
|
851 off + 20 <= (TInt)(soa.iRd + soa.iRdLength)) |
|
852 { |
|
853 ttl = BigEndian::Get32(aMsg.Ptr()+off+16); |
|
854 LOG(Log::Printf(_L("\t\tDNS session [%u] -- Update cache: SOA minttl = %d"), (TInt)this, ttl)); |
|
855 } |
|
856 else |
|
857 { |
|
858 ttl = 0; // The reply is broken in some way, use ttl = 0 |
|
859 LOG(Log::Printf(_L("\t\tDNS session [%u] -- Update cache: SOA access failed"), (TInt)this)); |
|
860 } |
|
861 iRecord->FillData(aMsg, aAnswerOffset, iCurrentServer, aErr, ttl); |
|
862 #ifdef SYMBIAN_DNS_PUNYCODE |
|
863 |
|
864 LOG(iRecord->Print(iOwner->iControl,(TBool)iIdnEnabled)); |
|
865 #else |
|
866 LOG(iRecord->Print(iOwner->iControl)); |
|
867 #endif //SYMBIAN_DNS_PUNYCODE |
|
868 updated = TRUE; |
|
869 } |
|
870 // |
|
871 // Got some answer from a server, update the "good" server (unless error indicates "bad" |
|
872 // |
|
873 if (iFilter.IsUnicast() && aErr != KErrDndRefused && aErr != KErrDndServerUnusable) |
|
874 { |
|
875 TInetAddr addr; |
|
876 if (iOwner->iServerManager.Address(iCurrentServer, addr) == KErrNone) |
|
877 { |
|
878 #ifdef _LOG |
|
879 // borrow the 'name' from earlier LOG section! |
|
880 addr.OutputWithScope(name); |
|
881 Log::Printf(_L("\t\tDNS session [%u] -- Update cache: preferred server = %S port=%d ns=%d"), |
|
882 (TInt)this, &name, addr.Port(), iOwner->iServerManager.NameSpace(iFilter, iCurrentServer)); |
|
883 #endif |
|
884 iOwner->iCache->SetServerAddress(iOwner->iServerManager.NameSpace(iFilter, iCurrentServer), addr, KErrNone); |
|
885 } |
|
886 } |
|
887 |
|
888 return updated; |
|
889 } |
|
890 |
|
891 |
|
892 void CDndDnsclient::QueryBegin() |
|
893 { |
|
894 ++iActivityCount; |
|
895 } |
|
896 |
|
897 |
|
898 void CDndDnsclient::QueryEnd() |
|
899 { |
|
900 ASSERT(iActivityCount > 0); |
|
901 if (--iActivityCount == 0) |
|
902 DeactivateSocket(); |
|
903 } |
|
904 |
|
905 |
|
906 // |
|
907 // TDndReqData |
|
908 // |
|
909 |
|
910 // TDndReqData::TranslateRCODE |
|
911 // *************************** |
|
912 /** |
|
913 // |
|
914 // @param aHdr The fixed DNS reply header |
|
915 // |
|
916 // @returns |
|
917 // @li KErrNone, if reply is ok |
|
918 // @li KErrDndDiscard/KErrDndUnknown |
|
919 // if reply does not match the query, has errors |
|
920 // in the format or RCODE was unknown |
|
921 // @li KErrDndFormat, RCODE was EDnsRcode_FORMAT_ERROR |
|
922 // @li KErrDndServerFailure, RCODE was EDnsRcode_SERVER_FAILURE |
|
923 // @li KErrDndBadName, RCODE was EDnsRcode_NAME_ERROR |
|
924 // @li KErrDndNotImplemented, RCODE was EDnsRcode_NOT_IMPLEMENTED |
|
925 // @li KErrDndRefuced, RCODE was EDnsRcode_REFUSED |
|
926 // @li KErrDndServerUnusable, if empty reply is not authoritative |
|
927 */ |
|
928 TInt TDndReqData::TranslateRCODE(const TDndHeader &aHdr, TInt aRCode) const |
|
929 { |
|
930 switch (aRCode) |
|
931 { |
|
932 case EDnsRcode_NOERROR: |
|
933 break; |
|
934 case EDnsRcode_FORMAT_ERROR: |
|
935 return KErrDndFormat; |
|
936 case EDnsRcode_SERVER_FAILURE: |
|
937 return KErrDndServerFailure; |
|
938 case EDnsRcode_NAME_ERROR: |
|
939 return KErrDndBadName; |
|
940 case EDnsRcode_NOT_IMPLEMENTED: |
|
941 return KErrDndNotImplemented; |
|
942 case EDnsRcode_REFUSED: |
|
943 return KErrDndRefused; |
|
944 default: |
|
945 return KErrDndUnknown; |
|
946 } |
|
947 |
|
948 if (iOpcode == EDnsOpcode_QUERY && aHdr.QDCOUNT() != iQdCount) |
|
949 return KErrDndDiscard; |
|
950 // |
|
951 // A special heuristics: discard empty replies, if the selected server does |
|
952 // not do recursion as requested, and if it is not authority on the queried |
|
953 // name. => return a special "server unusable for this query" error |
|
954 if (aHdr.ANCOUNT() == 0 && (iFlags & KDnsModifier_RD) != 0 && !aHdr.RA() && !aHdr.AA()) |
|
955 return KErrDndServerUnusable; |
|
956 return KErrNone; |
|
957 } |
|
958 |
|
959 // TDndReqData::CheckQuestion |
|
960 // ************************** |
|
961 /** |
|
962 // @param aOffset starting offset of the question section in the message |
|
963 // @param aMsg the reply message from a DNS server |
|
964 // |
|
965 // @returns |
|
966 // @li > 0, |
|
967 // if message checks ok, the value is the new offset pointing |
|
968 // to the next section after the question. |
|
969 // @li = 0, reply does not match the question. |
|
970 // @li < 0, bad DNS reply |
|
971 */ |
|
972 TInt TDndReqData::CheckQuestion(const TMsgBuf &aMsg, TInt &aRCode) const |
|
973 { |
|
974 if (!iIsReqPending) |
|
975 return 0; // Not for me. |
|
976 |
|
977 TDndQuestion question; |
|
978 const TInt offset = aMsg.VerifyMessage(aRCode, question); |
|
979 if (offset < 0) |
|
980 return offset; // Invalid message format, just ignore. |
|
981 const TDndHeader &hdr = aMsg.Header(); |
|
982 |
|
983 // Only ONE question supported, sematics of receiving a reply |
|
984 // with more than one Question are hairy... [which answers |
|
985 // relate to which question?] (and multiple questions are |
|
986 // not supported by current servers anyway -- msa) |
|
987 if (hdr.QDCOUNT() != 1) |
|
988 return KErrDndUnknown; |
|
989 |
|
990 // This is supposed to be a REPLY, if not, then "no match". |
|
991 if (!hdr.QR()) |
|
992 return 0; |
|
993 // Reply OPCODE match the query? |
|
994 if (hdr.OPCODE() != iOpcode) |
|
995 return KErrDndDiscard; |
|
996 |
|
997 // Does the question match the query? |
|
998 // |
|
999 if (question.CheckQuestion(iQuestion) != KErrNone) |
|
1000 return 0; |
|
1001 return offset; |
|
1002 } |
|
1003 |
|
1004 |
|
1005 // TDndReqData::GetResponse |
|
1006 // ************************ |
|
1007 /** |
|
1008 // Map the contents of single resource record into TNameRecord. |
|
1009 // |
|
1010 // @param aRR the resource from which the reply is extracted |
|
1011 // @retval aAnswer receives the extracted value |
|
1012 // @returns |
|
1013 // @li KErrNone, if extraction successful |
|
1014 // @li KErrDndNameTooBig, if answer cannot be fit into aAnswer |
|
1015 // @li and other errors |
|
1016 */ |
|
1017 TInt TDndReqData::GetResponse(const TDndRR &aRR, TNameRecord &aAnswer) const |
|
1018 { |
|
1019 TInt err = aRR.GetResponse(aAnswer.iName, TInetAddr::Cast(aAnswer.iAddr)); |
|
1020 if (err != KErrNone) |
|
1021 return err; |
|
1022 aAnswer.iFlags |= (aRR.iType == EDnsType_CNAME) ? (EDnsAlias | EDnsServer) : EDnsServer; |
|
1023 return KErrNone; |
|
1024 } |
|
1025 |
|
1026 |
|
1027 // TDndReqData::SendResponce |
|
1028 // ************************* |
|
1029 /** |
|
1030 // @param aErr is the status, |
|
1031 // @li = 0, the request has completed successfully |
|
1032 // @li > 0, request being processed, just a progress noticification |
|
1033 // @li < 0, the request has completed with an error |
|
1034 */ |
|
1035 void TDndReqData::SendResponse(TInt aErr) |
|
1036 { |
|
1037 if (aErr <= 0) |
|
1038 { |
|
1039 iIsReqPending = FALSE; // Current request completed |
|
1040 Cancel(); |
|
1041 } |
|
1042 if (iCallback) |
|
1043 iCallback->ReplyCallback(aErr); |
|
1044 } |
|
1045 |
|
1046 // TDndReqData::Build |
|
1047 // ****************** |
|
1048 /** |
|
1049 // @retval aMsg |
|
1050 // contains the fully constructed message to be sent to the DNS server, |
|
1051 // if Build succeeds |
|
1052 // @retval aServer |
|
1053 // contains the server address for which the message should be sent |
|
1054 // @param aMaxMessage |
|
1055 // the size of the current receive buffer (if UDP, zero for TCP) |
|
1056 // |
|
1057 // @returns TRUE, successful Build, and error (< 0) otherwise |
|
1058 */ |
|
1059 TBool TDndReqData::Build(CDnsSocket &aSource, TMsgBuf &aMsg, TInetAddr &aServer, TInt aMaxMessage) |
|
1060 { |
|
1061 CDndDnsclient &dns = (CDndDnsclient &)aSource; |
|
1062 |
|
1063 if (dns.iServerManager.Address(iCurrentServer, aServer) != KErrNone) |
|
1064 return 0; |
|
1065 ASSERT(aServer.Port() != 0); |
|
1066 |
|
1067 aMsg.SetLength(sizeof(TDndHeader)); |
|
1068 TDndHeader &hdr = (TDndHeader &)aMsg.Header(); |
|
1069 if (aServer.IsMulticast()) |
|
1070 { |
|
1071 ASSERT(iFilter.IsMulticast()); |
|
1072 hdr.SetRD(0); |
|
1073 if (aServer.IsV4Mapped()) |
|
1074 { |
|
1075 if(iQuestion.QType() == EDnsQType_AAAA) |
|
1076 { |
|
1077 SendResponse(KErrDndServerUnusable); |
|
1078 return 0; |
|
1079 } |
|
1080 } |
|
1081 else |
|
1082 { |
|
1083 |
|
1084 if(iQuestion.QType() == EDnsQType_A) |
|
1085 { |
|
1086 SendResponse(KErrDndServerUnusable); |
|
1087 return 0; |
|
1088 } |
|
1089 } |
|
1090 #ifdef LLMNR_ENABLED |
|
1091 dns.SetHoplimit(dns.iControl.GetConfig().iLlmnrHoplimit); |
|
1092 #endif |
|
1093 } |
|
1094 else |
|
1095 { |
|
1096 #ifdef LLMNR_ENABLED |
|
1097 dns.SetHoplimit(-1); |
|
1098 #endif |
|
1099 hdr.SetRD(iFlags & KDnsModifier_RD); |
|
1100 } |
|
1101 hdr.SetQdCount(1); |
|
1102 if (iQuestion.Append(aMsg) < 0) |
|
1103 return 0; |
|
1104 |
|
1105 // Assume EDNS0 enabled, if the current receive buffer |
|
1106 // is larger than KDnsMaxMessage (all smaller |
|
1107 // values are treated as "no EDNS0". |
|
1108 if (aMaxMessage > KDnsMaxMessage) |
|
1109 { |
|
1110 TDndRROut opt(aMsg); |
|
1111 #ifdef SYMBIAN_DNS_PUNYCODE |
|
1112 opt.iIdnEnabled = (TBool) iIdnEnabled; |
|
1113 #endif //SYMBIAN_DNS_PUNYCODE |
|
1114 opt.iType = (TUint16)EDnsQType_OPT; |
|
1115 opt.iClass = (TUint16)aMaxMessage; |
|
1116 opt.iTTL = 0; // RCODE = 0, version = 0, Z = 0 |
|
1117 if (opt.Append(KNullDesC8, 0) == KErrNone) |
|
1118 { |
|
1119 hdr.SetArCount(1); |
|
1120 opt.AppendRData(); |
|
1121 } |
|
1122 } |
|
1123 return 1; |
|
1124 } |
|
1125 |
|
1126 |
|
1127 void TDndReqData::Sent(CDnsSocket &aSource) |
|
1128 { |
|
1129 CDndDnsclient &dns = (CDndDnsclient &)aSource; |
|
1130 |
|
1131 dns.iServerManager.CloseList(iFilter); |
|
1132 |
|
1133 SendResponse(KDnsNotify_QUERY_SENT); |
|
1134 } |
|
1135 |
|
1136 |
|
1137 TBool TDndReqData::Reply(CDnsSocket &aSource, const TMsgBuf &aBuf, const TInetAddr &aServer) |
|
1138 { |
|
1139 TInt rcode = 0; |
|
1140 const TInt offset = CheckQuestion(aBuf, rcode); |
|
1141 if (offset < 0) |
|
1142 return 1; // Invalid message format, just ignore. |
|
1143 const TDndHeader &hdr = aBuf.Header(); |
|
1144 TInt err = TranslateRCODE(hdr, rcode); |
|
1145 |
|
1146 CDndDnsclient &dns = (CDndDnsclient &)aSource; |
|
1147 ASSERT(&dns == iOwner); |
|
1148 // If configuration requests that "Not found" replies from a server |
|
1149 // are not to be cached, but ignored, then substitue err with |
|
1150 // KErrDndServerUnusable (meaning that this server is not usable for |
|
1151 // this query) This error is not cached! |
|
1152 // |
|
1153 if (err == KErrDndBadName && dns.iControl.GetConfig().iSkipNotFound) |
|
1154 err = KErrDndServerUnusable; |
|
1155 else if (iIsUsingTCP == 0 && hdr.TC() != 0) |
|
1156 { |
|
1157 // The current query was not TCP and got truncated reply, |
|
1158 // restart query with TCP (if we get truncated reply with |
|
1159 // TCP, something is broken...) |
|
1160 // |
|
1161 |
|
1162 // It is assumed that the aServer has the correct port |
|
1163 // already set (it should be the remote port of the original |
|
1164 // UDP query) |
|
1165 ASSERT(aServer.IsUnicast()); // Should always be true. |
|
1166 if (aServer.IsUnicast()) |
|
1167 { |
|
1168 if (iOwner->Queue(*this, aServer, hdr.ID()) == KErrNone) |
|
1169 { |
|
1170 iIsUsingTCP = 1; |
|
1171 SendResponse(KDnsNotify_USING_TCP); |
|
1172 return 1; |
|
1173 } |
|
1174 } |
|
1175 // If cannot use the TCP, then just use the trunctated |
|
1176 // answer as is... |
|
1177 } |
|
1178 // |
|
1179 // UpdateCacheData updates data in cache only, if err is KErrNone, or |
|
1180 // updates error status for some specific err codes, |
|
1181 // otherwise, it does nothing. |
|
1182 TBool updated = UpdateCacheData(aBuf, offset, err); |
|
1183 |
|
1184 #ifdef DEBUG_CACHE |
|
1185 iCache->Dump(*iControl); |
|
1186 #endif |
|
1187 |
|
1188 if (updated) |
|
1189 { |
|
1190 // Cache modified! In addition to the original query, |
|
1191 // send the responce to every request that is waiting for |
|
1192 // the same reply... (iRecord pointers are same). |
|
1193 for (TUint i = 0; i < KDndNumRequests; ++i) |
|
1194 { |
|
1195 TDndReqData &rq = dns.iDndReqData[i]; |
|
1196 if (rq.iIsReqPending && iRecord == rq.iRecord) |
|
1197 { |
|
1198 rq.Cancel(); // No need to send the query |
|
1199 rq.SendResponse(err); |
|
1200 } |
|
1201 } |
|
1202 return 1; |
|
1203 } |
|
1204 |
|
1205 // Cache not updated, send to original query only. |
|
1206 SendResponse(err); |
|
1207 return 1; |
|
1208 } |
|
1209 |
|
1210 void TDndReqData::Abort(CDnsSocket &/*aSource*/, const TInt aReason) |
|
1211 { |
|
1212 SendResponse(aReason); |
|
1213 } |