linklayerprotocols/pppnif/SPPP/MSCBCP.CPP
changeset 0 af10295192d8
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/linklayerprotocols/pppnif/SPPP/MSCBCP.CPP	Tue Jan 26 15:23:49 2010 +0200
@@ -0,0 +1,521 @@
+// Copyright (c) 1997-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:
+//
+
+#include <in_iface.h>
+#include "MSCBCPC.H"
+
+
+//
+
+
+/*
+   Exactly one Callback Control Protocol packet is encapsulated
+   in the Information field of a PPP Data Link Layer frame.
+   A summary of the CBCP packet format is shown below.  The
+   fields are transmitted from left to right.
+    0                   1                   2                   3
+    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+   |     Code      |  Identifier   |            Length             |
+   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+   |    Data ...   +-+-+-+-+   Code
+      The Code field is one octet and identifies the type of CBCP
+      packet.  CBCP Codes are assigned as follows:
+	     1       Callback Request       ( Answerer -> Caller )
+         2       Callback Response      ( Caller   -> Answerer )
+         3       Callback Ack           ( Answerer -> Caller )
+	Identifier
+      The Identifier field is one octet and aids in matching requests
+      and replies.   Length
+      The Length field is two octets and indicates the length of the
+      CBCP packet including the Code, Identifier, Length and Data
+      fields.  Octets outside the range of the Length field should be
+      treated as Data Link Layer padding and should be ignored on
+      reception.
+
+
+  3.2 Callback Configuration Options
+
+    0                   1                   2
+    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3
+   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+   | Callback Type |    Length     |Callback delay |
+   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+   | Callback Address(es) ...
+   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-
+
+    Callback Type
+
+    1 - No callback
+    2 - Callback to a user-specifiable number.
+    3 - Callback to a pre-specified or administrator specified number.
+    4 - Callback to any of a list of numbers.
+
+
+    Length
+      The Length field is one octet and indicates the length of the
+      Callback Option including the Type, Length and Data fields.
+      >=3
+
+    Callback delay
+        The amount of time is seconds the Answerer MUST wait before
+        calling the Caller back.  Answerer sets to 0, Caller MAY change if
+        the required value if is different from 0.
+        <= 255
+
+
+    Callback Addresses
+
+    0                   1                   2
+    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3
+   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+   | Address Type  |    ASCIIZ address
+   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+        Address Type
+
+            1 - for PSTN/ISDN
+
+            Other? (TBD)
+
+        Address Format
+
+            For PSTN/ISDN this is an NULL terminated ASCII string.
+
+            Valid characters are 0-9, *, #, T, P, W, @, comma, space,
+            dash, and parentheses."
+
+
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+--+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+//-+-----+
+|  Code | Id  |    Length     |CBckType| Len | delay | 1   | phone number  |  0  |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+--+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+//-+-----+
+    0      1     2       3        4       5      6      7     8    ..
+*/
+
+
+const TUint KPppIdNcpMsCbcp = 0xC029;
+
+const TUint8 KPppMsCbcpOptRequest  = 1;	// Callback Request  ( Answerer -> Caller )
+const TUint8 KPppMsCbcpOptResponse = 2;	// Callback Response ( Caller   -> Answerer )
+const TUint8 KPppMsCbcpOptAck     = 3;	// Callback Ack      ( Answerer -> Caller )
+
+enum TMsCbcpCallbackOptType
+		{
+		EMsCbcpCallbackOptTypeNoCallback = 1,
+		EMsCbcpCallbackOptTypeUserSpecified = 2,
+		EMsCbcpCallbackOptTypeServerSpecified = 3,
+		EMsCbcpCallbackOptTypeList = 4
+		};
+
+#define PKT_BUF_SIZE			256
+#define RESPONSE_BUF_SIZE		PKT_BUF_SIZE
+#define KCbcpMaxRequestLength	256	// maximum length allowed for an incoming MsCbcpOptRequest pkt
+
+/**
+'MS CBCP-client' class (a client is the machine that dials out to request callback)
+@internalComponent
+*/
+NONSHARABLE_CLASS(CPppMsCbcpClient) : public CBase, public MPppRecvr
+	{
+	friend class PppNcpMsCbcpFactory;
+public:
+	CPppMsCbcpClient(	CPppLcp * aLcp,
+						TMSCBCPAction aAction,
+						const TUint8 * aCallbackInfo,
+						const TInt aCallbackInfoLen );
+	~CPppMsCbcpClient();
+	void ConstructL();
+private:
+	// from MPppRecvr: upcalls
+	virtual TBool RecvFrame(RMBufChain& aPacket);		// from MPppRecvr
+	virtual void LowerLayerUp();						// from MPppRecvr
+	virtual void LowerLayerDown(TInt aStatus);			// from MPppRecvr
+	virtual void KillProtocol();
+	virtual void FrameError();
+
+private:
+	INLINE void ValidateArgumentsL() const;
+	INLINE void HandleRXOfferFromServer(const TUint8 * aPacket, TInt aLen );
+	INLINE TBool GenerateResponseToServersOffer(	TBool&		aServerOfferAcceptable,
+									TUint8 *		aResponse,
+									TInt&			aResponseLen,
+									const TUint8 *	aOfferPktFromServer,	// recvd packet
+									TInt			aOfferPktFromServerLen);
+	INLINE void ProcessRecvdFrame(const TUint8 * aPacket, TInt aLen );
+	INLINE void SendResponse(const TUint8 * aResponse, TInt aResponseLen );
+	INLINE TBool IsCallbackOfferAcceptable( TMsCbcpCallbackOptType aRecvdCallbackType ) const;
+	INLINE TBool AcceptServerSpecifiedNumber() const;
+	INLINE TInt GetCallbackResponsePhoneNumber(
+											TUint8 *				aResultPhoneNumber,	// output
+											TMsCbcpCallbackOptType	aRecvdCallbackType ) const;
+	INLINE void RecvdAck();
+	INLINE TInt GetClientPhoneNumber( TUint8 * aResultPhoneNumber ) const;
+private:
+	CPppLcp * const			iLcp;
+	TBool					iServerOfferAcceptable;	// True if the server has made an acceptable offer of callback to use
+	const TMSCBCPAction		iAction;
+	const TUint8 * const	iCallbackInfo;
+	const TInt				iCallbackInfoLen;
+	TInt					iResponseLen;
+	TUint8					iResponseBuf[ RESPONSE_BUF_SIZE ];
+	TBool					iDead;
+	};
+
+#define LOG1( p1 )				LOG( Log::Printf(_L(p1) ) )
+#define LOG2( p1, p2 )			LOG( Log::Printf(_L(p1), (p2) ) )
+#define LOG3( p1, p2, p3 )		LOG( Log::Printf(_L(p1), (p2), (p3) ) )
+#define LOG4( p1, p2, p3, p4 )	LOG( Log::Printf(_L(p1), (p2), (p3), (p4) ) )
+
+
+
+CPppMsCbcpClient * PppNcpMsCbcpFactory::NewL(	CPppLcp * aLcp,
+												TMSCBCPAction aAction,
+												const TUint8 * aCallbackInfo,
+												const TInt aCallbackInfoLen )
+	{
+	CPppMsCbcpClient * p;
+
+	p = new (ELeave) CPppMsCbcpClient(aLcp, aAction, aCallbackInfo, aCallbackInfoLen );
+	CleanupStack::PushL( p );
+	p->ConstructL();
+	CleanupStack::Pop();
+	return p;
+	}
+
+void PppNcpMsCbcpFactory::Delete( CPppMsCbcpClient * instance)
+	{
+	delete instance;
+	}
+
+CPppMsCbcpClient::CPppMsCbcpClient(	CPppLcp *		aLcp,
+									TMSCBCPAction	aAction,
+									const TUint8 *	aCallbackInfo,
+									const TInt		aCallbackInfoLen )
+	:	MPppRecvr(aLcp, EPppPhaseLateCallback, KPppIdNcpMsCbcp ),
+		iLcp( aLcp ),
+		iServerOfferAcceptable( EFalse ),
+		iAction( aAction ),
+		iCallbackInfo( aCallbackInfo ),
+		iCallbackInfoLen( aCallbackInfo ? aCallbackInfoLen : 0 ),
+		iDead( EFalse )
+	{
+	__ASSERT_DEBUG( iCallbackInfoLen >=0, PppPanic(EPppPanic_PPPNoCallbackInfo) );
+	__ASSERT_DEBUG( iLcp, PppPanic(EPppPanic_PPPLcpRequired) );
+	}
+
+CPppMsCbcpClient::~CPppMsCbcpClient()
+	{
+	}
+
+void CPppMsCbcpClient::ConstructL()
+	{
+	ValidateArgumentsL();
+	Register();
+	}
+
+//
+// CRC Error for a frame deliverred to this protocol
+// No use to this module
+//
+void CPppMsCbcpClient::FrameError()
+	{
+	}
+
+//
+// Someone rejected this we should not receive any more 
+// frames on it.
+//
+void CPppMsCbcpClient::KillProtocol()
+	{
+	iDead = ETrue;
+	}
+
+void CPppMsCbcpClient::ValidateArgumentsL() const
+	{
+	if (RESPONSE_BUF_SIZE < (8+1+iCallbackInfoLen))
+		{
+		User::Leave(KErrArgument);
+		}
+	}
+
+void CPppMsCbcpClient::LowerLayerUp()
+	{
+	// TBS
+	}
+
+void CPppMsCbcpClient::LowerLayerDown(TInt /*aStatus*/)
+	{
+	}
+
+
+TBool CPppMsCbcpClient::RecvFrame(RMBufChain& aPacket)
+	{
+	RMBufPacket pkt;
+	pkt.Assign(aPacket);
+	pkt.Unpack();
+
+	if (iDead)
+		{
+		// Link dead
+		pkt.Free();
+		return EFalse;
+		}
+
+	const TUint rmbuflen = pkt.Info()->iLength;
+
+	// Extract and drop LCP header
+	pkt.Align(4);
+	const TUint8 * ptr = pkt.First()->Ptr();
+
+	TUint len = BigEndian::Get16( ptr+2 );
+	
+	// Check packet length is OK
+	if (rmbuflen < len)
+		{
+		// Too short!
+		pkt.Free();
+		return EFalse;
+		}
+	ProcessRecvdFrame( ptr, len );
+	pkt.Free();
+	return EFalse;
+	}
+
+void CPppMsCbcpClient::HandleRXOfferFromServer(const TUint8 * aPacket, TInt aLen )
+	{
+	if (! GenerateResponseToServersOffer(	iServerOfferAcceptable,
+									iResponseBuf,
+									iResponseLen,
+									aPacket,
+									aLen))
+		{
+		// if invalid or unacceptable, take it all down
+		iPppLcp->TerminateLink(MNifIfNotify::EDisconnect, KErrIfCallbackNotAcceptable);	// all done, shut it all down
+		return;	
+		}
+
+	SendResponse( iResponseBuf, iResponseLen );
+	}
+
+
+void CPppMsCbcpClient::RecvdAck()
+	{
+
+	iLcp->CallbackGrantedAndAuthenticated();
+	iPppLcp->TerminateLink(MNifIfNotify::ECallBack);	// all done, shut it all down
+	}
+
+
+void CPppMsCbcpClient::SendResponse(const TUint8 * aResponsePkt, TInt aResponsePktLen )
+	{
+	RMBufPacket pkt;
+	RMBufPktInfo* info = NULL;
+	const TInt len = aResponsePktLen;
+
+	TRAPD(err, pkt.AllocL(len));
+	if (err!=KErrNone)
+		return;
+
+	TRAP(err, info = pkt.NewInfoL());
+	if (err!=KErrNone)
+		{
+		pkt.Free();
+		return;
+		}
+	TUint8 * const ptr = pkt.First()->Ptr();
+	Mem::Copy( ptr, aResponsePkt, aResponsePktLen );
+	info->iLength = len;	
+	TPppAddr::Cast((info->iDstAddr)).SetProtocol(iPppId);
+	pkt.Pack();
+	SendFrame(pkt);
+	}
+
+const TInt KMsCbcpCallbackDelay = 2;	// callback delay in seconds suggested to server
+
+TBool CPppMsCbcpClient::IsCallbackOfferAcceptable( TMsCbcpCallbackOptType aRecvdCallbackType ) const
+// Return TRUE if the servers offer of callback is acceptable
+	{
+	switch ( aRecvdCallbackType )
+		{
+	case EMsCbcpCallbackOptTypeNoCallback:	// 1
+		return EFalse;
+	case EMsCbcpCallbackOptTypeUserSpecified:	// 2
+		return ETrue;
+	case EMsCbcpCallbackOptTypeServerSpecified:	// 3
+		return AcceptServerSpecifiedNumber();
+	case EMsCbcpCallbackOptTypeList: // 4
+		return EFalse;
+	default:
+		return EFalse;	// recvd a bad garbage thing
+		}
+	}
+
+INLINE TBool CPppMsCbcpClient::AcceptServerSpecifiedNumber() const
+// Returns true if we are willing to accept a phone number specified by the server, rather than one we've specified ourselves.
+	{
+	return iAction != EMSCBCPActionRequireClientSpecifiedNumber;
+	}
+
+
+
+INLINE TInt ResponseLength( TMsCbcpCallbackOptType responseCallbackType, TInt responsePhoneNumberLen, TInt aRequestFromSrvLen )
+// Return the total length of a response packet of type 'responseCallbackType'
+// including a phone number of length responsePhoneNumberLen, if applicable
+// Note: responsePhoneNumberLen does NOT include any trailing zero on the phone number
+	{
+	switch ( responseCallbackType )
+		{
+	case EMsCbcpCallbackOptTypeUserSpecified:
+		return (8 + 1 + responsePhoneNumberLen);	// +1 for zt
+	case EMsCbcpCallbackOptTypeServerSpecified:
+		return aRequestFromSrvLen+3;
+	default:
+		return 0;				
+		}
+	}
+
+TBool CPppMsCbcpClient::GenerateResponseToServersOffer(	TBool&			aServerOfferAcceptable,	// out
+												TUint8 *		aResponse,				// out: resulting response packet
+												TInt&			aResponseLen,			// out: len of above
+												const TUint8 *	aOfferPktFromServer,			// recvd packet
+												TInt			aOfferPktFromServerLen)
+// Analyses the given received recvd MsCbcpOptRequest packet 'aOfferPktFromServer'
+// If it is valid,
+//		writes an appropriate response packet at aResponse, aResponseLen
+// Sets caller's aServerOfferAcceptable true/false according to whether the server's offer of callback is acceptable
+// Returns True if the recvd MsCbcpOptRequest packet is valid and acceptable
+// Returns False if its corrupt or unacceptable, in which case we must terminate the link
+	{
+
+	if ( aOfferPktFromServerLen > KCbcpMaxRequestLength )
+		return EFalse;
+	if ( aOfferPktFromServerLen <= 4 )
+		return EFalse;
+
+	//
+	// We can receive multiple options in that case we have to decide which 
+	// to use. Specifically in the case off a user specified number to be called
+	// back on the options are:
+	// 1. No Callback and
+	// 2. Caller specifies the number
+	// PRR 8/4/98
+	//
+	TInt	lengthProcessed=4; // Used to keep track when we're offerred multiple options
+	TBool	callbackok = EFalse;
+	TInt	responsePhoneNumberLen;
+	TMsCbcpCallbackOptType recvdCallbackType;
+
+	do
+		{
+		recvdCallbackType = TMsCbcpCallbackOptType( aOfferPktFromServer[lengthProcessed] );
+
+		//
+		// If someone offers us No Call back then we try to match any other option 
+		// they may send, but we have this as a default to go back to.
+		callbackok = IsCallbackOfferAcceptable( recvdCallbackType );
+
+		if (callbackok)
+			{
+			break;
+			}
+
+		lengthProcessed += aOfferPktFromServer[lengthProcessed+1];
+		} while ( lengthProcessed < aOfferPktFromServerLen );
+
+	if (!callbackok)
+		{
+		return EFalse;	// cannot agree
+		}
+
+	TMsCbcpCallbackOptType responseCallbackType = recvdCallbackType;
+
+	responsePhoneNumberLen = GetCallbackResponsePhoneNumber(	aResponse+8,	// result phone number
+																recvdCallbackType );
+
+
+	*(aResponse+8 + responsePhoneNumberLen) = 0;	// zero-terminate it
+
+	TInt responselen = ResponseLength( responseCallbackType, responsePhoneNumberLen, aOfferPktFromServerLen-lengthProcessed );
+
+	aResponse[0] = KPppMsCbcpOptResponse;
+	aResponse[1] = aOfferPktFromServer[1];		// id
+	BigEndian::Put16( aResponse+2, TUint16(responselen) );
+	aResponse[4] = TUint8(responseCallbackType);
+	aResponse[5] = TUint8( responselen - 4 ); 
+	if (recvdCallbackType == EMsCbcpCallbackOptTypeUserSpecified)
+		{
+		aResponse[6] = KMsCbcpCallbackDelay;
+		}
+	else
+		{
+		aResponse[6] = aOfferPktFromServer[6+lengthProcessed];
+		}
+	aResponse[7] = 1;	// PSTN Address type
+
+	aResponseLen = responselen;				// return result
+	aServerOfferAcceptable = callbackok;	// return result
+	return ETrue;
+	}
+
+TInt CPppMsCbcpClient::GetClientPhoneNumber( TUint8 * aResultPhoneNumber ) const
+	{
+	Mem::Copy( aResultPhoneNumber, iCallbackInfo, iCallbackInfoLen );
+	return iCallbackInfoLen;
+	}
+
+TInt CPppMsCbcpClient::GetCallbackResponsePhoneNumber(
+											TUint8 *				aResultPhoneNumber,	// output
+											TMsCbcpCallbackOptType	aRecvdCallbackType ) const
+// Works out what the phone number should be in our response packet - our response to the server's offer
+// Outputs:
+//	Writes the phone number at aResultPhoneNumber
+//	Returns the length of the result
+//
+// Given the received
+	{
+	switch ( aRecvdCallbackType )
+		{
+	case EMsCbcpCallbackOptTypeUserSpecified: //= 2,
+		return GetClientPhoneNumber( aResultPhoneNumber );
+	case EMsCbcpCallbackOptTypeServerSpecified: //= 3,
+		return 0; // We have no say over the phone number, we do as we're bid PRR
+	default:		
+        __ASSERT_DEBUG( EFalse, PppPanic(EPppPanic_PPPInvalidCallback) );
+		}
+	return 0;
+	}
+
+
+void CPppMsCbcpClient::ProcessRecvdFrame(const TUint8 * aPacket, TInt aLen )
+	{
+	TUint op = aPacket[0];
+
+	TInt len = BigEndian::Get16( aPacket + 2);
+	if ( len <=4 || len > aLen )
+		return;
+
+	switch (op)
+		{
+	case KPppMsCbcpOptRequest:
+		HandleRXOfferFromServer( aPacket, len );
+		break;
+	case KPppMsCbcpOptAck:
+		RecvdAck();
+		break;
+	default:
+		break;
+		}
+	}
+