/************************************************************************* * * tdspx.c * * Copyright 1998, Microsoft * *************************************************************************/ #include #include #include #include #include #include #define _DEFCHARINFO_ #include #include #include #include #include "tdtdi.h" #include "tdspx.h" #ifdef _HYDRA_ // This becomes the device name PWCHAR ModuleName = L"tdspx"; #endif #define REG_IPX_Linkage \ L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\NwlnkIpx\\Linkage" #if DBG ULONG DbgPrint( PCH Format, ... ); #define DBGPRINT(x) DbgPrint x #if DBGTRACE #define TRACE0(x) DbgPrint x #define TRACE1(x) DbgPrint x #else #define TRACE0(x) #define TRACE1(x) #endif #else #define DBGPRINT(x) #define TRACE0(x) #define TRACE1(x) #endif /*============================================================================= == External Functions Defined =============================================================================*/ NTSTATUS TdiDeviceOpen( PTD, PSD_OPEN ); NTSTATUS TdiDeviceClose( PTD, PSD_CLOSE ); NTSTATUS TdiDeviceOpenEndpoint( PTD, PVOID, ULONG ); NTSTATUS TdiDeviceBuildTransportNameAndAddress( PTD, PICA_STACK_ADDRESS, PUNICODE_STRING, PTRANSPORT_ADDRESS *, PULONG ); NTSTATUS TdiDeviceBuildWildcardAddress( PTD, PTRANSPORT_ADDRESS *, PULONG ); NTSTATUS TdiDeviceWaitForDatagramConnection( PTD, PFILE_OBJECT, PDEVICE_OBJECT, PTRANSPORT_ADDRESS *, PULONG ); NTSTATUS TdiDeviceCompleteDatagramConnection( PTD, PFILE_OBJECT, PDEVICE_OBJECT, PTRANSPORT_ADDRESS, ULONG ); NTSTATUS TdiDeviceConnectionSend( PTD ); NTSTATUS TdiDeviceReadComplete( PTD, PUCHAR, PULONG ); NTSTATUS _OpenRegKey(PHANDLE, PWCHAR); NTSTATUS _GetRegMultiSZValue(HANDLE ,PWCHAR,PUNICODE_STRING ); VOID GetGUID(OUT PUNICODE_STRING, IN int); /*============================================================================= == External Functions Referenced =============================================================================*/ NTSTATUS MemoryAllocate( ULONG, PVOID * ); VOID MemoryFree( PVOID ); /*============================================================================= == Internal Functions Defined =============================================================================*/ NTSTATUS _IpxGetTransportAddress( PTD, int, PULONG, PUCHAR ); NTSTATUS _IPXGetAdapterNumber( int Lana, int * AdpNum ); #if DBG VOID DumpIpxAddress( PTRANSPORT_ADDRESS p, ULONG Len ); #endif /*============================================================================= == Global variables =============================================================================*/ /* * Define variables used by afdcommon code */ USHORT TdiDeviceEndpointType = TdiConnectionStream; USHORT TdiDeviceAddressType = TDI_ADDRESS_TYPE_IPX; USHORT TdiDeviceInBufHeader = 0; // ULONG TdiDeviceMaxTransportAddressLength = sizeof(TA_IPX_ADDRESS); /******************************************************************************* * * TdiDeviceOpen * * Allocate and initialize private data structures * * ENTRY: * pTd (input) * Pointer to TD data structure * pSdOpen (input/output) * Points to the parameter structure SD_OPEN. * * EXIT: * STATUS_SUCCESS - no error * ******************************************************************************/ NTSTATUS TdiDeviceOpen( PTD pTd, PSD_OPEN pSdOpen ) { return( STATUS_SUCCESS ); } /******************************************************************************* * * TdiDeviceClose * * Close transport driver * * NOTE: this must not close the current connection endpoint * * ENTRY: * pTd (input) * Pointer to TD data structure * pSdClose (input/output) * Points to the parameter structure SD_CLOSE. * * EXIT: * STATUS_SUCCESS - no error * ******************************************************************************/ NTSTATUS TdiDeviceClose( PTD pTd, PSD_CLOSE pSdClose ) { return( STATUS_SUCCESS ); } /******************************************************************************* * * TdiDeviceOpenEndpoint * * Open an existing endpoint * * ENTRY: * pTd (input) * Pointer to TD data structure * pIcaEndpoint (input) * Pointer to ICA endpoint structure * IcaEndpointLength (input) * length of endpoint data * * EXIT: * STATUS_SUCCESS - no error * ******************************************************************************/ NTSTATUS TdiDeviceOpenEndpoint( PTD pTd, PVOID pIcaEndpoint, ULONG IcaEndpointLength ) { return( STATUS_SUCCESS ); } /***************************************************************************** * * TdiDeviceBuildTransportNameAndAddress * * Build the Transport Name and Address given an optional ICA_STACK_ADDRESS, * or the Lana value from the pTd->Params structure. * * ENTRY: * * pTd (input) * pointer to TD data structure * pLocalAddress (input) * pointer to local address to use (OPTIONAL) * pTransportName (output) * pointer to UNICODE_STRING to return transport name * NOTE: the buffer pointed to be pTransportName.Buffer must * be free'd by the caller * ppTransportAddress (output) * pointer to location to return TRANSPORT_ADDRESS structure * NOTE: the transport address buffer must be free'd by the caller * pTransportAddressLength (output) * pointer to location to return TransportAddress length * * EXIT: * STATUS_SUCCESS - Success * ****************************************************************************/ NTSTATUS TdiDeviceBuildTransportNameAndAddress( PTD pTd, PICA_STACK_ADDRESS pLocalAddress, PUNICODE_STRING pTransportName, PTRANSPORT_ADDRESS *ppTransportAddress, PULONG pTransportAddressLength ) { PTDI_ADDRESS_IPX pIpxAddress; int Lana; NTSTATUS Status; /* * For SPX, the transport device name is fixed, * so just allocate and initialize the transport name string here. */ Status = MemoryAllocate( sizeof(DD_SPX_DEVICE_NAME), &pTransportName->Buffer ); if ( !NT_SUCCESS( Status ) ) { DBGPRINT(("TdiDeviceBuildTransportNameAndAddress: SPX Could not allocate memory\n")); goto badmalloc1; } wcscpy( pTransportName->Buffer, DD_SPX_DEVICE_NAME ); pTransportName->Length = sizeof(DD_SPX_DEVICE_NAME) - sizeof(UNICODE_NULL); pTransportName->MaximumLength = pTransportName->Length + sizeof(UNICODE_NULL); /* * Allocate a transport address structure */ *pTransportAddressLength = sizeof(TRANSPORT_ADDRESS) + sizeof(TDI_ADDRESS_IPX); Status = MemoryAllocate( *pTransportAddressLength, ppTransportAddress ); if ( !NT_SUCCESS( Status ) ) { DBGPRINT(("TdiDeviceBuildTransportNameAndAddress: SPX Could not allocate memory\n")); goto badmalloc2; } /* * Initialize the static part of the transport address */ (*ppTransportAddress)->TAAddressCount = 1; (*ppTransportAddress)->Address[0].AddressLength = sizeof(TDI_ADDRESS_IPX); (*ppTransportAddress)->Address[0].AddressType = TDI_ADDRESS_TYPE_IPX; pIpxAddress = (PTDI_ADDRESS_IPX)(*ppTransportAddress)->Address[0].Address; pIpxAddress->Socket = htons( CITRIX_SPX_SOCKET ); /* * If a local address is specified, then use it. */ if ( pLocalAddress ) { /* * Skip over the address family(type) data (bytes 0&1) of the * local address struct, and copy the remainder of the address * directly to the Address field of the TransportAddress struct. */ ASSERT( *(PUSHORT)pLocalAddress == TDI_ADDRESS_TYPE_IPX ); RtlCopyMemory( pIpxAddress, &((PCHAR)pLocalAddress)[2], sizeof(TDI_ADDRESS_IPX) ); TRACE1(("TDSPX: TdiDeviceBuildTransportAddress: Local address:\n")); /* * There was no local address specified. * In this case, we use the LanAdapter value from the PDPARAMS * structure to lookup the corresponding IPX address. */ } else if ( (Lana = pTd->Params.Network.LanAdapter) ) { /* * Get Local Address Information */ Status = _IpxGetTransportAddress( pTd, Lana, &pIpxAddress->NetworkAddress, pIpxAddress->NodeAddress ); if ( !NT_SUCCESS( Status ) ) { DBGPRINT(("TDSPX: TdiDeviceBuildTransportAddress: Bad Lana 0x%x\n",Lana)); goto badadapterdata; } TRACE1(("TDSPX: TdiDeviceBuildTransportAddress: Found address for Lana 0x%x\n",Lana)); /* * No LanAdapter value was specified, so use the wildcard address (zero) */ } else { DBGPRINT(("TDSPX: TdiDeviceBuildTransportAddress: Wildcard address")); pIpxAddress->NetworkAddress = 0; RtlZeroMemory( pIpxAddress->NodeAddress, sizeof(pIpxAddress->NodeAddress) ); } #if DBGTRACE DumpIpxAddress( *ppTransportAddress, *pTransportAddressLength ); #endif return( STATUS_SUCCESS ); /*============================================================================= == Error returns =============================================================================*/ badadapterdata: MemoryFree( *ppTransportAddress ); badmalloc2: MemoryFree( pTransportName->Buffer ); badmalloc1: return( Status ); } /***************************************************************************** * * TdiDeviceBuildWildcardAddress * * Build a wildcard Address for this protocol. * * ENTRY: * * pTd (input) * pointer to TD data structure * ppWildcardAddress (output) * pointer to location to return TRANSPORT_ADDRESS structure * NOTE: the transport address buffer must be free'd by the caller * pWildcardAddressLength (output) * pointer to location to return TransportAddress length * * EXIT: * STATUS_SUCCESS - Success * ****************************************************************************/ NTSTATUS TdiDeviceBuildWildcardAddress( PTD pTd, PTRANSPORT_ADDRESS *ppWildcardAddress, PULONG pWildcardAddressLength ) { PTDI_ADDRESS_IPX pIpxAddress; NTSTATUS Status; /* * Allocate a transport address structure */ *pWildcardAddressLength = sizeof(TRANSPORT_ADDRESS) + sizeof(TDI_ADDRESS_IPX); Status = MemoryAllocate( *pWildcardAddressLength, ppWildcardAddress ); if ( !NT_SUCCESS( Status ) ) return( Status ); /* * Initialize the static part of the transport address */ (*ppWildcardAddress)->TAAddressCount = 1; (*ppWildcardAddress)->Address[0].AddressLength = sizeof(TDI_ADDRESS_IPX); (*ppWildcardAddress)->Address[0].AddressType = TDI_ADDRESS_TYPE_IPX; pIpxAddress = (PTDI_ADDRESS_IPX)(*ppWildcardAddress)->Address[0].Address; pIpxAddress->NetworkAddress = 0; RtlZeroMemory( pIpxAddress->NodeAddress, sizeof(pIpxAddress->NodeAddress) ); pIpxAddress->Socket = 0; return( STATUS_SUCCESS ); } /***************************************************************************** * * TdiDeviceWaitForDatagramConnection * * Wait for a datagram connection request, validate it, * and return the remote transport address of the connection. * * ENTRY: * * pTd (input) * pointer to TD data structure * pFileObject (input) * pointer to file object to wait for a connection on * ppRemoteAddress (output) * pointer to location to return TRANSPORT_ADDRESS structure * NOTE: the transport address buffer must be free'd by the caller * pRemoteAddressLength (output) * pointer to location to return RemoteAddress length * * EXIT: * STATUS_SUCCESS - Success * ****************************************************************************/ NTSTATUS TdiDeviceWaitForDatagramConnection( PTD pTd, PFILE_OBJECT pFileObject, PDEVICE_OBJECT pDeviceObject, PTRANSPORT_ADDRESS *ppRemoteAddress, PULONG pRemoteAddressLength ) { return( STATUS_NOT_SUPPORTED ); } /***************************************************************************** * * TdiDeviceCompleteDatagramConnection * * Do any final work to complete a datagram connection. * * ENTRY: * * pTd (input) * pointer to TD data structure * pFileObject (input) * pointer to file object for this connection * * EXIT: * STATUS_SUCCESS - Success * ****************************************************************************/ NTSTATUS TdiDeviceCompleteDatagramConnection( PTD pTd, PFILE_OBJECT pFileObject, PDEVICE_OBJECT pDeviceObject, PTRANSPORT_ADDRESS pTransportAddress, ULONG TransportAddressLength ) { return( STATUS_NOT_SUPPORTED ); } /******************************************************************************* * * TdiDeviceConnectionSend * * Initialize host module data structure * -- this structure gets sent to the client * * * ENTRY: * pTd (input) * Pointer to td data structure * * EXIT: * STATUS_SUCCESS - no error * ******************************************************************************/ NTSTATUS TdiDeviceConnectionSend( PTD pTd ) { PCLIENTMODULES pClient; /* * Get pointer to client structure */ pClient = pTd->pClient; /* * Initialize Td host module structure */ pClient->TdVersionL = VERSION_HOSTL_TDSPX; pClient->TdVersionH = VERSION_HOSTH_TDSPX; pClient->TdVersion = VERSION_HOSTH_TDSPX; return( STATUS_SUCCESS ); } /******************************************************************************* * * TdiDeviceReadComplete * * Do any read complete processing * * * ENTRY: * pTd (input) * Pointer to td data structure * pBuffer (input) * Pointer to input buffer * pByteCount (input/output) * Pointer to location containing byte count read * * EXIT: * STATUS_SUCCESS - no error * ******************************************************************************/ NTSTATUS TdiDeviceReadComplete( PTD pTd, PUCHAR pBuffer, PULONG pByteCount ) { return( STATUS_SUCCESS ); } /******************************************************************************* * * _IpxGetTransportAddress * * Get IPX transport address for a given LanAdapter number * * * ENTRY: * pTd (input) * pointer to TD data structure * Lana (input) * Lan Adapter number * pNetworkAddress (output) * pointer to location to return NetworkAddress * pNodeAddress (output) * pointer to location to return NodeAddress * * * EXIT: * STATUS_SUCCESS - no error * ******************************************************************************/ NTSTATUS _IpxGetTransportAddress( PTD pTd, int Lana, PULONG pNetworkAddress, PUCHAR pNodeAddress ) { OBJECT_ATTRIBUTES objectAttributes; IO_STATUS_BLOCK ioStatusBlock; UNICODE_STRING nameString; HANDLE IPXHandle; ULONG tdiBufferLength; PSTREAMS_TDI_ACTION tdiBuffer; ULONG cmd; PIPX_ADDRESS_DATA pIpxAddressData; PFILE_OBJECT pFileObject; NTSTATUS Status; int IpxAdapterNum = 0; /* * Open a Handle to the IPX driver. */ RtlInitUnicodeString( &nameString, DD_IPX_DEVICE_NAME ); InitializeObjectAttributes( &objectAttributes, &nameString, OBJ_CASE_INSENSITIVE, (HANDLE) NULL, (PSECURITY_DESCRIPTOR) NULL ); Status = ZwCreateFile( &IPXHandle, SYNCHRONIZE | FILE_READ_DATA | FILE_WRITE_DATA, &objectAttributes, &ioStatusBlock, NULL, FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_OPEN_IF, 0, NULL, 0 ); if ( !NT_SUCCESS( Status ) ) return( Status ); /* * Allocate a TDI buffer to do the IOCTL. */ tdiBufferLength = sizeof(STREAMS_TDI_ACTION) + sizeof(ULONG) + sizeof(IPX_ADDRESS_DATA); Status = MemoryAllocate( tdiBufferLength, &tdiBuffer ); if ( !NT_SUCCESS( Status ) ) { ZwClose( IPXHandle ); return( Status ); } // Get IPX Adapter Num from winstation Lana ID Status = _IPXGetAdapterNumber( Lana, &IpxAdapterNum); if ( !NT_SUCCESS( Status ) ) { ZwClose( IPXHandle ); return( Status ); } /* * Fill in the tdiBuffer header */ RtlMoveMemory( &tdiBuffer->Header.TransportId, "MISN", 4 ); // use the following since we opened a control channel above tdiBuffer->DatagramOption = NWLINK_OPTION_CONTROL; /** Fill out the buffer, the buffer looks like this: ULONG cmd data passed. **/ cmd = MIPX_GETCARDINFO; memcpy( tdiBuffer->Buffer, &cmd, sizeof(ULONG)); tdiBuffer->BufferLength = sizeof(ULONG) + sizeof(IPX_ADDRESS_DATA); pIpxAddressData = (PIPX_ADDRESS_DATA)(tdiBuffer->Buffer + sizeof(ULONG)); pIpxAddressData->adapternum = IpxAdapterNum; Status = ObReferenceObjectByHandle( IPXHandle, 0, NULL, KernelMode, (PVOID *)&pFileObject, NULL ); ASSERT( Status == STATUS_SUCCESS ); Status = CtxDeviceIoControlFile( pFileObject, IOCTL_TDI_ACTION, NULL, 0, tdiBuffer, tdiBufferLength, FALSE, NULL, &ioStatusBlock, NULL ); ObDereferenceObject( pFileObject ); ZwClose( IPXHandle ); if ( Status == STATUS_SUCCESS ) { RtlCopyMemory( pNetworkAddress, pIpxAddressData->netnum, sizeof(pIpxAddressData->netnum) ); RtlCopyMemory( pNodeAddress, pIpxAddressData->nodenum, sizeof(pIpxAddressData->nodenum) ); } return( Status ); } #if DBG VOID DumpIpxAddress( PTRANSPORT_ADDRESS p, ULONG Len ) { ULONG j; LONG i; PTDI_ADDRESS_IPX Ipx; DbgPrint("TAAddressCount %d, TotalLen %d\n",p->TAAddressCount,Len); for( i=0; i < p->TAAddressCount; i++ ) { DbgPrint("AddressLength %d, AddressType %d\n",p->Address[i].AddressLength,p->Address[i].AddressType); Ipx = (PTDI_ADDRESS_IPX)&p->Address[i].Address[0]; DbgPrint("Network 0x%x Socket 0x%x\n",Ipx->NetworkAddress,Ipx->Socket); for( j=0; j < 6; j++ ) { DbgPrint("%x:",Ipx->NodeAddress[j]); } DbgPrint("\n\n"); } } #endif NTSTATUS _IPXGetAdapterNumber( int Lana, int * AdpNum ) /*++ Routine Description: Get the IPX Lan Adapter number using the instation Lana ID index Arguments: Lana - IN the winstation Lana ID index AdpNum - OUT the IPX Lana Adapter num Return Value: return nt status --*/ { NTSTATUS Status; int AdapterNum; UNICODE_STRING RouteString; UNICODE_STRING NameString; HANDLE KeyHandle = NULL; PWSTR RouteStr, Str; unsigned RouteLen, Len; /* * Convert winstation Lana_ID to Network Adapter number */ RtlInitUnicodeString( &RouteString , NULL ); GetGUID( &RouteString , Lana ); RouteLen = RouteString.Length; RouteStr = RouteString.Buffer; AdapterNum =0; #if DBG KdPrint( ( "TDIPX: _TcpGetTransportAddress Length = %d GUID = %ws\n" , RouteLen, RouteStr ) ); #endif if (RouteLen < (2 * sizeof(UNICODE_NULL))) { return( STATUS_DEVICE_DOES_NOT_EXIST ); } RtlInitUnicodeString( &NameString, NULL ); Status = _OpenRegKey( &KeyHandle, REG_IPX_Linkage ); if ( !NT_SUCCESS( Status ) ) return Status; NameString.Length = 0; NameString.MaximumLength = 0; NameString.Buffer = NULL; Status = _GetRegMultiSZValue( KeyHandle, L"Route", &NameString ); ZwClose( KeyHandle ); if ( !NT_SUCCESS( Status ) ) return Status; if (NameString.Length < (2 * sizeof(WCHAR))) { return(STATUS_DEVICE_DOES_NOT_EXIST); } Len = NameString.Length; Str = NameString.Buffer; Status = STATUS_DEVICE_DOES_NOT_EXIST; AdapterNum = 0; for (;;) { // Check current string to see if it's a GUID (it must start with an // open brace after initial double-quote). if (Str[1] == L'{') { if ( wcsncmp( (PWSTR) &Str[1],RouteStr, wcslen(RouteStr)-1) == 0) { #if DBG KdPrint( ( "TDIPX: _TcpGetTransportAddress: Get Lan Adapter Number = %i\n",AdapterNum ) ); #endif Status = STATUS_SUCCESS; break; } else { // KdPrint( ( "TDIPX: _TcpGetTransportAddress, No this lana Adapter = %i\n",AdapterNum ) ); AdapterNum ++; } } // Skip through current string past NULL. while (Len >= sizeof(WCHAR)) { Len -= sizeof(WCHAR); if (*Str++ == UNICODE_NULL) break; } // Check for index out of range. if (Len < (2 * sizeof(UNICODE_NULL))) { break; } } if ( !NT_SUCCESS( Status ) ) return Status; *AdpNum = AdapterNum; return STATUS_SUCCESS; }