/*++

******************************************************************************
* Copyright (c) 1990-96 Digi International Inc., All Rights Reserved         *
*                                                                            *
* This software contains proprietary and confidential information of Digi    *
* International Inc.  By accepting transfer of this copy, Recipient agrees   *
* to retain this software in confidence, to prevent disclosure to others,    *
* and to make no use of this software other than that for which it was       *
* delivered.  This is an unpublished copyrighted work of Digi International  *
* Inc.  Except as permitted by federal law, 17 USC 117, copying is strictly  *
* prohibited.                                                                *
*                                                                            *
* Restricted Rights Legend                                                   *
*                                                                            *
* Use, duplication, or disclosure by the Government is subject to            *
* restrictions set forth in sub-paragraph (c)(1)(ii) of The Rights in        *
* Technical Data and Computer Software clause at DFARS 252.227-7031 or       *
* subparagraphs (c)(1) and (2) of the Commercial Computer Software -         *
* Restricted Rights at 48 CFR 52.227-19, as applicable.                      *
*                                                                            *
* Digi International Inc. 11001 Bren Road East, Minnetonka, MN 55343         *
*                                                                            *
******************************************************************************

Module Name:

   x25route.c

Abstract:

   This module contains the X25 address routing functionality for this driver.
	The router is based on lists read from the registry.  X.25 addresses and 
	the corresponding DigiSxb devices on which they should be routed are read 
	and stored in a singly linked list.  
	
	An address to be routed is compared against addresses in the list.  The call
	is routed out the first matching address, taking into account * and ? 
	wildcards.  The wildcards work just as in DOS, where * matches any number of
	digits (1-max), and ? matches only a single digit.  Thus, the order in which
	the table is built is significant.  For example, if the route entries were
	as follows:

		Address		Device
		123*			DigiSxb2A
		12*			DigiSxb2B
		12345			DigiSxb6A

	Then the address 12345 would always be routed out DigiSxb2A, not 2B or 6A.
	Also, address 11111 would fail to be routed, since no valid route exists.

Author:

   Michael Zarns

Revision History:

   $History: x25route.c $
 * 
 * *****************  Version 6  *****************
 * User: Mikez        Date: 4/28/97    Time: 1:55p
 * Updated in $/Components/Windows/NT/Sync/Wan
 * Updated to use DsApi library.  Uses IoBuildDeviceIoControlRequest to
 * alloc Irps causing two problems.  Because ioctls are METHOD_BUFFERED, a
 * systemBuffer is alloc'd, but, because the completionroutines return
 * STATUS_MORE_PROCESSING_REQUIRED, never gets freed.  Also, SystemBuffer
 * is not copied to OutputBuffer until after completionRoutine returns
 * STATUS_SUCCESS.  Need to move from METHOD_BUFFER or from using
 * IoBuildDeviceIoControlRequest.
 * 
 * *****************  Version 5  *****************
 * User: Mikez        Date: 3/19/97    Time: 9:13a
 * Updated in $/Components/Windows/NT/Sync/Wan
 * Alloc route string buffers from NonPagedPool and copy from PagedPool
 * buffers alloc'd by Ndis at init time.
 * 
 * *****************  Version 4  *****************
 * User: Mikez        Date: 2/26/97    Time: 10:21a
 * Updated in $/Components/Windows/NT/Sync/Wan
 * Fixed bug in routing algorithm which made all masks operate as if they
 * ended in a '*' wildcard.
 * 
 * *****************  Version 3  *****************
 * User: Mikez        Date: 12/06/96   Time: 2:28p
 * Updated in $/Components/Windows/NT/Sync/Wan
 * Modified to support new SXB paridigm of "one NT device per adapter"
 * rather than the older "one NT device per port".
 * 
 * *****************  Version 2  *****************
 * User: Mikez        Date: 10/28/96   Time: 1:25p
 * Updated in $/Components/Windows/NT/Sync/Wan
 * Updated to support latest firmware release, v1.1.2b.

--*/
#include <ntddk.h>
#include <ntddndis.h>
#include <ndis.h>
#include <ndiswan.h>

#include "DsApi.h"
#include "digix25.h"
#include "digix25p.h"
#include "digifile.h"
#include <digialloc.h>


#define REGKEY_ROUTEADDRESS NDIS_STRING_CONST("RouteAddress")
#define REGKEY_ROUTEDEVICES NDIS_STRING_CONST("RouteAlias")

#define REGKEY_DEVICE         NDIS_STRING_CONST("Device")
#define REGKEY_ALIAS          NDIS_STRING_CONST("Alias")
#define REGKEY_SNID           NDIS_STRING_CONST("Snid")


// The global list of alias table entries
PALIAS_ENTRY gpAliasList = NULL;

// The global list of router table entries.
PROUTE_ENTRY gpRouteList = NULL;

// The MULTI_SZ strings read from the registry are copied into NonPagedPool and 
// held by these globals.
ANSI_STRING gAddrMasks;
UNICODE_STRING gDevices;
UNICODE_STRING gAliases;



NDIS_STATUS
X25Router(
	OUT PDIGIWAN_PHYS_ADAPTER * pPhysAdapter,
	OUT PULONG pSnid,
	IN PUCHAR X25Address,
	IN ULONG Length
	)
/*++

Description:

	This routine is used to route X.25 addresses onto specific adapter/line pair.
	The X25 address specified by DestAddress is checked against the internal routing
	table.  Device handle, hDevice, and Snid are returned if a valid route exists.

Parameters:

	pPhysAdapter - pointer to location in which to write the physAdapter on which to
	               direct the call.

	pSnid - pointer to location in which to write the Snid (line or port identifier)
	        on which to direct the call.

	X25Address - pointer to ascii string containing the X25 address (need not be 
	             null-terminated)

	Length - length of string pointed to by X25Address

Return Value:

	NDIS_STATUS_SUCCESS indicates valid values were written to phDevice and pSnid
	NDIS_STATUS_FAILURE otherwise.

--*/
{
	PROUTE_ENTRY pRoute = gpRouteList;
	NDIS_STATUS Status;
	BOOLEAN bFound = FALSE;
	ULONG RealLength;

	// Strip off terminating null
	RealLength = X25Address[Length-1] ? Length : Length - 1;

	while ( pRoute )
	{
		// Get the route address mask
		PUCHAR Mask = pRoute->Address.Buffer;
		USHORT MaxLen = pRoute->Address.Length;

		if ( Mask[0] == '*' )
		{
			// '*' matches all addresses!
			bFound = TRUE;
			break;
		}

		// If the requested address is shorter than the RouteTable address mask,
		// there's no way it could match.  Only compare strings if !shorter than mask.

		if ( RealLength >= pRoute->Address.Length )
		{
			USHORT i;
			// compare all digits
			for ( i = 0; i < MaxLen; i++ )
			{
				if ( X25Address[ i ] == Mask[ i ] )
				{
					// This digit matches...
					bFound = TRUE;
					continue;
				}
				else 
				{
					switch ( Mask[i] )
					{
						case '*':
							// Asterisk matches all remaining digits, get outta here.
							bFound = TRUE;
							break;

						case '?':
							// QuestionMark matches any single digit...
							if ( RealLength == pRoute->Address.Length )
								bFound = TRUE;
							break;

						default:
							// Not a match!
							bFound = FALSE;
							break;
					}
					// Done!
					break;
				}
			}
		}
		if ( bFound )
			break;

		// Not found, try next route
		pRoute = pRoute->pNext;
	}

	if ( bFound )
	{
		// Found a match!  Fill in return values
		*pPhysAdapter = pRoute->pAlias->PhysAdapter;
		*pSnid = pRoute->pAlias->Snid;
		Status = NDIS_STATUS_SUCCESS;
	}
	else
		Status = NDIS_STATUS_FAILURE;

	return( Status );
}



NDIS_STATUS
BuildX25RouterTable(
	NDIS_HANDLE RegistryHandle,
	PLIST_ENTRY PhysAdapters
	)
/*++

Description:

	Builds the routing table to be used by X25Router.  There is (and must be) a direct 
	one-to-one relationship between the addresses in pAddresses and the devicenames in
	pDevices.  The first address maps to the first device, second address to second
	device, etc.  The order is also significant, addresses are always matched against
	those in the table *in order* and the first to match defines the route.

Parameters:

	RegistryHandle - handle used to access registry.  This is the handle returned from 
	                 a previous call to NdisOpenConfiguration.

	PhysAdapters - pointer to head of PhysAdapters list.  The physAdapter objects define
	               the adapters and contain the handle used to access the adapters.

Return Value:

	NDIS_STATUS_SUCCESS if successfully built table, else
	NDIS_STATUS_FAILURE

--*/
{
	NDIS_STRING RouteAddressesStr = REGKEY_ROUTEADDRESS;
	ANSI_STRING RouteAddresses;
	ULONG AddrOffset;

	NDIS_STRING RouteDevicesStr = REGKEY_ROUTEDEVICES;
	NDIS_STRING RouteDevices;
	ULONG DevOffset;

   PNDIS_CONFIGURATION_PARAMETER RegValue;	// Value read from registry
	PROUTE_ENTRY pPrevious = NULL;
	NDIS_STATUS Status;

	// Get list of router table addresses

	NdisReadConfiguration( &Status,
	                       &RegValue,
	                       RegistryHandle,
	                       &RouteAddressesStr,
	                       NdisParameterString );
	if ( !NT_SUCCESS( Status ) )
		return( Status );
	Status = RtlUnicodeStringToAnsiString( &RouteAddresses,
	                                       &RegValue->ParameterData.StringData, TRUE );
	if ( !NT_SUCCESS( Status ) )
		return( Status );

	gAddrMasks.Length        = RouteAddresses.Length;
	gAddrMasks.MaximumLength = RouteAddresses.MaximumLength;
	gAddrMasks.Buffer        = DigiAllocMem( NonPagedPool, gAddrMasks.MaximumLength );
	if ( !gAddrMasks.Buffer )
		return( NDIS_STATUS_RESOURCES );

	RtlZeroMemory( gAddrMasks.Buffer, gAddrMasks.MaximumLength );
	RtlCopyMemory( gAddrMasks.Buffer, RouteAddresses.Buffer, gAddrMasks.Length );

	// Get list of corresponding router table device aliases

	NdisReadConfiguration( &Status,
	                       &RegValue,
	                       RegistryHandle,
	                       &RouteDevicesStr,
	                       NdisParameterString );

	if ( !NT_SUCCESS( Status ) )
		return( Status );

	RouteDevices = RegValue->ParameterData.StringData;

	// Alloc and init entries for each address in gAddrMasks

	AddrOffset = DevOffset = 0;
	while ( gAddrMasks.Buffer[ AddrOffset ] )
	{
		PROUTE_ENTRY pRoute;
		BOOLEAN bFound;
		PALIAS_ENTRY pAlias;
		UNICODE_STRING AliasName;

		if ( RouteDevices.Buffer[ DevOffset/2 ] == 0 )
			// Hit end of Devices string before end of Addresses string!
			// Configuration error!
			return( NDIS_STATUS_FAILURE );

		pRoute = DigiAllocMem( NonPagedPool, sizeof( ROUTE_ENTRY ) );
		if ( !pRoute )
			return( NDIS_STATUS_RESOURCES );

		NdisZeroMemory( pRoute, sizeof( ROUTE_ENTRY ) );
		pRoute->Type = DIGIWAN_TYPE_ROUTE_ENTRY;
		
		// Get this entry's X.25 address

		RtlInitAnsiString( &pRoute->Address, 
		                   &gAddrMasks.Buffer[ AddrOffset ] );
		if ( !pRoute->Address.Length )
			// Invalid address
			return( NDIS_STATUS_FAILURE );

		// Get this entry's DeviceName alias

		RtlInitUnicodeString( &AliasName, 
		                      &RouteDevices.Buffer[ DevOffset/2 ] );

		if ( !AliasName.Length )
			// Invalid address
			return( NDIS_STATUS_FAILURE );

		bFound = FALSE;

		// Associate this route enty with its corresponding alias
		for ( pAlias = gpAliasList; pAlias; pAlias = pAlias->pNext )
		{
			ASSERT( pAlias->Type == DIGIWAN_TYPE_ALIAS_ENTRY );
			if ( RtlEqualUnicodeString( &AliasName, &pAlias->Alias, TRUE ) )
			{
				bFound = TRUE;
				break;
			}
		}

		if ( bFound )
			pRoute->pAlias = pAlias;
		else
			// No physAdapter for this entry.  Must leave it in the route table, but
			// when a request comes down to route on this link, it'll get bounced.
			DigiDump( DIGIINIT, ("Router: Alias %wZ not found.\n", &AliasName) );

		// Add this entry to the route list
		if ( pPrevious )
		{
			pPrevious->pNext = pRoute;
			pPrevious = pPrevious->pNext;
		}
		else
		{
			gpRouteList = pRoute;
			pPrevious = pRoute;
		}

		// Skip to next address and device strings
		AddrOffset += pRoute->Address.Length + 1;						 // add 1 for ascii null
		DevOffset += AliasName.Length + sizeof( WCHAR ); // add for unicode null
	}

	ASSERT( gAddrMasks.Buffer[ AddrOffset ] == 0 );

	if ( RouteDevices.Buffer[ DevOffset/2 ] )
		// Configuration error - Devices string has more entries than the addresses did!
		return( NDIS_STATUS_FAILURE );

#if DBG
{
	PROUTE_ENTRY MyRoute = gpRouteList;

	DigiDump( DIGIX25ROUTE, ("X25Router: X25 Routing Table\n") );
	while ( MyRoute )
	{
		ASSERT( MyRoute->Type == DIGIWAN_TYPE_ROUTE_ENTRY );
		DigiDump( DIGIX25ROUTE, ("   Address: %s\n"
		                         "      DeviceName: %wZ\n"
		                         "      Snid: %c (0x%x)\n"
		                         "      PhysAdapter: 0x%x\n"
		                         "      hDevice: 0x%x\n",
										 MyRoute->Address.Buffer,
										 &MyRoute->pAlias->DeviceName,
										 MyRoute->pAlias->Snid, MyRoute->pAlias->Snid,
										 MyRoute->pAlias->PhysAdapter,
										 MyRoute->pAlias->PhysAdapter->hDevice) );
		MyRoute = MyRoute->pNext;
	}
}
#endif

	return( NDIS_STATUS_SUCCESS );
}



NDIS_STATUS
BuildX25AliasTable(
	NDIS_HANDLE RegistryHandle
	)
/*++

Description:

	Builds the alias table used by X25Router.  Takes a registry access handle
	to retrieve registry settings to map Aliases (or "friendly names") to
	device/snid (adapter/port) pairs.
	
	There is (and must be) a direct one-to-one relationship between the aliases 
	in Aliases and the devicenames in Devices, as well as the snids in Snids.  
	The first alias maps to the first device and first snid, second address to 
	second device and snid, etc.  

Parameters:

	RegistryHandle - Handle returned from NdisOpenConfiguration.

Return Value:

	NDIS_STATUS_SUCCESS if successfully built table, else
	NDIS_STATUS_FAILURE

--*/
{
	NDIS_STATUS Status;
   PNDIS_CONFIGURATION_PARAMETER ReturnedValue;	// Value read from registry
	UCHAR i;

	NDIS_STRING DevicesStr = REGKEY_DEVICE;
	NDIS_STRING Devices;
   NDIS_STRING Device;

	NDIS_STRING AliasesStr = REGKEY_ALIAS;
	NDIS_STRING Aliases;
   NDIS_STRING Alias;

	NDIS_STRING SnidsStr = REGKEY_SNID;
	NDIS_STRING Snids;
   NDIS_STRING Snid;

	PALIAS_ENTRY PreviousAlias = NULL;

   // Get the Devices multistring.  This string contains a list of sxb devices
	// (or adapters) to be used.
   NdisReadConfiguration( &Status,
                          &ReturnedValue,
                          RegistryHandle,
                          &DevicesStr,
                          NdisParameterString );
   if( !NT_SUCCESS( Status ) )
   {
      DigiDump( DIGIERRORS, ("Unable to find Devices multistring!\n", 
                            DevicesStr) );
		goto BuildX25AliasTableExit;
   }

	Devices = ReturnedValue->ParameterData.StringData;

   DigiDump( DIGIINIT, ("Devices = %wZ", &Devices ) );
	DigiDump( DIGIINIT, ("\n") );

	gDevices.Length        = Devices.Length;
	gDevices.MaximumLength = Devices.MaximumLength;
	gDevices.Buffer        = DigiAllocMem( NonPagedPool, gDevices.MaximumLength );
	if ( !gDevices.Buffer )
		return( NDIS_STATUS_RESOURCES );

	RtlZeroMemory( gDevices.Buffer, gDevices.MaximumLength );
	RtlCopyMemory( gDevices.Buffer, Devices.Buffer, gDevices.Length );

   // Get the Aliases multistring.  This string contains a list of aliases to 
   // sxb devices to be used.
   NdisReadConfiguration( &Status,
                          &ReturnedValue,
                          RegistryHandle,
                          &AliasesStr,
                          NdisParameterString );
   if( !NT_SUCCESS( Status ) )
   {
      DigiDump( DIGIERRORS, ("Unable to find %S multistring!\n",
                            AliasesStr) );
		goto BuildX25AliasTableExit;
   }

	Aliases = ReturnedValue->ParameterData.StringData;

   DigiDump( DIGIINIT, ("Aliases = %wZ", &Aliases ) );
	DigiDump( DIGIINIT, ("\n") );

	gAliases.Length        = Aliases.Length;
	gAliases.MaximumLength = Aliases.MaximumLength;
	gAliases.Buffer        = DigiAllocMem( NonPagedPool, gAliases.MaximumLength );
	if ( !gAliases.Buffer )
		return( NDIS_STATUS_RESOURCES );

	RtlZeroMemory( gAliases.Buffer, gAliases.MaximumLength );
	RtlCopyMemory( gAliases.Buffer, Aliases.Buffer, gAliases.Length );

   // Get the Snid multistring.  This string contains a list of Snids
   // corresponding the the Device and Alias identifying an sxb device 
   // to be used.
   NdisReadConfiguration( &Status,
                          &ReturnedValue,
                          RegistryHandle,
                          &SnidsStr,
                          NdisParameterString );
   if( !NT_SUCCESS( Status ) )
   {
      DigiDump( DIGIERRORS, ("Unable to find %S multistring!\n",
                            SnidsStr) );
		goto BuildX25AliasTableExit;
   }

	Snids = ReturnedValue->ParameterData.StringData;

   DigiDump( DIGIINIT, ("Snids = %wZ", &Snids ) );
	DigiDump( DIGIINIT, ("\n") );

	//
	// Parse strings and build table
	//

	RtlInitUnicodeString( &Device, NULL );
	RtlInitUnicodeString( &Alias, NULL );
	RtlInitUnicodeString( &Snid, NULL );

	while ( GetNextMultistringString( &Device, &gDevices ) &&
	        GetNextMultistringString( &Alias, &gAliases )  &&
	        GetNextMultistringString( &Snid, &Snids ) )
	{
		PALIAS_ENTRY AliasEntry;
		AliasEntry = DigiAllocMem( NonPagedPool, sizeof( ALIAS_ENTRY ) );
		if ( !AliasEntry )
		{
			Status = NDIS_STATUS_RESOURCES;
			goto BuildX25AliasTableExit;
		}
		NdisZeroMemory( AliasEntry, sizeof( ALIAS_ENTRY ) );
		AliasEntry->Type = DIGIWAN_TYPE_ALIAS_ENTRY;
		RtlInitUnicodeString( &AliasEntry->Alias, Alias.Buffer );
		RtlInitUnicodeString( &AliasEntry->DeviceName, Device.Buffer );

		// Convert the string to and Integer
		RtlUnicodeStringToInteger(&Snid,10,&AliasEntry->Snid);

		if ( !AliasEntry->Snid )
		{
			Status = NDIS_STATUS_FAILURE;
			goto BuildX25AliasTableExit;
		}
		
		if ( !PreviousAlias )
			gpAliasList = AliasEntry;
		else
			PreviousAlias->pNext = AliasEntry;

		PreviousAlias = AliasEntry;
	}

	Status = NDIS_STATUS_SUCCESS;
	   
BuildX25AliasTableExit:;

	return( Status );
}