|
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 // llmnrresponder.cpp - DND Link-local Multicast Name Resolution |
|
15 // responder module |
|
16 // |
|
17 |
|
18 #ifdef LLMNR_ENABLED |
|
19 #include <e32math.h> |
|
20 #include <in_iface.h> |
|
21 #include <in6_if.h> |
|
22 #include "engine.h" |
|
23 #include "llmnrresponder.h" |
|
24 #include "llmnrconflict.h" |
|
25 #include <networking/dnd_err.h> |
|
26 #include "serverconf.h" |
|
27 #include "inet6log.h" |
|
28 |
|
29 |
|
30 enum TEntryState |
|
31 { |
|
32 EInactive = 0, //< Entry is not yet available |
|
33 EUnique = 1, //< Entry is unique and alive |
|
34 EDisabled = -1, //< Entry is faulty, disabled |
|
35 EConflict = -2 //< Entry has conflict with another host |
|
36 }; |
|
37 |
|
38 class CNotifyConflict : public CActive |
|
39 /** |
|
40 Exists while a conflict notifier process is active. |
|
41 */ |
|
42 { |
|
43 private: |
|
44 CNotifyConflict(CDndLlmnrResponder &aResponder, CLlmnrEntry &aEntry); |
|
45 void DoStart(); |
|
46 public: |
|
47 ~CNotifyConflict(); |
|
48 static void Start(CDndLlmnrResponder &aResponder, CLlmnrEntry &aEntry); |
|
49 |
|
50 void DoCancel(); |
|
51 void RunL(); |
|
52 |
|
53 CDndLlmnrResponder &iResponder; |
|
54 CLlmnrEntry &iEntry; |
|
55 TPckgBuf<TLlmnrConflictNotify> iInfo; |
|
56 RNotifier iNotifier; |
|
57 TUint iConnected:1; |
|
58 }; |
|
59 |
|
60 class CLlmnrEntry : public CBase |
|
61 /** |
|
62 Describes an answer (RR) that can be asked from this node. |
|
63 |
|
64 An instance of this object is created for each unique answer |
|
65 which this node is responsible for at this point of time. |
|
66 Each entry describes an answer for a specific interface. |
|
67 If the same name is defined for multiple interfaces, |
|
68 then multiple instances are created |
|
69 (one for each interface). |
|
70 |
|
71 These entries are created and deleted dynamically, as |
|
72 interfaces go up and down. |
|
73 */ |
|
74 { |
|
75 public: |
|
76 ~CLlmnrEntry(); |
|
77 |
|
78 CLlmnrEntry *iNext; //< Links the entry set together (head is CDndLlmnrResponder::iEntryList) |
|
79 TDndQuestion iQuestion; //< The question that is answered by this entry |
|
80 TInetScopeIds iZone; //< The scope vector of the related interface. |
|
81 TIpVer iVersion:8; //< Which protocol: IPv4 or IPv6 |
|
82 TEntryState iState:8; //< State of this entry. |
|
83 TUint iDown:1; //< Used in interface tracking |
|
84 TUint iHostName:1; //< Set 1, if the name depends on the local hostname. |
|
85 CNotifyConflict *iNotify; //< Non-NULL when conlict nofitication is in progress |
|
86 }; |
|
87 |
|
88 |
|
89 |
|
90 // TRawAddr |
|
91 // ********* |
|
92 // Lightweight internal help class for handling the link layer addresses |
|
93 // (modified from ipadm engine.cpp) |
|
94 class TRawAddr : public TSockAddr |
|
95 { |
|
96 public: |
|
97 |
|
98 TInt Append(TDes8& aBuf) const |
|
99 { |
|
100 TUint8* p = (TUint8*)UserPtr(); |
|
101 TInt len = ((TSockAddr *)this)->GetUserLen(); |
|
102 |
|
103 if (len == 0) |
|
104 return KErrNotSupported; |
|
105 if (aBuf.MaxLength() < aBuf.Length() + len * 2) |
|
106 return KErrNoMemory; |
|
107 |
|
108 while (--len >= 0) |
|
109 aBuf.AppendFormat(_L8("%02X"), *p++ & 0xFF); |
|
110 |
|
111 return KErrNone; |
|
112 } |
|
113 |
|
114 inline static TRawAddr& Cast(const TSockAddr& aAddr) |
|
115 { |
|
116 return *((TRawAddr *)&aAddr); |
|
117 } |
|
118 }; |
|
119 |
|
120 |
|
121 CLlmnrEntry::~CLlmnrEntry() |
|
122 { |
|
123 delete iNotify; |
|
124 } |
|
125 |
|
126 |
|
127 CDndLlmnrResponder::CDndLlmnrResponder(CDndEngine &aControl, |
|
128 MDnsServerManager &aServerManager, |
|
129 THostNames &aHostNames) |
|
130 : iControl(aControl), |
|
131 iServerManager(aServerManager), |
|
132 iHostNames(aHostNames) |
|
133 { |
|
134 |
|
135 // Need to initialize the request blocks with "parent" pointer |
|
136 for (TUint i = 0; i < sizeof(iLlmnrDataList) / sizeof(iLlmnrDataList[0]); ++i) |
|
137 iLlmnrDataList[i].iParent = this; |
|
138 } |
|
139 |
|
140 CDndLlmnrResponder::~CDndLlmnrResponder() |
|
141 { |
|
142 // Make sure all requests are totally idle |
|
143 for (TUint i = 0; i < sizeof(iLlmnrDataList) / sizeof(iLlmnrDataList[0]); ++i) |
|
144 { |
|
145 iLlmnrDataList[i].iTimeout.Cancel(); |
|
146 iLlmnrDataList[i].Cancel(); |
|
147 } |
|
148 while (iEntryList) |
|
149 { |
|
150 CLlmnrEntry *p = iEntryList; |
|
151 iEntryList = p->iNext; |
|
152 delete p; |
|
153 } |
|
154 delete iLlmnrNotifyHandler; |
|
155 delete iLlmnrConf; |
|
156 } |
|
157 |
|
158 void CDndLlmnrResponder::ConstructL() |
|
159 { |
|
160 LOG(Log::Printf(_L("CDndLlmnrResponder::ConstructL() size=%d\r\n"), (TInt)sizeof(*this))); |
|
161 CDnsSocket::ConstructL (); |
|
162 iLlmnrConf = new (ELeave) CLlmnrConf(iControl); |
|
163 iLlmnrConf->ConstructL(); |
|
164 iLlmnrConf->GetHostNamesL(); |
|
165 |
|
166 iLlmnrNotifyHandler = new (ELeave) CDndLlmnrNotifyHandler(*this); |
|
167 iLlmnrNotifyHandler->ConstructL(); |
|
168 } |
|
169 |
|
170 void CDndLlmnrResponder::ConfigurationChanged() |
|
171 { |
|
172 iLlmnrNotifyHandler->ConfigurationChanged(); |
|
173 } |
|
174 |
|
175 void CDndLlmnrResponder::ActivateSocketL() |
|
176 { |
|
177 if (iConnected && IsOpened()) |
|
178 return; // Assume all done |
|
179 |
|
180 const TDndConfigParameters &cf = iControl.GetConfig(); |
|
181 const TInetAddr bind(TInetAddr(cf.iLlmnrPort)); |
|
182 |
|
183 CDnsSocket::ActivateSocketL(bind); |
|
184 |
|
185 // Disable multicast loopback to lessen the possibility to receive our own replies |
|
186 (void)Socket().SetOpt(KSoIp6MulticastLoop, KSolInetIp, 0); |
|
187 // Set Unicast and Multicast TTL/Hop limit to 1 for LLMNR responses |
|
188 (void)Socket().SetOpt(KSoIp6UnicastHops, KSolInetIp, cf.iLlmnrHoplimit); |
|
189 (void)Socket().SetOpt(KSoIp6MulticastHops, KSolInetIp, cf.iLlmnrHoplimit); |
|
190 (void)Socket().SetOpt(KSoUserSocket, KSolInetIp, 0); |
|
191 // THe LLMNR responder socket does not need to keep the interfaces up. |
|
192 (void)Socket().SetOpt(KSoKeepInterfaceUp, KSolInetIp, 0); |
|
193 ActivateListenL(bind, 1); |
|
194 } |
|
195 |
|
196 // Join multicast group to LLMNR responder iSocket |
|
197 TInt CDndLlmnrResponder::JoinMulticastGroup(TUint32 aIndex, TIpVer aIpVer) |
|
198 { |
|
199 |
|
200 TPckgBuf<TIp6Mreq> opt; |
|
201 |
|
202 TRAPD(err, ActivateSocketL()); // Need socket opened... |
|
203 if (err != KErrNone) |
|
204 return err; |
|
205 |
|
206 if(aIpVer == EIPv4) |
|
207 opt().iAddr = iControl.GetConfig().iLlmnrIpv4; |
|
208 else |
|
209 opt().iAddr = iControl.GetConfig().iLlmnrIpv6; |
|
210 opt().iInterface = aIndex; |
|
211 |
|
212 err=Socket().SetOpt(KSoIp6JoinGroup, KSolInetIp, opt); |
|
213 |
|
214 if ((err != KErrNone)&&(err != KErrInUse)) // may return KErrInUse when multicast hook is on |
|
215 { |
|
216 LOG(Log::Printf(_L("CDndLlmnrResponder::JoinMulticastGroup SetOpt KSoIp6JoinGroup if=%d error: %d"), aIndex, err)); |
|
217 return err; |
|
218 } |
|
219 |
|
220 return KErrNone; |
|
221 } |
|
222 |
|
223 |
|
224 TInt CDndLlmnrResponder::MakeMyAddr(const TUint32 aIfIndex, const TInetAddr &aTarget, EDnsType aType, TInetAddr &aAddr) |
|
225 { |
|
226 const EDnsType target_type = (aTarget.Family() == KAfInet || aTarget.IsV4Mapped()) ? EDnsType_A : EDnsType_AAAA; |
|
227 |
|
228 TPckgBuf<TSoInetIfQuery> opt; |
|
229 opt().iIndex = aIfIndex; |
|
230 |
|
231 for (;;) |
|
232 { |
|
233 TInt err; |
|
234 |
|
235 if (aType == target_type) |
|
236 { |
|
237 // The target address matches the address type being queried, |
|
238 // try obtaining the "best address" that matches the target. |
|
239 opt().iDstAddr = aTarget; |
|
240 if ((err = iControl.iSocket.GetOpt(KSoInetIfQueryByIndex, KSolInetIfQuery, opt)) != KErrNone) |
|
241 return err; |
|
242 if (!opt().iSrcAddr.IsUnspecified()) |
|
243 break; // Success! |
|
244 } |
|
245 |
|
246 // Try finding an acceptable link local or any higher scope address |
|
247 |
|
248 if (aType == EDnsType_A) |
|
249 opt().iDstAddr.SetV4MappedAddress(KInetAddrLinkLocalNet); |
|
250 else if (aType == EDnsType_AAAA) |
|
251 opt().iDstAddr.SetAddress(KInet6AddrLinkLocal); |
|
252 else |
|
253 return KErrNotSupported; |
|
254 |
|
255 if ((err = iControl.iSocket.GetOpt(KSoInetIfQueryByIndex, KSolInetIfQuery, opt)) != KErrNone) |
|
256 { |
|
257 LOG(Log::Printf(_L("CDndLlmnrResponder::MakeMyAddr GetOpt KSoInetIfQueryByIndex error: %d"),err)); |
|
258 return err; |
|
259 } |
|
260 break; |
|
261 // *NEVER GET HERE!* |
|
262 } |
|
263 // |
|
264 // Address is unspecified or some valid source address |
|
265 // |
|
266 aAddr = opt().iSrcAddr; |
|
267 return KErrNone; |
|
268 } |
|
269 |
|
270 |
|
271 #ifdef _LOG |
|
272 void CDndLlmnrResponder::LogPrint(const TDesC &aStr, const CLlmnrEntry &aEntry) |
|
273 { |
|
274 TBuf<KDnsMaxName> name; |
|
275 |
|
276 aEntry.iQuestion.GetName(name); |
|
277 |
|
278 Log::Printf(_L("%S%S QType=%d State=%d ip%d for if=%u\r\n"), |
|
279 &aStr, |
|
280 &name, (TInt)aEntry.iQuestion.QType(), |
|
281 (TInt)aEntry.iState, |
|
282 (TInt)aEntry.iVersion, |
|
283 (TInt)aEntry.iZone[0]); |
|
284 } |
|
285 |
|
286 void CDndLlmnrResponder::LogPrint(const TDesC &aStr, const TDndQuestion &aQuestion) |
|
287 { |
|
288 TBuf<KDnsMaxName> name; |
|
289 |
|
290 aQuestion.GetName(name); |
|
291 |
|
292 Log::Printf(_L("%S%S QType=%d\r\n"), |
|
293 &aStr, |
|
294 &name, (TInt)aQuestion.QType()); |
|
295 } |
|
296 #endif |
|
297 |
|
298 // Mark all entries as "unupdated" |
|
299 void CDndLlmnrResponder::UpdateStart() |
|
300 { |
|
301 for(CLlmnrEntry *entry = iEntryList; entry != NULL; entry = entry->iNext) |
|
302 entry->iDown = 1; |
|
303 } |
|
304 |
|
305 // Remove all entries that were not updated (interface has gone down) |
|
306 void CDndLlmnrResponder::UpdateFinish() |
|
307 { |
|
308 for(CLlmnrEntry **h = &iEntryList;;) |
|
309 { |
|
310 CLlmnrEntry *entry = *h; |
|
311 if (entry == NULL) |
|
312 break; // -- all done! |
|
313 if (entry->iDown || |
|
314 JoinMulticastGroup(entry->iZone[0], STATIC_CAST(TIpVer, entry->iVersion)) != KErrNone) |
|
315 { |
|
316 // |
|
317 // Remove entry. Either interface is down (not exist) or |
|
318 // it does not support multicast, in which case it cannot |
|
319 // be used for LLMNR. |
|
320 // |
|
321 *h = entry->iNext; |
|
322 // |
|
323 // If entry has any associated requests outstanding, |
|
324 // they must be cancelled. |
|
325 CancelAll(*entry); |
|
326 LOG(LogPrint(_L("\tLLMNR removed: "), *entry)); |
|
327 delete entry; |
|
328 } |
|
329 else |
|
330 { |
|
331 // |
|
332 // Entry is still active and usable |
|
333 // |
|
334 h = &entry->iNext; |
|
335 continue; |
|
336 } |
|
337 } |
|
338 if (iEntryList == NULL) |
|
339 { |
|
340 // Nothing enabled for LLMNR, no need to keep the socket either |
|
341 DeactivateSocket(); |
|
342 LOG(Log::Printf(_L("CDndLlmnrResponder::UpdateFinish - no entries, socket deactivated\r\n"))); |
|
343 } |
|
344 } |
|
345 |
|
346 TInt CDndLlmnrResponder::UpdateInterface |
|
347 (const TName &aIfName, const TIpVer aIpVer, const TInetScopeIds &aZone, const TSockAddr &aHwAddr, TInt aLlmnrDisable) |
|
348 /** |
|
349 * Update the LLMNR entries for the specified interface. |
|
350 * |
|
351 * @param aIfName The interface name |
|
352 * @parma aIpVer The IP version (either EIPv4 or EIPv6) |
|
353 * @param aZone The zone ids |
|
354 * @param aHwAddr The hardware address (if present) |
|
355 * @param aLlmnrDisable =1, if LLMNR is to be disabled on this interface |
|
356 */ |
|
357 { |
|
358 |
|
359 // Check that this is not a loopback interface |
|
360 if(!aZone[1]) |
|
361 return KErrNone; |
|
362 |
|
363 // An interface is open. Check if it is LLMNR enabled and if update(s) |
|
364 // have not been sent or disabled |
|
365 |
|
366 TInt is_active = 0; |
|
367 for(CLlmnrEntry *entry = iEntryList; entry != NULL; entry = entry->iNext) |
|
368 { |
|
369 if (entry->iZone[0] == aZone[0]) |
|
370 { |
|
371 is_active = 1; |
|
372 // If LLMNR is to be disable, this keeps iDown as 1 and |
|
373 // the entry will be deleted in UpdateFinish. |
|
374 entry->iDown = aLlmnrDisable; |
|
375 // |
|
376 // Copy the scope vector each time, in case it has been modified |
|
377 // |
|
378 for (TUint k = 0; k < sizeof(TInetScopeIds) / sizeof(entry->iZone[0]); k++) |
|
379 entry->iZone[k] = aZone[k]; |
|
380 } |
|
381 } |
|
382 if (is_active || aLlmnrDisable) |
|
383 return KErrNone; // Interface already activated or being disabled, nothing else to do. |
|
384 |
|
385 // Interface is not yet LLMNR enabled, Check if there |
|
386 // are hostnames enabled that match this interface |
|
387 |
|
388 const EDnsQType qt = aIpVer == EIPv4 ? EDnsQType_A : aIpVer == EIPv6 ? EDnsQType_AAAA : EDnsQType(0); |
|
389 |
|
390 const TInt N = iLlmnrConf->iHostList->Count(); |
|
391 for (TInt i = 0; i < N; i++) |
|
392 { |
|
393 const THostNameEntry &host = iLlmnrConf->iHostList->At(i); |
|
394 |
|
395 if (host.iVersion != EIPany && host.iVersion != aIpVer) |
|
396 continue; // Does not apply to this entry, go look next |
|
397 if (host.iIfName.Length() > 0 && host.iIfName.Compare(aIfName) != 0) |
|
398 continue; // Entry is for a specific interface (not this), go look next |
|
399 // |
|
400 // Build a new LLMNR entry |
|
401 // |
|
402 TInetAddr addr(iControl.GetConfig().iLlmnrPort); |
|
403 // Give name and address to the server manager |
|
404 if(aIpVer == EIPv4) |
|
405 addr.SetAddress(iControl.GetConfig().iLlmnrIpv4); |
|
406 else |
|
407 addr.SetAddress(iControl.GetConfig().iLlmnrIpv6); |
|
408 iServerManager.AddServerAddress(aIfName, addr); |
|
409 |
|
410 // Make an entry to the list |
|
411 CLlmnrEntry *entry = new CLlmnrEntry(); |
|
412 if (entry == NULL) |
|
413 break; // Ooops, out of memory... |
|
414 entry->iNext = iEntryList; |
|
415 iEntryList = entry; |
|
416 |
|
417 entry->iQuestion.SetName(host.iName); |
|
418 entry->iQuestion.SetQType(qt); |
|
419 entry->iQuestion.SetQClass(EDnsQClass_IN); |
|
420 entry->iVersion = aIpVer; |
|
421 // |
|
422 // Copy the scope vector |
|
423 // |
|
424 for (TUint k = 0; k < sizeof(TInetScopeIds) / sizeof(entry->iZone[0]); k++) |
|
425 entry->iZone[k] = aZone[k]; |
|
426 |
|
427 entry->iState = EInactive; |
|
428 if(FormatHostName(*entry, aHwAddr) == KErrNone) |
|
429 (void)DoUpdate(*entry); |
|
430 else |
|
431 entry->iState = EDisabled; |
|
432 LOG(LogPrint(_L("\tLLMNR Created: "), *entry)); |
|
433 } |
|
434 return KErrNone; |
|
435 } |
|
436 |
|
437 TInt CDndLlmnrResponder::FormatHostName(CLlmnrEntry &aEntry, const TSockAddr &aHwAddr) |
|
438 { |
|
439 TInt i; |
|
440 aEntry.iHostName = 0; |
|
441 if((i = aEntry.iQuestion.Find(FORMATHWADDR)) != KErrNotFound) |
|
442 { // hostname contains hardware address format string |
|
443 if(aHwAddr.Family() == KAFUnspec) |
|
444 { |
|
445 LOG(Log::Printf(_L("CDndLlmnrResponder::FormatHostName - no hw address\r\n"))); |
|
446 return KErrNotFound; |
|
447 }; |
|
448 |
|
449 // Insert hw address into hostname |
|
450 // (We assume, that there is only one hw address insert in the hostname) |
|
451 TDndName tmpstr; |
|
452 tmpstr.Append(aEntry.iQuestion.Left(i)); |
|
453 TInt err; |
|
454 if((err = TRawAddr::Cast(aHwAddr).Append(tmpstr)) != KErrNone) |
|
455 return err; |
|
456 tmpstr.Append(aEntry.iQuestion.Mid(i + FORMATHWADDR().Length())); |
|
457 (TDndName &)aEntry.iQuestion = tmpstr; |
|
458 return KErrNone; |
|
459 } |
|
460 else if((i = aEntry.iQuestion.Compare(WILDCARDPRIMARYHOSTNAME)) == 0) |
|
461 { |
|
462 // Entry requests use of local hostname. Find a matching local name |
|
463 // based on the network id. |
|
464 aEntry.iHostName = 1; |
|
465 const TUint32 nid = aEntry.iZone[KIp6AddrScopeNetwork-1]; |
|
466 (void)iHostNames.Refresh(nid); // Reread hostname from Comms Database. |
|
467 aEntry.iQuestion.SetName(iHostNames.Find(nid)); |
|
468 return aEntry.iQuestion.Length() > 0 ? KErrNone : KErrNotFound; |
|
469 } |
|
470 else |
|
471 return KErrNone; // No format string in hostname |
|
472 } |
|
473 |
|
474 |
|
475 void CDndLlmnrResponder::CancelAll(const CLlmnrEntry &aEntry) |
|
476 { |
|
477 |
|
478 // Abort all associated requests, if any |
|
479 |
|
480 for (TInt session = 0; session < KLlmnrMaxSessions; ++session) |
|
481 if(iLlmnrDataList[session].iLlmnrEntry == &aEntry) |
|
482 { |
|
483 iLlmnrDataList[session].iTimeout.Cancel(); |
|
484 iLlmnrDataList[session].Cancel(); |
|
485 iLlmnrDataList[session].iLlmnrEntry = NULL; |
|
486 } |
|
487 } |
|
488 |
|
489 |
|
490 TInt CDndLlmnrResponder::DoUpdate(CLlmnrEntry &aEntry) |
|
491 { |
|
492 if (JoinMulticastGroup(aEntry.iZone[0], STATIC_CAST(TIpVer, aEntry.iVersion)) != KErrNone) |
|
493 { |
|
494 // The interface does not support multicast, disable entry. |
|
495 LOG(LogPrint(_L("CDndLlmnrResponder::DoUpdate() No multicast, disable: "), aEntry)); |
|
496 aEntry.iState = EDisabled; |
|
497 return KErrNotSupported; |
|
498 } |
|
499 |
|
500 TInt session = 0; |
|
501 for(session=0; ; session++) |
|
502 { |
|
503 if(session == KLlmnrMaxSessions) |
|
504 { |
|
505 LOG(Log::Printf(_L("CDndLlmnrResponder::DoUpdate - No free slots in iLlmnrDataList"))); |
|
506 return KErrNoMemory; |
|
507 } |
|
508 if(!iLlmnrDataList[session].iLlmnrEntry) |
|
509 break; |
|
510 } |
|
511 TLlmnrMsgData &upd = iLlmnrDataList[session]; |
|
512 upd.iLlmnrEntry = &aEntry; |
|
513 |
|
514 // Do Unique check by sending a query for the name, and if nobody answers within |
|
515 // specified time, assume the name is valid to use for this node. |
|
516 upd.iQuestion = aEntry.iQuestion; |
|
517 upd.iQR = 0; |
|
518 upd.iAA = 0; |
|
519 upd.iRCode = EDnsRcode_NOERROR; |
|
520 if (aEntry.iVersion == EIPv4) |
|
521 upd.iDstAddr.SetAddress(iControl.GetConfig().iLlmnrIpv4); |
|
522 else |
|
523 upd.iDstAddr.SetAddress(iControl.GetConfig().iLlmnrIpv6); |
|
524 upd.iDstAddr.SetScope(aEntry.iZone[upd.iDstAddr.Ip6Address().Scope()-1]); |
|
525 upd.iDstAddr.SetPort(iControl.GetConfig().iLlmnrPort); |
|
526 upd.iRepeat = 1 + iControl.GetConfig().iLlmnrRetries; |
|
527 Queue(upd); |
|
528 return KErrNone; |
|
529 } |
|
530 |
|
531 void CDndLlmnrResponder::Query(const TMsgBuf &aBuf, const TInetAddr &aServer, const RSocket &aSocket) |
|
532 { |
|
533 ASSERT(aServer.Port() != 0); |
|
534 |
|
535 // Preselect the the session... |
|
536 TInt session = 0; |
|
537 for(;; session++) |
|
538 { |
|
539 if(session == KLlmnrMaxSessions) |
|
540 { |
|
541 LOG(Log::Printf(_L("\tLLMNR No free slots in iLlmnrDataList\r\n"))); |
|
542 return; |
|
543 } |
|
544 if(!iLlmnrDataList[session].iLlmnrEntry) |
|
545 break; |
|
546 } |
|
547 TLlmnrMsgData &resp = iLlmnrDataList[session]; |
|
548 TInt rcode; |
|
549 TInt offset = aBuf.VerifyMessage(rcode, resp.iQuestion); |
|
550 if (offset < 0) |
|
551 { |
|
552 LOG(Log::Printf(_L("\tLLMNR Corrupt message\r\n"))); |
|
553 return; // A corrupted message |
|
554 } |
|
555 const TDndHeader &hdr = aBuf.Header(); |
|
556 if (hdr.QR() || hdr.ANCOUNT() != 0) |
|
557 return; // This is responce, Not a query. Ignore. |
|
558 |
|
559 CLlmnrEntry *entry = iEntryList; |
|
560 TUint32 index = 0; |
|
561 |
|
562 // Note: there are other PTR queries than just for address! |
|
563 // (fall to normal query processing for non address PTR queries). |
|
564 if (resp.iQuestion.QType() == EDnsQType_PTR && |
|
565 (index = IsMyAddress(resp.iQuestion)) != 0) |
|
566 { |
|
567 // See if this interface is active... |
|
568 for(;; entry = entry->iNext) |
|
569 { |
|
570 if (entry == NULL) |
|
571 { |
|
572 LOG(LogPrint(_L("\tLLMNR No match: "), resp.iQuestion)); |
|
573 return; // No match, ignore |
|
574 } |
|
575 if (entry->iState > 0) |
|
576 { |
|
577 if (entry->iZone[0] == index) |
|
578 break; |
|
579 } |
|
580 } |
|
581 } |
|
582 else |
|
583 { |
|
584 |
|
585 // The question must match one of the entries |
|
586 |
|
587 const TInt scopelevel = aServer.Ip6Address().Scope()-1; |
|
588 const TUint32 scopeid = aServer.Scope(); |
|
589 for(CLlmnrEntry *my_name = NULL; ;entry = entry->iNext) |
|
590 { |
|
591 if (entry == NULL) |
|
592 { |
|
593 if (my_name == NULL) |
|
594 { |
|
595 LOG(LogPrint(_L("\tLLMNR Query does not match: "), resp.iQuestion)); |
|
596 return; // No match, ignore |
|
597 } |
|
598 // |
|
599 // No exact match to the query, but the name |
|
600 // matched to at least one of my unique names. |
|
601 // Reply with empty RR-set, using one of those entries. |
|
602 entry = my_name; |
|
603 break; |
|
604 } |
|
605 // A iState > 0 means valid entry. |
|
606 if (entry->iState > 0) |
|
607 { |
|
608 if (scopeid == entry->iZone[scopelevel]) |
|
609 { |
|
610 if (DnsCompareNames(resp.iQuestion, entry->iQuestion)) |
|
611 { |
|
612 my_name = entry; |
|
613 if (resp.iQuestion.QClass() == entry->iQuestion.QClass() && |
|
614 resp.iQuestion.QType() == entry->iQuestion.QType()) |
|
615 break; // Full Match, answer using this entry! |
|
616 } |
|
617 } |
|
618 } |
|
619 } |
|
620 } |
|
621 // |
|
622 // Actually answering the question, assign the request |
|
623 // |
|
624 LOG(LogPrint(_L("\tLLMNR Sending reply to matched query: "), *entry)); |
|
625 resp.iLlmnrEntry = entry; |
|
626 resp.iQR = 1; |
|
627 resp.iAA = 1; |
|
628 resp.iRCode = EDnsRcode_NOERROR; |
|
629 resp.iDstAddr = aServer; |
|
630 resp.iRepeat = 0; |
|
631 Queue(resp, aSocket, hdr.ID()); |
|
632 } |
|
633 |
|
634 |
|
635 TUint32 CDndLlmnrResponder::IsMyAddress(const TDndQuestion &aQuestion) |
|
636 { |
|
637 TPckgBuf<TSoInetIfQuery> opt; |
|
638 if (!aQuestion.GetAddress(opt().iSrcAddr)) |
|
639 return 0; // Not a parsable PTR for IP address, ignore. |
|
640 if(Socket().GetOpt(KSoInetIfQueryBySrcAddr, KSolInetIfQuery, opt) != KErrNone) |
|
641 return 0;// not my address, ignore |
|
642 return opt().iIndex; |
|
643 } |
|
644 |
|
645 TInt CDndLlmnrResponder::SetHostName(TUint32 aId, const TDesC &aName) |
|
646 { |
|
647 for (CLlmnrEntry *e = iEntryList; e; e = e->iNext) |
|
648 { |
|
649 if (e->iHostName && e->iZone[KIp6AddrScopeNetwork-1] == aId) |
|
650 { |
|
651 if (aName.Length() > 0) |
|
652 { |
|
653 e->iState = EInactive; |
|
654 e->iQuestion.SetName(aName); |
|
655 (void)DoUpdate(*e); |
|
656 } |
|
657 else |
|
658 { |
|
659 e->iState = EDisabled; |
|
660 CancelAll(*e); |
|
661 } |
|
662 } |
|
663 } |
|
664 return KErrNone; |
|
665 } |
|
666 |
|
667 |
|
668 TInt CDndLlmnrResponder::GetHostName(TUint32 aId, MDnsResolver &aCallback) |
|
669 { |
|
670 const TInt result = DoHostNameState(aId); |
|
671 |
|
672 if (result > 0) |
|
673 { |
|
674 // |
|
675 // Pending entries present, install callback |
|
676 // |
|
677 for (CHostCallback *c = iCallbacks; ; c = c->iNext) |
|
678 { |
|
679 if (c == NULL) |
|
680 { |
|
681 c = new CHostCallback(aCallback); |
|
682 if (c == NULL) |
|
683 return KErrNoMemory; |
|
684 c->iNext = iCallbacks; |
|
685 iCallbacks = c; |
|
686 break; |
|
687 } |
|
688 else if (&aCallback == &c->iCallback) |
|
689 break; // Already installed. |
|
690 } |
|
691 } |
|
692 return result; |
|
693 } |
|
694 |
|
695 |
|
696 |
|
697 /** |
|
698 * Determines the state of the hostname. |
|
699 * |
|
700 * Scans through all LLMNR names and checks state of the |
|
701 * entries that have been generated from the host name. |
|
702 * Ignore entries in EDisabled state. |
|
703 * |
|
704 * @return result as follows: |
|
705 * |
|
706 * - KErrNone, if all existing entries are currently |
|
707 * marked as "unique" (note: this includes the case, |
|
708 * where none of the names are generated from the |
|
709 * host name). No entry is in conflict state. |
|
710 * - KErrAlreadyExists, if at least one of the entries |
|
711 * is in conflict. |
|
712 * - = 1 (Pending), if at least one of the entries is |
|
713 * still being tested for uniqueness, and none of the |
|
714 * entries is in conflict. |
|
715 */ |
|
716 TInt CDndLlmnrResponder::DoHostNameState(TUint32 aId) |
|
717 { |
|
718 TInt result = KErrNone; |
|
719 for (CLlmnrEntry *e = iEntryList; e; e = e->iNext) |
|
720 { |
|
721 if (e->iZone[KIp6AddrScopeNetwork-1] == aId && e->iHostName) |
|
722 { |
|
723 if (e->iState == EInactive) |
|
724 { |
|
725 // Pending state, unique test not yet complete |
|
726 result = 1; |
|
727 } |
|
728 else if (e->iState == EConflict) |
|
729 { |
|
730 // Conflict state, assume a name collision has occurred |
|
731 return KErrAlreadyExists; |
|
732 } |
|
733 } |
|
734 } |
|
735 return result; |
|
736 } |
|
737 |
|
738 /** |
|
739 * Do callbacks for pending GetHostName. |
|
740 * |
|
741 * Determines the state of the hostname and does the |
|
742 * callbacks, if the state is not pending. |
|
743 */ |
|
744 void CDndLlmnrResponder::DoCallbacks(TUint32 aId) |
|
745 { |
|
746 if (iCallbacks == NULL) |
|
747 return; |
|
748 |
|
749 const TInt result = DoHostNameState(aId); |
|
750 if (result > 0) |
|
751 return; |
|
752 |
|
753 CHostCallback *c = iCallbacks; |
|
754 iCallbacks = NULL; |
|
755 while (c) |
|
756 { |
|
757 CHostCallback *cb = c; |
|
758 c = cb->iNext; |
|
759 cb->iCallback.ReplyCallback(result); |
|
760 delete cb; |
|
761 } |
|
762 } |
|
763 |
|
764 //****************** TLlmnrMsgData **************************** |
|
765 |
|
766 // constructor needed only to setup the timeout framework |
|
767 TLlmnrMsgData::TLlmnrMsgData() : iTimeout(TLlmnrMsgDataTimeoutLinkage::Timeout) |
|
768 { |
|
769 } |
|
770 |
|
771 void TLlmnrMsgData::Timeout(const TTime & /*aNow*/) |
|
772 { |
|
773 LOG(Log::Printf(_L("--> TLlmnrMsgData::Timeout() -start-\r\n"))); |
|
774 if (iLlmnrEntry) |
|
775 { |
|
776 if (iRepeat > 0) |
|
777 { |
|
778 LOG(iParent->LogPrint(_L("\tLLMNR Resend query: "), *iLlmnrEntry)); |
|
779 iParent->ReSend(*this); |
|
780 } |
|
781 else |
|
782 { |
|
783 Cancel(); |
|
784 // The timeout is only activated with Unique testing, thus |
|
785 // when final repeat has copleted without conflict, mark |
|
786 // entry as unique |
|
787 LOG(iParent->LogPrint(_L("\tLLMNR Name is unique: "), *iLlmnrEntry)); |
|
788 iLlmnrEntry->iState = EUnique; |
|
789 if (iLlmnrEntry->iHostName) |
|
790 { |
|
791 // If entry depended on the hostname and there are pending GetHostNames, |
|
792 // then check if this was the last such entry in pending state, and |
|
793 // if so, generate callbacks. |
|
794 iParent->DoCallbacks(iLlmnrEntry->iZone[KIp6AddrScopeNetwork-1]); |
|
795 } |
|
796 iLlmnrEntry = NULL; |
|
797 } |
|
798 } |
|
799 LOG(Log::Printf(_L("<-- TLlmnrMsgData::Timeout() -exit-\r\n"))); |
|
800 } |
|
801 |
|
802 TBool TLlmnrMsgData::Reply(CDnsSocket &aSource, const TMsgBuf &aBuf, const TInetAddr & /*aServer*/) |
|
803 { |
|
804 if (!iLlmnrEntry) |
|
805 return FALSE; // Nothing to do if no entry associated |
|
806 |
|
807 TDndQuestion question; |
|
808 TInt rcode; |
|
809 TInt offset = aBuf.VerifyMessage(rcode, question); |
|
810 if (offset <= 0) |
|
811 return FALSE; |
|
812 |
|
813 const TDndHeader &hdr = aBuf.Header(); |
|
814 if (!hdr.QR()) |
|
815 return FALSE; // This is query, not a response. Ignore. |
|
816 if (hdr.OPCODE() != EDnsOpcode_QUERY || rcode != EDnsRcode_NOERROR) |
|
817 return FALSE; // Not an OK reply |
|
818 |
|
819 if (question.CheckQuestion(iQuestion) != KErrNone) |
|
820 return FALSE; |
|
821 |
|
822 // This is a matching reply to query of our own name, there is |
|
823 // a collision. |
|
824 iLlmnrEntry->iState = EConflict; |
|
825 if (iLlmnrEntry->iHostName) |
|
826 iParent->DoCallbacks(iLlmnrEntry->iZone[KIp6AddrScopeNetwork-1]); |
|
827 CNotifyConflict::Start(*iParent, *iLlmnrEntry); |
|
828 |
|
829 Cancel(); |
|
830 CDndLlmnrResponder &responder = (CDndLlmnrResponder &)aSource; |
|
831 responder.CancelAll(*iLlmnrEntry); |
|
832 return TRUE; |
|
833 } |
|
834 |
|
835 |
|
836 void TLlmnrMsgData::Sent(CDnsSocket &/*aSource*/) |
|
837 { |
|
838 if (iRepeat == 0) |
|
839 { |
|
840 iLlmnrEntry = NULL; |
|
841 Cancel(); |
|
842 } |
|
843 else |
|
844 { |
|
845 iRepeat -= 1; |
|
846 iTimeout.Set(&iParent->iControl.Timer(), iParent->iControl.GetConfig().iLlmnrMinTime); |
|
847 } |
|
848 } |
|
849 |
|
850 void TLlmnrMsgData::Abort(CDnsSocket &/*aSource*/, const TInt aReason) |
|
851 { |
|
852 LOG(Log::Printf(_L("TLlmnrMsgData::Abort - reason: %d\r\n"),aReason)); |
|
853 (void)aReason; // silence compiler warning |
|
854 iLlmnrEntry = NULL; |
|
855 } |
|
856 |
|
857 // TLlmnrMsgData::Build |
|
858 // ****************** |
|
859 /** |
|
860 // @retval aMsg |
|
861 // contains the fully constructed message to be sent to the DNS server, |
|
862 // if Build succeeds |
|
863 // @retval aServer |
|
864 // contains the server address for which the message should be sent |
|
865 // |
|
866 // @returns TRUE, successful Build, and error (< 0) otherwise |
|
867 */ |
|
868 TBool TLlmnrMsgData::Build(CDnsSocket &/*aSource*/, TMsgBuf &aMsg, TInetAddr &aServer, TInt /*aMaxMessage*/) |
|
869 { |
|
870 TInt ret = KErrNone; |
|
871 for (;;) |
|
872 { |
|
873 if (iLlmnrEntry == NULL) |
|
874 { |
|
875 LOG(Log::Printf(_L("TLlmnrMsgData::Build() - Faulty component\r\n"))); |
|
876 ret = KErrArgument; |
|
877 break; |
|
878 } |
|
879 |
|
880 aServer = iDstAddr; |
|
881 aMsg.SetLength(sizeof(TDndHeader)); |
|
882 TDndHeader &hdr = (TDndHeader &)aMsg.Header(); |
|
883 hdr.SetRD(0); // Part of zero field |
|
884 hdr.SetOpcode(EDnsOpcode_QUERY); |
|
885 hdr.SetQR(iQR); |
|
886 hdr.SetAA(iAA); |
|
887 hdr.SetRCode(iRCode); |
|
888 hdr.SetQdCount(1); |
|
889 const TInt qname_offset = aMsg.Length(); |
|
890 ret = iQuestion.Append(aMsg); |
|
891 if (ret != KErrNone) |
|
892 break; |
|
893 |
|
894 if (iQR) |
|
895 { |
|
896 // This is a reply message |
|
897 TDndRROut rr(aMsg); |
|
898 rr.iType = (TUint16)iQuestion.QType(); |
|
899 rr.iClass = (TUint16)iQuestion.QClass(); |
|
900 rr.iTTL = iParent->iLlmnrConf->iTTL; |
|
901 |
|
902 if (rr.iType == EDnsType_PTR) |
|
903 { |
|
904 // answer contains one or several hostnames, which |
|
905 // are configured for the same interface as the |
|
906 // queried address. |
|
907 TUint16 count = 0; |
|
908 for(CLlmnrEntry *entry = iParent->iEntryList; entry != NULL; entry = entry->iNext) |
|
909 { |
|
910 if (entry->iState <= 0) |
|
911 continue; |
|
912 if (entry->iZone[0] != iLlmnrEntry->iZone[0]) |
|
913 continue; |
|
914 // |
|
915 // Build an RR |
|
916 // |
|
917 ret = rr.Append(KNullDesC8, qname_offset); |
|
918 if (ret != KErrNone) |
|
919 break; // -- no reply sent! |
|
920 ret = rr.AppendRData(entry->iQuestion, 0); |
|
921 if (ret != KErrNone) |
|
922 break; // -- no reply sent! |
|
923 count++; |
|
924 } |
|
925 hdr.SetAnCount(count); // set answer or prerequisite count |
|
926 } |
|
927 else if (iQuestion.QType() == EDnsQType_ANY) |
|
928 { |
|
929 // For ANY query, return one address |
|
930 // (experimental, not fully worked out yet) |
|
931 TUint16 count = 0; |
|
932 TInetAddr addr; |
|
933 rr.iType = EDnsType_A; |
|
934 ret = iParent->MakeMyAddr(iLlmnrEntry->iZone[0], iDstAddr, (EDnsType)rr.iType, addr); |
|
935 if (ret != KErrNone) |
|
936 break; |
|
937 if (!addr.IsUnspecified()) |
|
938 { |
|
939 ret = rr.Append(KNullDesC8, qname_offset);// compressed name and answer |
|
940 if (ret != KErrNone) |
|
941 ret = rr.AppendRData(addr); |
|
942 ++count; |
|
943 } |
|
944 rr.iType = EDnsType_AAAA; |
|
945 ret = iParent->MakeMyAddr(iLlmnrEntry->iZone[0], iDstAddr, (EDnsType)rr.iType, addr); |
|
946 if (ret != KErrNone) |
|
947 break; |
|
948 if (!addr.IsUnspecified()) |
|
949 { |
|
950 ret = rr.Append(KNullDesC8, qname_offset);// compressed name and answer |
|
951 if (ret != KErrNone) |
|
952 ret = rr.AppendRData(addr); |
|
953 ++count; |
|
954 } |
|
955 hdr.SetAnCount(count); |
|
956 } |
|
957 else if (iQuestion.QType() != iLlmnrEntry->iQuestion.QType()) |
|
958 { |
|
959 // The question and entry do not match. Assume this is |
|
960 // query for RR that we don't have, but the name matched. |
|
961 // Reply with empty RR-set. |
|
962 hdr.SetAnCount(0); |
|
963 } |
|
964 else if (rr.iType == EDnsType_A || rr.iType == EDnsType_AAAA) |
|
965 { |
|
966 // answer contains one address |
|
967 TInetAddr addr; |
|
968 ret = iParent->MakeMyAddr(iLlmnrEntry->iZone[0], iDstAddr, (EDnsType)rr.iType, addr); |
|
969 if (ret != KErrNone) |
|
970 break; |
|
971 if (addr.IsUnspecified()) |
|
972 hdr.SetAnCount(0); // No address |
|
973 else |
|
974 { |
|
975 ret = rr.Append(KNullDesC8, qname_offset);// compressed name and answer |
|
976 if (ret == KErrNone) |
|
977 ret = rr.AppendRData(addr); |
|
978 hdr.SetAnCount(1); // set answer count |
|
979 } |
|
980 } |
|
981 else |
|
982 break; // Unsupported RR type, no answer! |
|
983 } |
|
984 if (ret != KErrNone) |
|
985 break; |
|
986 return 1; |
|
987 } |
|
988 // |
|
989 // Failed to build the message |
|
990 // |
|
991 iLlmnrEntry = NULL; |
|
992 Cancel(); |
|
993 return 0; |
|
994 } |
|
995 |
|
996 |
|
997 // ********************************************************************** |
|
998 |
|
999 CNotifyConflict::CNotifyConflict(CDndLlmnrResponder &aResponder, CLlmnrEntry &aEntry) |
|
1000 : CActive(0), iResponder(aResponder), iEntry(aEntry) |
|
1001 { |
|
1002 LOG(Log::Printf(_L("\tnotifier[%u] new"), (TUint)this)); |
|
1003 CActiveScheduler::Add(this); |
|
1004 } |
|
1005 |
|
1006 CNotifyConflict::~CNotifyConflict() |
|
1007 { |
|
1008 Cancel(); |
|
1009 if (iConnected) |
|
1010 { |
|
1011 iNotifier.Close(); |
|
1012 } |
|
1013 |
|
1014 // Detach from the entry. |
|
1015 if (iEntry.iNotify == this) |
|
1016 iEntry.iNotify = NULL; |
|
1017 // Deque from the active scheduler |
|
1018 if (IsAdded()) |
|
1019 Deque(); |
|
1020 LOG(Log::Printf(_L("\tnotifier[%u] deleted"), (TUint)this)); |
|
1021 } |
|
1022 |
|
1023 void CNotifyConflict::Start(CDndLlmnrResponder &aResponder, CLlmnrEntry &aEntry) |
|
1024 { |
|
1025 if (aEntry.iNotify) |
|
1026 { |
|
1027 return; // Notify process is already active for this hostname, nothing to do. |
|
1028 } |
|
1029 aEntry.iNotify = new CNotifyConflict(aResponder, aEntry); |
|
1030 if (aEntry.iNotify) |
|
1031 { |
|
1032 aEntry.iNotify->DoStart(); |
|
1033 } |
|
1034 } |
|
1035 |
|
1036 void CNotifyConflict::DoStart() |
|
1037 { |
|
1038 if (iNotifier.Connect() == KErrNone) |
|
1039 { |
|
1040 iConnected = 1; |
|
1041 // Fill in information |
|
1042 iInfo().iIAPId = iEntry.iZone[1]; // IAP is iZone[1] |
|
1043 iInfo().iNetworkId = iEntry.iZone[15]; // NET is iZone[15] |
|
1044 (void)iEntry.iQuestion.GetName(iInfo().iName); |
|
1045 LOG(Log::Printf(_L("\tnotifier[%u] activated IAP=%d NET=%d HOST='%S'"), (TUint)this, |
|
1046 iInfo().iIAPId, iInfo().iNetworkId, &iInfo().iName)); |
|
1047 iNotifier.StartNotifierAndGetResponse(iStatus, TUid::Uid(KLlmnrConflictNotifyUid), iInfo, iInfo); |
|
1048 SetActive(); |
|
1049 } |
|
1050 else |
|
1051 { |
|
1052 // Cancel Notifier activity, cannot get it to work. |
|
1053 delete this; |
|
1054 } |
|
1055 } |
|
1056 |
|
1057 void CNotifyConflict::DoCancel() |
|
1058 { |
|
1059 LOG(Log::Printf(_L("\tnotifier[%u] canceling"), (TUint)this)); |
|
1060 iNotifier.CancelNotifier(TUid::Uid(KLlmnrConflictNotifyUid)); |
|
1061 } |
|
1062 |
|
1063 void CNotifyConflict::RunL() |
|
1064 { |
|
1065 LOG(Log::Printf(_L("-->\tnotifier[%u] Completion result=%d"), (TUint)this, iStatus.Int())); |
|
1066 if (iStatus.Int() == KErrNone && iInfo().IsSafe() && iInfo().iName.Length() > 0) |
|
1067 { |
|
1068 if (iEntry.iQuestion.SetName(iInfo().iName) == KErrNone) |
|
1069 { |
|
1070 LOG(Log::Printf(_L("-->\tnotifier[%u] Trying new name '%S'"), (TUint)this, &iInfo().iName)); |
|
1071 iEntry.iState = EInactive; |
|
1072 // Detach from the entry. |
|
1073 if (iEntry.iNotify == this) |
|
1074 iEntry.iNotify = NULL; |
|
1075 (void)iResponder.DoUpdate(iEntry); |
|
1076 } |
|
1077 } |
|
1078 delete this; |
|
1079 } |
|
1080 |
|
1081 #endif |