diff -r 000000000000 -r af10295192d8 tcpiputils/dhcp/src/DHCPStateMachine.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tcpiputils/dhcp/src/DHCPStateMachine.cpp Tue Jan 26 15:23:49 2010 +0200 @@ -0,0 +1,720 @@ +// 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: +// Implements the DHCP statemachine helper functions +// +// + +/** + @file DHCPStateMachine.cpp + @internalTechnology +*/ + +#include "DHCPStates.h" +#include "DHCPStatesDebug.h" +#include "DHCPControl.h" +#include "ExpireTimer.h" +#include "DHCPMsg.h" +#include +#include "DHCPServer.h" +#include "in6_opt.h" +#include "es_sock.h" + +CDHCPStateMachine::~CDHCPStateMachine() +/** + * Destructor of the If base class + * + * @internalTechnology + * + */ + { + __CFLOG_VAR((KLogSubSysDHCP, KLogCode, _L8("CDHCPStateMachine::~CDHCPStateMachine"))); + Cancel(); + delete iDhcpMessage; + delete iMessageSender; + delete iTimer; + delete iHostName; + delete iDomainName; +#ifdef SYMBIAN_NETWORKING_DHCP_MSG_HEADERS + iSavedExtraParameters.Close(); +#endif //SYMBIAN_NETWORKING_DHCP_MSG_HEADERS + iClientId.Close(); + iSocket.Close(); +#ifdef SYMBIAN_NETWORKING_DHCPSERVER + iSvrSocket.Close(); + delete iDNSInformation; +#endif // SYMBIAN_NETWORKING_DHCPSERVER + } + +void CDHCPStateMachine::ConstructL() +/** + * Creates socket and connections for the object + * + * + * @internalTechnology + * + */ + { + __CFLOG_VAR((KLogSubSysDHCP, KLogCode, _L8("CDHCPStateMachine::ConstructL"))); + +#ifdef _DEBUG + // let's set debug properties to something + // so they can be read immediately.. + CDHCPStateMachine* const & iStateMachine = this; + CDHCPStateMachine* const & iDhcpStateMachine = iStateMachine; + DHCP_DEBUG_PUBLISH_READY(DHCPDebug::ENotReady); + DHCP_DEBUG_PUBLISH_STATE(DHCPDebug::EStateUnknown); +#endif + + iTimer = CExpireTimer::NewL(); +#ifdef SYMBIAN_NETWORKING_DHCPSERVER + if(!iServerImpl) // Assemble client Ids only for DHCP client implementation + { +#endif // SYMBIAN_NETWORKING_DHCPSERVER + InitialiseSocketL(); + AssembleClientIDsL(); +#ifdef SYMBIAN_NETWORKING_DHCPSERVER + } + else + InitialiseServerSocketL(); +#endif // SYMBIAN_NETWORKING_DHCPSERVER + } + +void CDHCPStateMachine::Start(MStateMachineNotify* aStateMachineNotify) +/** + * The Start function + * + * Starts the statemachine task + * + * @internalTechnology + */ +{ + SetLastError( KErrNone ); + iReceiving = EFalse; + iHistory = 0; + SetActiveEvent(iFirstState); + if ( !aStateMachineNotify ) + {//no notifier specified => leave the current one unchanged + aStateMachineNotify = iStateMachineNotify; + } + CStateMachine::Start(NULL, NULL, aStateMachineNotify); +} + +void CDHCPStateMachine::CloseNSendMsgL(TRequestStatus& aStatus, CDHCPStateMachine::EAddressType aEAddressType) +/** + * Handles sending of packets for this object + * + * @internalTechnology + * + */ + { + __CFLOG_VAR((KLogSubSysDHCP, KLogCode, _L8("CDHCPStateMachine::CloseNSendMsgL"))); + PrepareToSendL(aEAddressType); + iMessageSender->Cancel(); + + GetServerAddress(iSocketAddr); + AddScopeToAddrL(iSocketAddr); + +#ifdef _DEBUG + THostName addrDes; + iSocketAddr.Output(addrDes); + __CFLOG_VAR((KLogSubSysDHCP, KLogCode, + _L("CDHCPStateMachine::CloseNSendMsgL - Sending message to %S%%%d"), &addrDes, iSocketAddr.Scope())); +#endif + + iSocket.SendTo(iDhcpMessage->Message(), iSocketAddr, 0, aStatus); + } + +#ifdef SYMBIAN_NETWORKING_DHCPSERVER +void CDHCPStateMachine::CloseNSendServerMsgL(TRequestStatus& aStatus, CDHCPStateMachine::EAddressType aEAddressType) +/** + * Handles sending of packets for this object + * + * @internalTechnology + * + */ + { + __CFLOG_VAR((KLogSubSysDHCP, KLogCode, _L8("CDHCPStateMachine::CloseNSendMsgL"))); + + + PrepareToSendServerMsgL(aEAddressType); + iMessageSender->Cancel(); + + GetClientAddress(iSrvSocketAddr); + AddScopeToClientAddrL(iSrvSocketAddr); + +#ifdef _DEBUG + THostName addrDes; + iSrvSocketAddr.Output(addrDes); + __CFLOG_VAR((KLogSubSysDHCP, KLogCode, + _L("CDHCPStateMachine::CloseNSendServerMsgL - Sending message to %S%%%d"), &addrDes, iSrvSocketAddr.Scope())); +#endif + + iSvrSocket.SendTo(iDhcpMessage->Message(), iSrvSocketAddr, 0, aStatus); + } + +void CDHCPStateMachine::GetClientAddress( TInetAddr& /*aAddress*/ ) +/** + * GetClientAddress + * + * Null implementation + * + * @internalTechnology +*/ + { + } + +#ifdef SYMBIAN_DNS_PROXY +TInetAddr CDHCPStateMachine::GetListenerAddress() + { + return iCurrentAddress; + } +#endif + +void CDHCPStateMachine::InitServerStateMachineL(MStateMachineNotify* /*aStateMachineNotify*/) +/** + * InitServerStateMachineL + * + * Null implementation + * + * @internalTechnology + */ + { + } + +void CDHCPStateMachine::InitServerBinding(MStateMachineNotify* /*aStateMachineNotify*/) + { + + } + +#endif // SYMBIAN_NETWORKING_DHCPSERVER + +void CDHCPStateMachine::CloseNSendMsgL(TTimeIntervalSeconds aSecs, TInt aMaxRetryCount, CDHCPStateMachine::EAddressType aEAddressType) +/** + * Handles sending of packets for this object + * + * @internalTechnology + * + */ + { + __CFLOG_VAR((KLogSubSysDHCP, KLogCode, _L8("CDHCPStateMachine::CloseNSendMsgL"))); + PrepareToSendL(aEAddressType); + iMessageSender->Cancel(); + + TInetAddr addr; + GetServerAddress( addr ); + AddScopeToAddrL(addr); + +#ifdef _DEBUG + THostName addrDes; + addr.Output(addrDes); + __CFLOG_VAR((KLogSubSysDHCP, KLogCode, + _L("CDHCPStateMachine::CloseNSendMsgL - Sending message to %S%%%d"), &addrDes, addr.Scope())); +#endif + + if (FastTimeoutDuringInform()) // true only when InformNegotiationIsRequiredForConnectionStartCompletion is ETrue + { + iMessageSender->SendL(addr, iDhcpMessage->Message(), (aSecs.Int() * KMicrosecondsInSecs) / 14, aMaxRetryCount); + } + else + { + iMessageSender->SendL(addr, iDhcpMessage->Message(), aSecs.Int() * KMicrosecondsInSecs, aMaxRetryCount); + } + } + +TInt CDHCPStateMachine::MSReportError(TInt aError) +/** + * Report an error properly + * + * @internalTechnology + */ + { + Cancel(); + + SetLastError(aError); + OnCompletion(); + + return aError; + } + +void CDHCPStateMachine::Cancel() +/** + * Cancel the state machine's activities + * + * @internalTechnology + */ + { + __CFLOG_VAR((KLogSubSysDHCP, KLogCode, _L8("CDHCPStateMachine::Cancel"))); + CancelMessageSender(); + iSocket.Close(); +#ifdef SYMBIAN_NETWORKING_DHCPSERVER + iSvrSocket.Close(); +#endif // SYMBIAN_NETWORKING_DHCPSERVER + CancelTimer(); + CStateMachine::Cancel(KErrNone); + delete iFirstState; + iFirstState = NULL; + iFastTimeout = EFalse; // reset iFastTimeout + iMaxRetryCount = KInfinity;//reset + } + +void CDHCPStateMachine::DoCancel() +/** + * Implements a default docancel for the connection object + * + * @internalTechnology + */ + { + // we have to cancel send and recv independently as cancelAll() + // doesn't satisfy us, only supporting read, write, ioctl, connect, + // accept and shutdown...:-( + __CFLOG_VAR((KLogSubSysDHCP, KLogCode, _L8("CDHCPStateMachine::DoCancel"))); + if (iSocket.SubSessionHandle()) + { + // check that the socket is open + // and if it is then we need to cancel things on it + iSocket.CancelRecv(); + } +#ifdef SYMBIAN_NETWORKING_DHCPSERVER + if(iSvrSocket.SubSessionHandle()) + { + iSvrSocket.CancelRecv(); + } +#endif // SYMBIAN_NETWORKING_DHCPSERVER + /* iAsyncCancelHandler is set by the DHCP State that assumes ownership of the + RequestStatus object of CDHCPStateMachine + */ + if(iAsyncCancelHandler) + { + iAsyncCancelHandler->Cancel(); + } + } +#ifdef SYMBIAN_NETWORKING_DHCPSERVER +void CDHCPStateMachine::FetchServerAddressL() +/** + * This function is used to get the server's address (interface address) + * + */ + { + if(!iSvrSocket.SubSessionHandle()) + InitialiseServerSocketL(); + + TPckgBuf opt; + while (iSvrSocket.GetOpt(KSoInetNextInterface, KSolInetIfCtrl, opt) == KErrNone) + { + if (opt().iName == iInterfaceName) + { + iCurrentAddress = opt().iAddress; + break; + } + } + } + +void CDHCPStateMachine::SetDNSInformation(TDes8* aDNSInfo) + { + delete iDNSInformation; + iDNSInformation = NULL; + + iDNSInformation = HBufC8::NewL(aDNSInfo->Length()); + iDNSInformation->Des() = *aDNSInfo; + } + +TBool CDHCPStateMachine::CheckNetworkId() +/** + * This function compares the NetworkIds of the client and server + * + */ + { + iInformClientAddr.SetV4MappedAddress(iCiaddr); + + if ((iInformClientAddr.Address() & KInetAddrNetMaskC) == + (iCurrentAddress.Address() & KInetAddrNetMaskC)) + { + return ETrue; + } + else + { + return EFalse; + } + } +#endif // SYMBIAN_NETWORKING_DHCPSERVER + +void CDHCPStateMachine::FetchHWAddress() +/** + * Fetches hardware address from the interface + * + */ + { + TPckgBuf opt; + while (iSocket.GetOpt(KSoInetNextInterface, KSolInetIfCtrl, opt) == KErrNone) + { + if (opt().iName == iInterfaceName) + { + iHardwareAddr = opt().iHwAddr; + if(iHardwareAddr.Length() <= KHwAddrOffset) + { + // the hardware address came back too short + // to be a valid TSockAddr.. so we'll treat it as an empty value + iHardwareAddr.SetFamily(KAFUnspec); + iHardwareAddr.SetLength(KHwAddrOffset); + } + break; + } + } + } + + +void CDHCPStateMachine::StartTimer(TTimeIntervalSeconds aSeconds, MExpireTimer& aExpireTimer) +/** + * Give the tcp/ip6 stack time to perform + * its gratuitous ARP... + * + * @internalTechnology + */ + { + CancelTimer(); + __CFLOG_VAR((KLogSubSysDHCP, KLogCode, _L8("CDHCPStateMachine::StartTimerL"))); + + iTimer->After(aSeconds, aExpireTimer); + } + +void CDHCPStateMachine::StartTimer( TTimeIntervalMicroSeconds32 aMicroSeconds, MExpireTimer& aExpireTimer) +/** + * Give the tcp/ip6 stack time to perform + * its gratuitous ARP... + * + * @internalTechnology + */ + { + CancelTimer(); + __CFLOG_VAR((KLogSubSysDHCP, KLogCode, _L8("CDHCPStateMachine::StartTimerL"))); + + iTimer->After(aMicroSeconds, aExpireTimer); + } + +void CDHCPStateMachine::RemoveConfiguredAddress( const TSoInet6InterfaceInfo& aSoInet6InterfaceInfo ) +/** + * This function can be called as a result of DAD failing + * or the lease expiring! It removes the address from the interface + * inside the TCP/IP6 stack as the address cannot continue to be used. + * + * @see "Implementation of IPv4/IPv6 Basic Socket API for Symbian OS" + * document for explanation of TSoInet6InterfaceInfo and its use + * @internalTechnology + */ + { + __CFLOG_VAR((KLogSubSysDHCP, KLogCode, _L8("CDHCPStateMachine::RemoveConfiguredAddress"))); + + TPckgBuf configInfo(aSoInet6InterfaceInfo); + // not interested in error + // how could we attempt to handle it anyway?...keep trying??? i think not... + // ensure that we have a socket to write down + iSocket.Close(); + (void)iSocket.Open(iEsock, KAfInet, KSockDatagram, KProtocolInetUdp, iConnection); + (void)iSocket.SetOpt(KSoInetConfigInterface, KSolInetIfCtrl, configInfo); + // make socket invisible for interface counting + (void)iSocket.SetOpt(KSoKeepInterfaceUp, KSolInetIp, 0); + } + +void CDHCPStateMachine::ConfigureInterfaceL( const TSoInet6InterfaceInfo& aInterfaceInfo ) +/** + * Set the interface IP address and other params + * into the TCP/IP6 stack. + * + * What we set depends on the setup + * in commDB for the service. If ipAddressFromServer + * is true then we set the ip address that has + * been assigned by DHCP, along with the netmask and gateway. + * If ipAddressFromServer is false, then we set the static ip + * address as long as it has been okayed by the DHCP server after + * we have sent an inform. We will then set the netmask and gateway + * choosing from those in commDB if they have been given values, or + * those returned in the DHCP Server ACK if a zero address is in commDB + * The same principle applies to DNS Server addresses. + * + * @internalTechnology + */ + { + + TPckgBuf configInfo(aInterfaceInfo); + + + __CFLOG_VAR((KLogSubSysDHCP, KLogCode, _L8("CDHCPStateMachine::ConfigureInterfaceL - KSoInetConfigInterface"))); + + User::LeaveIfError(iSocket.SetOpt(KSoInetConfigInterface, KSolInetIfCtrl, configInfo)); + } + +TInt CDHCPStateMachine::BindToSource() +/** + * Binds socket to newly assigned address for the interface + * + * @internalTechnology + */ + { + __CFLOG_VAR((KLogSubSysDHCP, KLogCode, _L8("CDHCPStateMachine::BindToSourceL"))); + iSocket.Close(); // destroy the old socket + + UpdateHistory(CDHCPState::EBindToSource); + // now start a new one. + // might be nice if we left here if the socket open fails...but nobody's perfect... + // PS: cannot leave here the failure doesn't mean exception here see the usage.... + TInt err = iSocket.Open(iEsock, KAfInet, KSockDatagram, KProtocolInetUdp, iConnection); + // make socket invisable for interface counting + if (err == KErrNone) + { + err = iSocket.SetOpt(KSoKeepInterfaceUp, KSolInetIp, 0); + } + + if (err == KErrNone) //PDEF122482: Enabling the ReUseAddr option. + { + err = iSocket.SetOpt(KSoReuseAddr, KSolInetIp, 1); + } + + if (err == KErrNone) + { + TInetAddr bindTo; + AssignAddresses( bindTo, iCurrentAddress ); + + err = iSocket.Bind(bindTo); + if (err==KErrNone) + { + // we are finished with this socket, + // release it so as not to hold esock open + iSocket.Close(); + } + } + return err; + } + + +TUint32 CDHCPStateMachine::GetNetworkIdL() const + { + TUint32 networkId; + _LIT(KIapNetwork, "IAP\\IAPNetwork"); + User::LeaveIfError(iConnection.GetIntSetting(KIapNetwork, networkId)); + + return networkId; + } + +void CDHCPStateMachine::AddScopeToAddrL(TInetAddr& addr) + { + TPckgBuf queryBuf; + queryBuf().iName = iInterfaceName; + User::LeaveIfError(iSocket.GetOpt(KSoInetIfQueryByName, KSolInetIfQuery, queryBuf)); + + const TUint s = addr.Ip6Address().Scope() - 1; + + if (s < 16) + { + addr.SetScope(queryBuf().iZone[s]); + } + } + +#ifdef SYMBIAN_NETWORKING_DHCPSERVER +void CDHCPStateMachine::AddScopeToClientAddrL(TInetAddr& addr) + { + TPckgBuf queryBuf; + queryBuf().iName = iInterfaceName; + User::LeaveIfError(iSvrSocket.GetOpt(KSoInetIfQueryByName, KSolInetIfQuery, queryBuf)); + + const TUint s = addr.Ip6Address().Scope() - 1; + + if (s < 16) + { + addr.SetScope(queryBuf().iZone[s]); + } + } +// Set when the DHCP server implementation is being used +void CDHCPStateMachine::SetServerState(TBool aServerImpl) + { + iServerImpl = aServerImpl; + } + +TInt CDHCPStateMachine::BindServerInterface() +/** + * Binds socket to newly assigned address for the interface + * + * @internalTechnology + */ + { + __CFLOG_VAR((KLogSubSysDHCP, KLogCode, _L8("CDHCPStateMachine::BindServerInterface"))); + iSvrSocket.Close(); // destroy the old socket + + // now start a new one. + TInt err = iSvrSocket.Open(iEsock, KAfInet, KSockDatagram, KProtocolInetUdp, iConnection); + // make socket invisable for interface counting + if (err == KErrNone) + { + TInt err = iSvrSocket.SetOpt(KSoKeepInterfaceUp, KSolInetIp, 0); + } + + if (err == KErrNone) + { + TInetAddr bindTo; + AssignAddresses( bindTo, iCurrentAddress ); + err = iSvrSocket.Bind(bindTo); + } + return err; + } + + +TInetAddr CDHCPStateMachine::GetInterfaceServerGlobalAddress() +/** + * Are any of the addresses on the interface global addresses? + * If so, DHCP might decide not to attempt to discover an address. + * + * Returns unspecified address if no global address present. + */ + { + TPckgBuf opt; + if (iSvrSocket.SubSessionHandle() == 0) + { + InitialiseServerSocketL(); + } + iSvrSocket.SetOpt(KSoInetEnumInterfaces, KSolInetIfCtrl); + __CFLOG_STMT(TBuf<512> addrStr;); + while (iSvrSocket.GetOpt(KSoInetNextInterface, KSolInetIfCtrl, opt) == KErrNone) + { + if (opt().iName == iInterfaceName) + { + TInetAddr& addr = opt().iAddress; + __CFLOG_STMT(addr.Output(addrStr);); + if ( ! addr.IsLinkLocal()) + { + __CFLOG_VAR((KLogSubSysDHCP, KLogCode, _L("Global address %S found on interface %S"),&addrStr,&iInterfaceName)); + return addr; + } + else + { + __CFLOG_VAR((KLogSubSysDHCP, KLogCode, _L("Linklocal address %S found on interface %S"),&addrStr,&iInterfaceName)); + } + break; + } + } + return TInetAddr(); // unspecified + } + + + + +#endif // SYMBIAN_NETWORKING_DHCPSERVER + +TInetAddr CDHCPStateMachine::GetInterfaceGlobalAddress() +/** + * Are any of the addresses on the interface global addresses? + * If so, DHCP might decide not to attempt to discover an address. + * + * Returns unspecified address if no global address present. + */ + { +#ifdef SYMBIAN_NETWORKING_DHCPSERVER + if(iServerImpl) + { + return GetInterfaceServerGlobalAddress(); + } + else + { +#endif // SYMBIAN_NETWORKING_DHCPSERVER + + TPckgBuf opt; + if (iSocket.SubSessionHandle() == 0) + { + InitialiseSocketL(); + } + iSocket.SetOpt(KSoInetEnumInterfaces, KSolInetIfCtrl); + __CFLOG_STMT(TBuf<512> addrStr;); + while (iSocket.GetOpt(KSoInetNextInterface, KSolInetIfCtrl, opt) == KErrNone) + { + if (opt().iName == iInterfaceName) + { + TInetAddr& addr = opt().iAddress; + __CFLOG_STMT(addr.Output(addrStr);); + if ( ! addr.IsLinkLocal()) + { + __CFLOG_VAR((KLogSubSysDHCP, KLogCode, _L("Global address %S found on interface %S"),&addrStr,&iInterfaceName)); + return addr; + } + else + { + __CFLOG_VAR((KLogSubSysDHCP, KLogCode, _L("Linklocal address %S found on interface %S"),&addrStr,&iInterfaceName)); + } + break; + } + } + return TInetAddr(); // unspecified + +#ifdef SYMBIAN_NETWORKING_DHCPSERVER + } +#endif // SYMBIAN_NETWORKING_DHCPSERVER + } + + +TBool CDHCPStateMachine::DoesInterfaceKnowAnyDNSServers() +/** + * Does the interface know of any DNS servers? + */ + { + TPckgBuf opt; + if (iSocket.SubSessionHandle() == 0) + { + InitialiseSocketL(); + } + iSocket.SetOpt(KSoInetEnumInterfaces, KSolInetIfCtrl); + __CFLOG_STMT(TBuf<512> addrStr;); + while (iSocket.GetOpt(KSoInetNextInterface, KSolInetIfCtrl, opt) == KErrNone) + { + if (opt().iName == iInterfaceName) + { + TInetAddr& addr = opt().iNameSer1; + __CFLOG_STMT(addr.Output(addrStr);); + if ( ! addr.IsUnspecified() ) + { + __CFLOG_VAR((KLogSubSysDHCP, KLogCode, _L("DNS server %S found on interface %S"),&addrStr,&iInterfaceName)); + return ETrue; + } + else + { + __CFLOG_VAR((KLogSubSysDHCP, KLogCode, _L("No DNS servers already set on interface %S (prior to DHCP)"),&iInterfaceName)); + } + break; + } + } + return EFalse; + } + + + + +TDhcpRnd::TDhcpRnd():iXid(0) +/** + * Constructor for this little random number + * class that creates us a random transaction id + * + * @internalTechnology + */ + { + TTime now; + now.HomeTime(); + iSeed = now.Int64(); + } + +TInt TDhcpRnd::Rnd(TInt aMin, TInt aMax) +/** + * Utility class function to generated a real + * random number + * + * @internalTechnology + */ + { + return Math::Rand(iSeed)%(aMax-aMin+1)+aMin; + }