diff -r 000000000000 -r af10295192d8 tcpiputils/dnd/src/servers.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tcpiputils/dnd/src/servers.cpp Tue Jan 26 15:23:49 2010 +0200 @@ -0,0 +1,683 @@ +// Copyright (c) 2004-2009 Nokia Corporation and/or its subsidiary(-ies). +// All rights reserved. +// This component and the accompanying materials are made available +// under the terms of "Eclipse Public License v1.0" +// which accompanies this distribution, and is available +// at the URL "http://www.eclipse.org/legal/epl-v10.html". +// +// Initial Contributors: +// Nokia Corporation - initial contribution. +// +// Contributors: +// +// Description: +// servers.cpp - server manager module +// + +#include "servers.h" +#include "engine.h" +#include "inet6log.h" +#include "dns_hdr.h" // only for KDnsPort! +#include "dnd.hrh" + +// Item of the server list, hold DNS server information +class TDnsServerData + { +public: + TInetAddr iAddr; //< DNS Server Address + TDnsServerScope iScope; //< The Sever Scope and type (MC/UC) + TInt iServerId; //< Id of the server + TInt iInterface; //< Index to Interface Array + }; + +// Item of the interface list, hold interface information for DNS +class TDnsInterfaceData + { +public: + TName iName; //< Name of the interface + TUint32 iScope[16]; //< The scope vector + }; + +// Item of the configured servers list +class TDnsConfiguredServer + { +public: + TName iName; + TInetAddr iAddr; + }; + +class CDnsServerManager : public CBase, public MDnsServerManager + { + ~CDnsServerManager(); +public: + CDnsServerManager(CDndEngine &aControl); + void ConstructL(); + void ConfigurationChanged(); + + // + // MDnsServerManager API + // (for docs, see the MDnsServerManger definitions) + // + TInt OpenList(const TDnsServerFilter &aFilter, MDnsServerListNotify *aNotify); + TInt Count(const TDnsServerFilter &aFilter) const; + TInt Next(const TDnsServerFilter &aFilter, TInt aServerId) const; + void CloseList(const TDnsServerFilter &aFilter); + + TInt Address(TInt aServerId, TInetAddr &aAddr) const; + TUint32 Scope(TInt aServerId, const TInetAddr &aAddr); + TInt ServerId(const TInetAddr &aAddr) const; + TUint32 NameSpace(const TDnsServerFilter &aFilter, TInt aServerId) const; + TInt BuildServerList(); + void AddServerAddress(const TName &aInterface, const TInetAddr &aAddr); + void LockByAddress(const TInetAddr &aAddr, TUint32 aNid, TDnsServerFilter &aFilter); + +private: + // Build and add interface entry to the list (basic operation) + TInt AddInterfaceEntry(const TSoInetIfQuery &aInfo); + // Find interface matching the destination address + TInt FindInterface(const TInetAddr &aAddr); + // Add new server address to the server list + void AddToServerList(const TInetAddr &aAddr, TInt aIf, RSocket &aSocket); + // Add new interface to the interface list + TInt AddToInterfaceList(const TSoInetInterfaceInfo &aInfo, RSocket &aSocket); + // Compare the filter with a DNS server + TBool Match(const TDnsServerFilter &aFilter, const TDnsServerData &aServer) const; + + CDndEngine &iControl; + TUint32 iServerId; //< The last used server id. + TTime iMark; //< The time of the last configuration change. + TUint iConfigure:1; //< Reconfigure needed, if set. + TUint iStable:1; //< = 1, when scanned list is supposed to be stable + TUint iCacheFlushed:1; //< = 1, when cache has been flushed (only used with "FlushOnConfig" ini-option) + + CArrayFixFlat *iServerList; //< Current list of servers + CArrayFixFlat *iInterfaceList; //< Current list of interfaces + CArrayFixFlat *iConfiguredList; //< Current list of configured servers + }; + + +MDnsServerManager *DnsServerManager::NewL(CDndEngine &aControl) + { + CDnsServerManager *mgr = new (ELeave) CDnsServerManager(aControl); + + CleanupStack::PushL(mgr); + mgr->ConstructL(); + CleanupStack::Pop(); + return mgr; + } + +// +// CDnsServerManager +// + +CDnsServerManager::CDnsServerManager(CDndEngine &aControl) : iControl(aControl) + { + } + +void CDnsServerManager::ConstructL() + { + iServerList = new (ELeave) CArrayFixFlat(2); + iInterfaceList = new (ELeave) CArrayFixFlat(5); + // iConfiguredList is allocated only if required. + } + +void CDnsServerManager::ConfigurationChanged() + { + LOG(Log::Printf(_L("CDnsServerManager -- Configuration changed"))); + // Just flag that building a new server list is needed. Try + // to delay heavy interface scanning operation until really + // needed (because configuration changes may come in burts). + iConfigure = 1; + iStable = 0; + iCacheFlushed = 0; + iMark.UniversalTime(); + } + +CDnsServerManager::~CDnsServerManager() + { + delete iServerList; + delete iInterfaceList; + delete iConfiguredList; + } + +// CDnsServerManager::AddInterfaceData +TInt CDnsServerManager::AddInterfaceEntry(const TSoInetIfQuery &aInfo) + { + TRAPD(err, + TDnsInterfaceData &ifd = iInterfaceList->ExtendL(); + ifd.iName = aInfo.iName; + for (TInt i = sizeof(ifd.iScope) / sizeof(ifd.iScope[0]); --i >= 0; ) + ifd.iScope[i] = aInfo.iZone[i]; + ); + return err < 0 ? err : iInterfaceList->Count() - 1; + } + +// CDnsServerManager::AddToInterfaceList +// ************************************* +/** +// Add the interface to the interface list, if it does not already exist. +// The existence is based on comparing the interface names. +// +// @param aInfo the information that identifies the interface +// @param aSocket (must be open) to be used for GetOpt, if needed +// @returns +// @li < 0, if there are some errors (interface was not added) +// @li index to the interface (>= 0) in the interface list. +*/ +TInt CDnsServerManager::AddToInterfaceList(const TSoInetInterfaceInfo &aInfo, RSocket &aSocket) + { + if (aInfo.iName.Length() == 0) + return -1; // Interface must have a name. + // + // Check if interface already exists and don't insert duplicates + // + TInt i; + for (i = iInterfaceList->Count(); --i >= 0; ) + { + const TDnsInterfaceData &data = iInterfaceList->At(i); + if (data.iName.Compare(aInfo.iName) == 0) + return i; // Interface already present in the list + } + // + // A new interface, get the scope vector + // + TPckgBuf opt; + opt().iName = aInfo.iName; + const TInt err = aSocket.GetOpt(KSoInetIfQueryByName, KSolInetIfQuery, opt); + return err < 0 ? err : AddInterfaceEntry(opt()); + } + +// CDnsServerManager::FindInterface +// ******************************** +// +TInt CDnsServerManager::FindInterface(const TInetAddr &aAddr) + { + TPckgBuf opt; + opt().iDstAddr = aAddr; + const TInt err = iControl.iSocket.GetOpt(KSoInetIfQueryByDstAddr, KSolInetIfQuery, opt); + if (err < 0) + return err; + + // Check if interface is already known + + for (TInt i = iInterfaceList->Count(); --i >= 0; ) + { + const TDnsInterfaceData &data = iInterfaceList->At(i); + if (data.iName.Compare(opt().iName) == 0) + return i; + } + // Not present yet, just add it + return AddInterfaceEntry(opt()); + } + +// CDnsServerManager::AddToServerList +// ********************************** +/** +// Add new address to the server list. A new entry is only +// added if the address does not already exist. +// +// @param aAddr address of the DNS server +// @param aIf the interface (index to the interface list) +*/ +void CDnsServerManager::AddToServerList(const TInetAddr &aAddr, TInt aIf, RSocket &aSocket) + { + if (aAddr.IsUnspecified()) + return; // No address, nothing to add + // + // Normalize all addresses into IPv6 format + // + TDnsServerData sd; + sd.iAddr = aAddr; + sd.iInterface = aIf; + if (sd.iAddr.Family() == KAfInet) + sd.iAddr.ConvertToV4Mapped(); + else if (sd.iAddr.Family() != KAfInet6) + return; // Only IPv4 or IPv6 addresses are valid + if (sd.iAddr.IsMulticast()) + // sd.iScope = (TDnsServerScope)(sd.iAddr.Ip6Address().Scope()); + sd.iScope = EDnsServerScope_MC_LOCAL; + else + sd.iScope = EDnsServerScope_UC_GLOBAL; + + if (!sd.iAddr.Port()) + sd.iAddr.SetPort(KDnsPort); + + LOG(TBuf<80> dst); + LOG(sd.iAddr.OutputWithScope(dst)); + LOG(TBuf<80> src); + // In typhoon and later, DND never activates interfaces. All + // usable server addresses must have a valid route, before + // they can be used. Thus, check it... + // + TPckgBuf opt; + opt().iDstAddr = aAddr; + const TBool has_route = + (aSocket.GetOpt(KSoInetIfQueryByDstAddr, KSolInetIfQuery, opt) == KErrNone) && !opt().iSrcAddr.IsUnspecified(); + LOG(opt().iSrcAddr.OutputWithScope(src)); + if (!has_route) + { + LOG(Log::Printf(_L("\t\tnameserver [%S] (src=%S) has no route or no source address, skipped"), &dst, &src)); + return; // No route, unusable for now -- ignore + } + // + // Check if address already exists and don't insert duplicates + // + for (TInt i = iServerList->Count(); --i >= 0; ) + { + TDnsServerData &a = iServerList->At(i); + if (a.iAddr.Match(sd.iAddr) && a.iAddr.Scope() == sd.iAddr.Scope()) + { + // However, if server didn't have assigned + // interface yet, assign it from this call. + // + if (a.iInterface < 0) + a.iInterface = aIf; + LOG(Log::Printf(_L("\t\tnameserver (id=%d) [%S] (src=%S)"), a.iServerId, &dst, &src)); + return; // Duplicate address, do not add again. + } + } + + sd.iServerId = ++iServerId; // Assign a "server id" + LOG(Log::Printf(_L("\t\tnameserver (id=%d) [%S] (src=%S) (new)"), sd.iServerId, &dst, &src)); + TRAP_IGNORE(iServerList->AppendL(sd)); + } + +// CDnsServerManager::BuildServerList +// ********************************** +TInt CDnsServerManager::BuildServerList() + { + LOG(Log::Printf(_L("CDnsServerManager -- Scanning interfaces and building the server list"))); + TInt err = KErrNone; + + // Refresh the current list of DNS server addresses + // (this could be skipped if there was some definite way of knowing + // that nothing has changed since the last collect...) + + // Use Delete instead of Reset, so that space is reused? (not freed and reallocated) + iInterfaceList->Delete(0, iInterfaceList->Count()); + + // Trying to keep "server id" stable, thus do not clear existing + // server list, but just mark entries, so that unused ones can be + // reclaimed after build is complete. + for (TInt i = iServerList->Count(); --i >= 0; ) + iServerList->At(i).iInterface = -2; + + LOG(Log::Printf(_L("\t* Scanning interfaces"))); + // Read the DNS address from the interface + TSoInetInterfaceInfo *info = new TSoInetInterfaceInfo; // allocate large struct from heap! + if (info && (err = iControl.iSocket.SetOpt(KSoInetEnumInterfaces, KSolInetIfCtrl)) == KErrNone) + { + TPckg opt(*info); + while (iControl.iSocket.GetOpt(KSoInetNextInterface, KSolInetIfCtrl, opt) == KErrNone) + { + if (opt().iName.Length() == 0) + continue; // "null" interface, ignore + if (!opt().iAddress.IsUnspecified()) + { +#ifdef _LOG + TBuf<60> tmp; + opt().iAddress.OutputWithScope(tmp); + Log::Printf(_L("\t%S [%S]"), &opt().iName, &tmp); +#endif + TInt i = AddToInterfaceList(opt(), iControl.iSocket); + AddToServerList((TInetAddr &)(opt().iNameSer1), i, iControl.iSocket); + AddToServerList((TInetAddr &)(opt().iNameSer2), i, iControl.iSocket); + } + else + { + LOG(Log::Printf(_L("\tInterface: %S [no address, skipping]"), &opt().iName)); + } + } + } + delete info; + LOG(Log::Printf(_L("\t* Configured interface specific addresses"))); + + // + // Add configured servers, if a matching interface has become available + // (assumes that configured list and interface list are always short, so + // the loop over all lists is not too bad...) + if (iConfiguredList) + { + for (TInt i = iConfiguredList->Count(); --i >= 0; ) + { + const TDnsConfiguredServer &cs = iConfiguredList->At(i); + for (TInt j = iInterfaceList->Count(); --j >= 0; ) + { + const TDnsInterfaceData &data = iInterfaceList->At(j); + if (data.iName.Compare(cs.iName) == 0) + { + // Complete the address with the scope id + // from the interface! + TInetAddr addr(cs.iAddr); + if (addr.Family() == KAfInet) + addr.ConvertToV4Mapped(); + const TUint s = addr.Ip6Address().Scope() - 1; + if (s < 16) + { + addr.SetScope(data.iScope[s]); + AddToServerList(addr, j, iControl.iSocket); + } + break; + } + } + } + } + // + // Remove unused servers from the list + // + LOG(Log::Printf(_L("\t* Remove stale addresses"))); + const TInt N = iServerList->Count(); + TInt k = 0; + for (TInt j = 0; j < N; ++j) + { + const TDnsServerData &a = iServerList->At(j); + if (a.iInterface != -2) + { + // This server entry is still used + if (k != j) + iServerList->At(k) = a; + k++; + } +#ifdef _LOG + else + { + TBuf<80> tmp; + a.iAddr.OutputWithScope(tmp); + Log::Printf(_L("\t\tnameserver (id=%d) [%S] deleted"), a.iServerId, &tmp); + } +#endif + } + if (k < N) + { + iServerList->Delete(k, N - k); + } + + LOG(Log::Printf(_L("CDnsServerManager -- Done, current server count=%d"), iServerList->Count())); + return err; + } + +// CDnsServerManager::AddServerAddress +// *********************************** +void CDnsServerManager::AddServerAddress(const TName &aInterface, const TInetAddr &aAddr) + { + if (iConfiguredList == NULL && + (iConfiguredList = new CArrayFixFlat(5)) == NULL) + return; // No memory for the allocation, ignore.. + + // Do not add duplicates, check existing entries + for (TInt i = iConfiguredList->Count(); --i >= 0; ) + { + const TDnsConfiguredServer &cs = iConfiguredList->At(i); + if (cs.iName.Compare(aInterface) == 0 && cs.iAddr.Match(aAddr)) + return; // Duplicate! + } + + iConfigure = 1; // Request rebuild of server list. + + TRAP_IGNORE(TDnsConfiguredServer &cf = iConfiguredList->ExtendL(); + cf.iAddr = aAddr; + cf.iName = aInterface; + ); + } + +/** +// @param aFilter the server filter +// @parma aServer to be tested against the filter +// @returns +// @li TRUE, if the server matches the filter +// @li FALSE, if the server does not match the filter +*/ +TBool CDnsServerManager::Match(const TDnsServerFilter &aFilter, const TDnsServerData &aServer) const + { + if (aFilter.iServerScope != aServer.iScope) + return FALSE; + if (aFilter.iLockType < KIp6AddrScopeNodeLocal || aFilter.iLockType > KIp6AddrScopeNetwork) + return FALSE; // actually invalid locking scope level + + // If a server address is specified without interface (-1), then this + // server will match any filter (if server scopes are same). + if (aServer.iInterface >= 0) + { + const TDnsInterfaceData &id = iInterfaceList->At(aServer.iInterface); + if (aFilter.iLockId != id.iScope[aFilter.iLockType-1]) + return FALSE; // Not in locked scope + } + return TRUE; + } + + +// +// MDnsServerManager API +// +TInt CDnsServerManager::OpenList(const TDnsServerFilter &aFilter, MDnsServerListNotify * /*aNotify*/) + { + if (iConfigure) + { + const TInt err = BuildServerList(); + if (err != KErrNone) + return err; + iConfigure = 0; + } + if (iStable) + return KErrNone; + + if (iControl.GetConfig().iFlushOnConfig && iCacheFlushed == 0) + { + iCacheFlushed = 1; + TRAP_IGNORE(iControl.HandleCommandL(EDndFlush)); + } + + TTime stamp; + stamp.UniversalTime(); + TTimeIntervalSeconds elapsed; + stamp.SecondsFrom(iMark, elapsed); + if (elapsed.Int() > (TInt)iControl.GetConfig().iSetupTime) + { + iStable = 1; + return KErrNone; + } + if (aFilter.iServerScope == EDnsServerScope_MC_LOCAL) + return KErrNone; + // We are still within setup time, return "pending" if no servers available + return (Count(aFilter) == 0) ? 1 : KErrNone; + } + +TInt CDnsServerManager::Count(const TDnsServerFilter &aFilter) const + { + TInt count = 0; + for (TInt i = iServerList->Count(); --i >= 0; ) + if (Match(aFilter, iServerList->At(i))) + ++count; + return count; + } + +TInt CDnsServerManager::Next(const TDnsServerFilter &aFilter, TInt aServerId) const + { + const TInt N = iServerList->Count(); + + TInt wrap = 0; + TInt first_found = 0; + TInt current_found = 0; + + if (aServerId == 0) + { + // + // When "current" server is not specified, Next will + // return the first server (specified by iServerId) + // + for (TInt i = 0; i < N; ++i) + { + const TDnsServerData &server = iServerList->At(i); + if (server.iServerId == aFilter.iServerId) + first_found = 1; + if (first_found) + { + if (Match(aFilter, server)) + return server.iServerId; + } + else if (wrap == 0 && Match(aFilter, server)) + wrap = server.iServerId; + } + // + // iServerId server is not present (or it and none + // of the servers after it do not match). Return + // the first matching server. + return wrap; + } + else + { + // + // When aServerId is given, the Next will return + // the next matching server between (current, first) + // (or fail, if none exists) + for (TInt i = 0; i < N; ++i) + { + const TDnsServerData &server = iServerList->At(i); + if (current_found) + { + if (server.iServerId == aFilter.iServerId) + return 0; // Back to first, no Matching server + if (Match(aFilter, server)) + return server.iServerId; + } + else + { + if (server.iServerId == aServerId) + // Start looking for the next match + current_found = 1; + if (server.iServerId == aFilter.iServerId) + first_found = 1; + if (first_found == 0 && wrap == 0 && Match(aFilter, server)) + wrap = server.iServerId; + } + } + // + // Wrap around wrapping + // + if (current_found && first_found) + return wrap; + } + return 0; // No matching next server! + } + +void CDnsServerManager::CloseList(const TDnsServerFilter &/*aFilter*/) + { + } + + +TInt CDnsServerManager::Address(TInt aServerId, TInetAddr &aAddr) const + { + const TInt N = iServerList->Count(); + for (TInt i = 0; i < N; ++i) + { + const TDnsServerData &server = iServerList->At(i); + if (server.iServerId == aServerId) + { + aAddr = server.iAddr; + return KErrNone; + } + } + return KErrNotFound; + } + +TInt CDnsServerManager::ServerId(const TInetAddr &aAddr) const + { + for (TInt i = iServerList->Count(); --i >= 0; ) + { + const TDnsServerData &server = iServerList->At(i); + if (server.iAddr.CmpAddr(aAddr) && server.iAddr.Scope() == aAddr.Scope()) + return server.iServerId; + } + return 0; + } + +TUint32 CDnsServerManager::Scope(TInt aServerId, const TInetAddr &aAddr) + { + const TInt N = iServerList->Count(); + for (TInt i = 0; i < N; ++i) + { + const TDnsServerData &server = iServerList->At(i); + if (server.iServerId == aServerId) + { + const TUint i = aAddr.Ip6Address().Scope() - 1; + if (i > 15) + return 0; // Bad scope level. + const TInt j = server.iInterface < 0 ? FindInterface(server.iAddr) : server.iInterface; + if (j >= 0) + return (iInterfaceList->At(j)).iScope[i]; + } + } + return 0; + } + + +static TUint32 MakeNameSpaceId(TInt aServerScope, const TDnsInterfaceData &aIf) + { + // + // Construct the name space id from the server scope and + // matching scope id. + // + if (aServerScope < 0) + aServerScope = -aServerScope; + aServerScope -= 1; + if (aServerScope >= 0 && aServerScope < 16) + return (aIf.iScope[aServerScope] & ~(0xFu << 28)) | ((aServerScope & 0xFu) << 28); + return 0; + } + +TUint32 CDnsServerManager::NameSpace(const TDnsServerFilter &aFilter, TInt aServerId) const + { + if (aFilter.iLockType < KIp6AddrScopeNodeLocal || aFilter.iLockType > KIp6AddrScopeNetwork) + return 0; // actually invalid locking type + + if (aServerId) + { + const TInt N = iServerList->Count(); + for (TInt i = 0; i < N; ++i) + { + const TDnsServerData &server = iServerList->At(i); + if (server.iServerId == aServerId) + { + if (server.iInterface < 0) + break; + return MakeNameSpaceId(aFilter.iServerScope, iInterfaceList->At(server.iInterface)); + } + } + // Should this fall to generic search if server is not + // found? Or, just return 0? + } + + for (TInt i = iInterfaceList->Count(); --i >= 0; ) + { + TDnsInterfaceData &id = iInterfaceList->At(i); + if (aFilter.iLockId == id.iScope[aFilter.iLockType-1]) + return MakeNameSpaceId(aFilter.iServerScope, id); + } + // Cannot find name space id + return 0; + } + +void CDnsServerManager::LockByAddress(const TInetAddr &aAddr, TUint32 aNid, TDnsServerFilter &aFilter) + { + aFilter.iLockType = aAddr.Ip6Address().Scope(); + aFilter.iLockId = aAddr.Scope(); + if (aFilter.iLockId) + return; // Address specified the scope id, all done. + if (aFilter.iLockType == KIp6AddrScopeNetwork) + { + aFilter.iLockId = aNid; + return; // If lock scope level is network, we can use aNid as is. + } + + // Address does not specify the scope id. Try to pick one + // from known interfaces based on the network id. + for (TInt i = iInterfaceList->Count(); --i >= 0; ) + { + TDnsInterfaceData &id = iInterfaceList->At(i); + if (aNid == id.iScope[KIp6AddrScopeNetwork-1]) + { + aFilter.iLockId = id.iScope[aFilter.iLockType-1]; + break; + } + } + }