/*++ Copyright (c) 1989-1993 Microsoft Corporation Module Name: Nbtutils.c Abstract: This file continas a number of utility and support routines for the NBT code. Author: Jim Stewart (Jimst) 10-2-92 Revision History: --*/ #include "precomp.h" // For some reason inclusion of dnsapi.h seems to cause build error //#include "dns.h" // for DNS_MAX_NAME_LENGTH //#include "windns.h" // for DNS_MAX_NAME_LENGTH #define DNS_MAX_NAME_LENGTH (255) //#if DBG LIST_ENTRY UsedIrps; //#endif NTSTATUS NewInternalAddressFromNetbios( IN PTA_NETBIOS_ADDRESS pTA, IN ULONG MaxInputBufferLength, OUT PTA_NETBT_INTERNAL_ADDRESS *ppNetBT ); NTSTATUS NewInternalAddressFromNetbiosEX( IN PTA_NETBIOS_EX_ADDRESS pTA, IN ULONG MaxInputBufferLength, OUT PTA_NETBT_INTERNAL_ADDRESS *ppNetBT ); NTSTATUS NewInternalAddressFromUnicodeAddress( IN PTA_NETBIOS_UNICODE_EX_ADDRESS pTA, IN ULONG MaxInputBufferLength, OUT PTA_NETBT_INTERNAL_ADDRESS *ppNetBT ); //******************* Pageable Routine Declarations **************** #ifdef ALLOC_PRAGMA #pragma CTEMakePageable(PAGE, ConvertDottedDecimalToUlong) #pragma CTEMakePageable(PAGE, CloseLowerConnections) #endif //******************* Pageable Routine Declarations **************** //---------------------------------------------------------------------------- BOOLEAN IsEntryInList( PLIST_ENTRY pEntryToFind, PLIST_ENTRY pListToSearch ) { PLIST_ENTRY pEntry, pHead; pHead = pListToSearch; pEntry = pHead->Flink; while (pEntry != pHead) { if (pEntry == pEntryToFind) { // // This Entry is still valid // return (TRUE); } // // Go to next Entry // pEntry = pEntry->Flink; } return (FALSE); } //---------------------------------------------------------------------------- void NbtFreeAddressObj( tADDRESSELE *pAddress ) /*++ Routine Description: This routine releases all memory associated with an Address object. Arguments: Return Value: none --*/ { // // let's come back and do this later when it's not dpc time // NTQueueToWorkerThread( &pAddress->WorkItemClose, DelayedFreeAddrObj, NULL, pAddress, NULL, NULL, FALSE ); } //---------------------------------------------------------------------------- VOID DelayedFreeAddrObj( IN tDGRAM_SEND_TRACKING *pUnused1, IN PVOID pClientContext, IN PVOID pUnused2, IN tDEVICECONTEXT *pUnused3 ) /*++ Routine Description: This routine releases all memory associated with an Address object. Arguments: Return Value: none --*/ { tADDRESSELE *pAddress = (tADDRESSELE *) pClientContext; ULONG SavedVerify = pAddress->Verify; #ifndef VXD if (pAddress->SecurityDescriptor) { SeDeassignSecurity(&pAddress->SecurityDescriptor); } #endif #if DBG CTEMemSet (pAddress, 0x12, sizeof(tADDRESSELE)); #endif // DBG // free the address block itself // Modify the verify value so that another user of the same memory // block cannot accidently pass in a valid verifier pAddress->Verify = SavedVerify + 10; CTEMemFree ((PVOID) pAddress); } //---------------------------------------------------------------------------- void NbtFreeClientObj( tCLIENTELE *pClientEle ) /*++ Routine Description: This routine releases all memory associated with Client object. Arguments: Return Value: none --*/ { ULONG SavedVerify = pClientEle->Verify; #if DBG CTEMemSet (pClientEle, 0x12, sizeof(tCLIENTELE)); #endif // DBG // Modify the verify value so that another user of the same memory // block cannot accidently pass in a valid verifier pClientEle->Verify = SavedVerify + 10; CTEMemFree ((PVOID) pClientEle); } //---------------------------------------------------------------------------- void FreeConnectionObj( tCONNECTELE *pConnEle ) /*++ Routine Description: This routine releases all memory associated with a Connection object and then it frees the connection object itself. Arguments: Return Value: none --*/ { ULONG SavedVerify = pConnEle->Verify; #if DBG CTEMemSet (pConnEle, 0x12, sizeof(tCONNECTELE)); #endif // DBG // Modify the verify value so that another user of the same memory // block cannot accidently pass in a valid verifier pConnEle->Verify = SavedVerify + 10; CTEMemFree ((PVOID) pConnEle); } //---------------------------------------------------------------------------- tCLIENTELE * NbtAllocateClientBlock( tADDRESSELE *pAddrEle, tDEVICECONTEXT *pDeviceContext ) /*++ Routine Description: This routine allocates a block of memory for a client openning an address. It fills in default values for the block and links the block to the addresslist. The AddressEle spin lock is held when this routine is called. Arguments: Return Value: none --*/ { tCLIENTELE *pClientElement; // allocate memory for the client block pClientElement = (tCLIENTELE *) NbtAllocMem (sizeof (tCLIENTELE), NBT_TAG2('05')); if (!pClientElement) { ASSERTMSG("Unable to allocate Memory for a client block\n", pClientElement); return(NULL); } CTEZeroMemory((PVOID)pClientElement,sizeof(tCLIENTELE)); CTEInitLock(&pClientElement->LockInfo.SpinLock); #if DBG pClientElement->LockInfo.LockNumber = CLIENT_LOCK; #endif // Set Event handler function pointers to default routines provided by // TDI #ifndef VXD pClientElement->evConnect = TdiDefaultConnectHandler; pClientElement->evReceive = TdiDefaultReceiveHandler; pClientElement->evDisconnect = TdiDefaultDisconnectHandler; pClientElement->evError = TdiDefaultErrorHandler; pClientElement->evRcvDgram = TdiDefaultRcvDatagramHandler; pClientElement->evRcvExpedited = TdiDefaultRcvExpeditedHandler; pClientElement->evSendPossible = TdiDefaultSendPossibleHandler; #else // // VXD provides no client support for event handlers but does // make use of some of the event handlers itself (for RcvAny processing // and disconnect cleanup). // pClientElement->evConnect = NULL ; pClientElement->evReceive = NULL ; pClientElement->RcvEvContext = NULL ; pClientElement->evDisconnect = NULL ; pClientElement->evError = NULL ; pClientElement->evRcvDgram = NULL ; pClientElement->evRcvExpedited = NULL ; pClientElement->evSendPossible = NULL ; #endif pClientElement->RefCount = 1; // there are no rcvs or snds yet InitializeListHead(&pClientElement->RcvDgramHead); InitializeListHead(&pClientElement->ListenHead); InitializeListHead(&pClientElement->SndDgrams); InitializeListHead(&pClientElement->ConnectActive); InitializeListHead(&pClientElement->ConnectHead); #ifdef VXD InitializeListHead(&pClientElement->RcvAnyHead); pClientElement->fDeregistered = FALSE ; #endif pClientElement->pIrp = NULL; // copy a special value into the verify long so that we can verify // connection ptrs passed back from the application pClientElement->Verify = NBT_VERIFY_CLIENT; pClientElement->pAddress = (PVOID)pAddrEle; // link the client block to the Address element. pClientElement->pDeviceContext = pDeviceContext; // adapter this name is registered against. // put the new Client element block on the end of the linked list tied to // the address element InsertTailList(&pAddrEle->ClientHead,&pClientElement->Linkage); return(pClientElement); } //---------------------------------------------------------------------------- NTSTATUS NbtAddPermanentName( IN tDEVICECONTEXT *pDeviceContext ) /*++ Routine Description: This routine adds the node permanent name to the local name table. This is the node's MAC address padded out to 16 bytes with zeros. Arguments: DeviceContext - Adapter to add permanent pIrp - Irp (optional) to complete after name has been added Return Value: status --*/ { NTSTATUS status; TDI_REQUEST Request; TA_NETBIOS_ADDRESS Address; UCHAR pName[NETBIOS_NAME_SIZE]; USHORT uType; CTELockHandle OldIrq, OldIrq1; tNAMEADDR *pNameAddr; tCLIENTELE *pClientEle; CTEZeroMemory(pName,NETBIOS_NAME_SIZE); CTEMemCopy(&pName[10],&pDeviceContext->MacAddress.Address[0],sizeof(tMAC_ADDRESS)); // // be sure the name has not already been added // if (pDeviceContext->pPermClient) { if (CTEMemEqu(pDeviceContext->pPermClient->pAddress->pNameAddr->Name, pName, NETBIOS_NAME_SIZE)) { return(STATUS_SUCCESS); } else { NbtRemovePermanentName(pDeviceContext); } } CTESpinLock(&NbtConfig.JointLock,OldIrq); // // check if the name is already in the hash table // status = FindInHashTable (pNbtGlobConfig->pLocalHashTbl, pName, NbtConfig.pScope, &pNameAddr); if ((NT_SUCCESS(status)) && (pNameAddr->pAddressEle)) { // // Acquire Address Spinlock since we may be accessing the ClientHead list // Bug #: 230820 // CTESpinLock(pNameAddr->pAddressEle,OldIrq1); // // create client block and link to addresslist // pass back the client block address as a handle for future reference // to the client // pClientEle = NbtAllocateClientBlock (pNameAddr->pAddressEle, pDeviceContext); if (!pClientEle) { CTESpinFree(pNameAddr->pAddressEle,OldIrq1); CTESpinFree(&NbtConfig.JointLock,OldIrq); return(STATUS_INSUFFICIENT_RESOURCES); } NBT_REFERENCE_ADDRESS (pNameAddr->pAddressEle, REF_ADDR_NEW_CLIENT); CTESpinFree(pNameAddr->pAddressEle,OldIrq1); // // reset the ip address incase the the address got set to loop back // by a client releasing and re-openning the permanent name while there // was no ip address for this node. // pNameAddr->IpAddress = pDeviceContext->IpAddress; // turn on the adapter's bit in the adapter Mask and set the // re-register flag so we register the name out the new adapter. // pNameAddr->AdapterMask |= pDeviceContext->AdapterMask; pNameAddr->NameTypeState |= NAMETYPE_QUICK; IF_DBG(NBT_DEBUG_NAMESRV) KdPrint(("Nbt: Adding Permanent name %15.15s<%X> \n", pName,(UCHAR)pName[15])); } else { CTESpinFree(&NbtConfig.JointLock,OldIrq); // make up the Request data structure from the IRP info Request.Handle.AddressHandle = NULL; // // Make it a Quick name so it does not get registered on the net // Address.TAAddressCount = 1; Address.Address[0].AddressType = TDI_ADDRESS_TYPE_NETBIOS; Address.Address[0].AddressLength = TDI_ADDRESS_LENGTH_NETBIOS; Address.Address[0].Address[0].NetbiosNameType = TDI_ADDRESS_NETBIOS_TYPE_QUICK_UNIQUE; CTEMemCopy(Address.Address[0].Address[0].NetbiosName,pName,NETBIOS_NAME_SIZE); status = NbtOpenAddress(&Request, (PTA_ADDRESS)&Address.Address[0], pDeviceContext->IpAddress, pDeviceContext, NULL); CTESpinLock(&NbtConfig.JointLock,OldIrq); pClientEle = (tCLIENTELE *)Request.Handle.AddressHandle; } // // save the client element so we can remove the permanent name later // if required // if (NT_SUCCESS(status)) { pDeviceContext->pPermClient = pClientEle; #ifdef VXD // // 0th element is for perm. name: store it. // pDeviceContext->pNameTable[0] = pClientEle; #endif } CTESpinFree(&NbtConfig.JointLock,OldIrq); return(status); } //---------------------------------------------------------------------------- VOID NbtRemovePermanentName( IN tDEVICECONTEXT *pDeviceContext ) /*++ Routine Description: This routine remomves the node permanent name to the local name table. Arguments: DeviceContext - Adapter to add permanent pIrp - Irp (optional) to complete after name has been added Return Value: status --*/ { NTSTATUS status; tNAMEADDR *pNameAddr; CTELockHandle OldIrq; tCLIENTELE *pClientEle; tADDRESSELE *pAddressEle; CTESpinLock(&NbtConfig.JointLock,OldIrq); if (pDeviceContext->pPermClient) { // // We need to free the client and set the perm name ptr to null // pClientEle = pDeviceContext->pPermClient; pDeviceContext->pPermClient = NULL; #ifdef VXD pDeviceContext->pNameTable[0] = NULL; #endif CTESpinFree(&NbtConfig.JointLock,OldIrq); NBT_DEREFERENCE_CLIENT(pClientEle); } else { CTESpinFree(&NbtConfig.JointLock,OldIrq); } } //---------------------------------------------------------------------------- NTSTATUS ConvertDottedDecimalToUlong( IN PUCHAR pInString, OUT PULONG IpAddress ) /*++ Routine Description: This routine converts a unicode dotted decimal IP address into a 4 element array with each element being USHORT. Arguments: Return Value: NTSTATUS --*/ { USHORT i; ULONG value; int iSum =0; ULONG k = 0; UCHAR Chr; UCHAR pArray[4]; CTEPagedCode(); pArray[0] = 0; // go through each character in the string, skipping "periods", converting // to integer by subtracting the value of '0' // while ((Chr = *pInString++) && (Chr != ' ') ) { if (Chr == '.') { // be sure not to overflow a byte. if (iSum <= 0xFF) pArray[k] = (UCHAR) iSum; else return(STATUS_UNSUCCESSFUL); // check for too many periods in the address if (++k > 3) return STATUS_UNSUCCESSFUL; pArray[k] = 0; iSum = 0; } else { Chr = Chr - '0'; // be sure character is a number 0..9 if ((Chr < 0) || (Chr > 9)) return(STATUS_UNSUCCESSFUL); iSum = iSum*10 + Chr; } } // save the last sum in the byte and be sure there are 4 pieces to the // address if ((iSum <= 0xFF) && (k == 3)) pArray[k] = (UCHAR) iSum; else return(STATUS_UNSUCCESSFUL); // now convert to a ULONG, in network order... value = 0; // go through the array of bytes and concatenate into a ULONG for (i=0; i < 4; i++ ) { value = (value << 8) + pArray[i]; } *IpAddress = value; return(STATUS_SUCCESS); } //---------------------------------------------------------------------------- NTSTATUS NbtInitQ( PLIST_ENTRY pListHead, LONG iSizeBuffer, LONG iNumBuffers ) /*++ Routine Description: This routine allocates memory blocks for doubly linked lists and links them to a list. Arguments: ppListHead - a ptr to a ptr to the list head to add buffer to iSizeBuffer - size of the buffer to add to the list head iNumBuffers - the number of buffers to add to the queue Return Value: none --*/ { int i; PLIST_ENTRY pBuffer; // NOTE THAT THIS ASSUMES THAT THE LINKAGE PTRS FOR EACH BLOCK ARE AT // THE START OF THE BLOCK - so it will not work correctly if // the various types in types.h change to move "Linkage" to a position // other than at the start of each structure to be chained for (i=0;i= pNbtGlobConfig->iMaxNumBuff[eBuffType] ) { return(STATUS_INSUFFICIENT_RESOURCES); } // no memory blocks, so allocate another one status = NbtInitQ( pListHead, pNbtGlobConfig->iBufferSize[eBuffType], 1); if (!NT_SUCCESS(status)) { return(status); } NbtConfig.iCurrentNumBuff[eBuffType]++; *ppListEntry = RemoveHeadList(pListHead); } else { *ppListEntry = RemoveHeadList(pListHead); } return(STATUS_SUCCESS); } NTSTATUS NetbiosAddressToInternalAddress( IN PTA_NETBIOS_ADDRESS pTA, IN ULONG MaxInputBufferLength, OUT PTDI_ADDRESS_NETBT_INTERNAL pNetBT ) { // // name could be longer than 16 bytes (dns name), but make sure it's at // least 16 bytes (sizeof(TDI_ADDRESS_NETBIOS) == (16 + sizeof(USHORT))) // if (pTA->Address[0].AddressLength < sizeof(TDI_ADDRESS_NETBIOS)) { return(STATUS_INVALID_PARAMETER); } pNetBT->NameType = pTA->Address[0].Address[0].NetbiosNameType; pNetBT->AddressType = TDI_ADDRESS_TYPE_NETBIOS; pNetBT->OEMEndpointName.Buffer = NULL; pNetBT->OEMEndpointName.Length = pNetBT->OEMEndpointName.MaximumLength = 0; /* Here we bent OEM_STRING a little bit, we allow Length == MaximumLength */ /* That is, Rtl routines cannot be used since they expect null-terminated Buffer */ pNetBT->OEMRemoteName.MaximumLength = pNetBT->OEMRemoteName.Length = pTA->Address[0].AddressLength - (sizeof(TDI_ADDRESS_NETBIOS) - NETBIOS_NAME_SIZE); pNetBT->OEMRemoteName.Buffer = pTA->Address[0].Address[0].NetbiosName; pNetBT->pNetbiosUnicodeEX = NULL; return STATUS_SUCCESS; } NTSTATUS NetbiosEXAddressToInternalAddress( IN PTA_NETBIOS_EX_ADDRESS pTA, IN ULONG MaxInputBufferLength, OUT PTDI_ADDRESS_NETBT_INTERNAL pNetBT ) { // // Check for the minimum acceptable length for this type of address // if (MaxInputBufferLength < sizeof (TA_NETBIOS_EX_ADDRESS)) { ASSERT (0); return (STATUS_INVALID_ADDRESS); } pNetBT->OEMEndpointName.Buffer = pTA->Address[0].Address[0].EndpointName; pNetBT->OEMEndpointName.Length = pNetBT->OEMEndpointName.MaximumLength = NETBIOS_NAME_SIZE; pNetBT->NameType = pTA->Address[0].Address[0].NetbiosAddress.NetbiosNameType; pNetBT->AddressType = TDI_ADDRESS_TYPE_NETBIOS_EX; pNetBT->OEMRemoteName.MaximumLength = pNetBT->OEMRemoteName.Length = pTA->Address[0].AddressLength - FIELD_OFFSET(TDI_ADDRESS_NETBIOS_EX,NetbiosAddress) - FIELD_OFFSET(TDI_ADDRESS_NETBIOS,NetbiosName); pNetBT->OEMRemoteName.Buffer = pTA->Address[0].Address[0].NetbiosAddress.NetbiosName; pNetBT->pNetbiosUnicodeEX = NULL; return STATUS_SUCCESS; } NTSTATUS NewInternalAddressFromNetbiosEX( IN PTA_NETBIOS_EX_ADDRESS pTA, IN ULONG MaxInputBufferLength, OUT PTA_NETBT_INTERNAL_ADDRESS *ppNetBT ) { ULONG required_size; PTA_NETBT_INTERNAL_ADDRESS p; PTDI_ADDRESS_NETBT_INTERNAL pNetBT; TDI_ADDRESS_NETBT_INTERNAL ta; ppNetBT[0] = NULL; if (!NT_SUCCESS(NetbiosEXAddressToInternalAddress(pTA, MaxInputBufferLength, &ta))) { return (STATUS_INVALID_ADDRESS); } required_size = NBT_DWORD_ALIGN(sizeof(TA_NETBT_INTERNAL_ADDRESS)) + NBT_DWORD_ALIGN(ta.OEMRemoteName.Length+1) + NBT_DWORD_ALIGN(ta.OEMEndpointName.Length+1); p = (PTA_NETBT_INTERNAL_ADDRESS)NbtAllocMem (required_size, NBT_TAG2('TA')); if (p == NULL) { return STATUS_INSUFFICIENT_RESOURCES; } CTEZeroMemory(p, required_size); // From now on, we cannot have failure. pNetBT = p->Address[0].Address; p->TAAddressCount = 1; p->Address[0].AddressLength = sizeof(TA_NETBT_INTERNAL_ADDRESS); p->Address[0].AddressType = TDI_ADDRESS_TYPE_UNSPEC; pNetBT->NameType = ta.NameType; pNetBT->AddressType = ta.AddressType; pNetBT->OEMRemoteName.MaximumLength = NBT_DWORD_ALIGN(ta.OEMRemoteName.Length+1); pNetBT->OEMRemoteName.Length = ta.OEMRemoteName.Length; pNetBT->OEMRemoteName.Buffer = (PVOID)((PUCHAR)p + NBT_DWORD_ALIGN(sizeof(TA_NETBT_INTERNAL_ADDRESS))); ASSERT((ta.OEMRemoteName.Length % sizeof(ta.OEMRemoteName.Buffer[0])) == 0); CTEMemCopy(pNetBT->OEMRemoteName.Buffer, ta.OEMRemoteName.Buffer, ta.OEMRemoteName.Length); pNetBT->OEMRemoteName.Buffer[ta.OEMRemoteName.Length/sizeof(ta.OEMRemoteName.Buffer[0])] = 0; pNetBT->OEMEndpointName.MaximumLength = NBT_DWORD_ALIGN(ta.OEMEndpointName.Length+1); pNetBT->OEMEndpointName.Length = ta.OEMEndpointName.Length; pNetBT->OEMEndpointName.Buffer = (PVOID)((PUCHAR)pNetBT->OEMRemoteName.Buffer + pNetBT->OEMRemoteName.MaximumLength); ASSERT((ta.OEMEndpointName.Length % sizeof(ta.OEMEndpointName.Buffer[0])) == 0); CTEMemCopy(pNetBT->OEMEndpointName.Buffer, ta.OEMEndpointName.Buffer, ta.OEMEndpointName.Length); pNetBT->OEMEndpointName.Buffer[ta.OEMEndpointName.Length/sizeof(ta.OEMEndpointName.Buffer[0])] = 0; pNetBT->pNetbiosUnicodeEX = NULL; ppNetBT[0] = p; return STATUS_SUCCESS; } NTSTATUS NewInternalAddressFromNetbios( IN PTA_NETBIOS_ADDRESS pTA, IN ULONG MaxInputBufferLength, OUT PTA_NETBT_INTERNAL_ADDRESS *ppNetBT ) { ULONG required_size; PTA_NETBT_INTERNAL_ADDRESS p; PTDI_ADDRESS_NETBT_INTERNAL pNetBT; TDI_ADDRESS_NETBT_INTERNAL ta; ppNetBT[0] = NULL; if (!NT_SUCCESS(NetbiosAddressToInternalAddress(pTA, MaxInputBufferLength, &ta))) { return (STATUS_INVALID_ADDRESS); } required_size = NBT_DWORD_ALIGN(sizeof(TA_NETBT_INTERNAL_ADDRESS)) + NBT_DWORD_ALIGN(ta.OEMRemoteName.Length+1); p = (PTA_NETBT_INTERNAL_ADDRESS)NbtAllocMem (required_size, NBT_TAG2('TA')); if (p == NULL) { return STATUS_INSUFFICIENT_RESOURCES; } CTEZeroMemory(p, required_size); // From now on, we cannot have failure. pNetBT = p->Address[0].Address; p->TAAddressCount = 1; p->Address[0].AddressLength = sizeof(TA_NETBT_INTERNAL_ADDRESS); p->Address[0].AddressType = TDI_ADDRESS_TYPE_UNSPEC; pNetBT->NameType = ta.NameType; pNetBT->AddressType = ta.AddressType; pNetBT->OEMRemoteName.MaximumLength = NBT_DWORD_ALIGN(ta.OEMRemoteName.Length+1); pNetBT->OEMRemoteName.Length = ta.OEMRemoteName.Length; pNetBT->OEMRemoteName.Buffer = (PVOID)((PUCHAR)p + NBT_DWORD_ALIGN(sizeof(TA_NETBT_INTERNAL_ADDRESS))); ASSERT((ta.OEMRemoteName.Length % sizeof(ta.OEMRemoteName.Buffer[0])) == 0); CTEMemCopy(pNetBT->OEMRemoteName.Buffer, ta.OEMRemoteName.Buffer, ta.OEMRemoteName.Length); pNetBT->OEMRemoteName.Buffer[ta.OEMRemoteName.Length/sizeof(ta.OEMRemoteName.Buffer[0])] = 0; pNetBT->pNetbiosUnicodeEX = NULL; pNetBT->OEMEndpointName.MaximumLength = 0; pNetBT->OEMEndpointName.Length = 0; pNetBT->OEMEndpointName.Buffer = NULL; ppNetBT[0] = p; return STATUS_SUCCESS; } NTSTATUS NewInternalAddressFromUnicodeAddress( IN PTA_NETBIOS_UNICODE_EX_ADDRESS pTA, IN ULONG MaxInputBufferLength, OUT PTA_NETBT_INTERNAL_ADDRESS *ppNetBT ) { OEM_STRING OemEndpoint, OemRemote; UNICODE_STRING temp; ULONG required_size; PTA_NETBT_INTERNAL_ADDRESS p; PTDI_ADDRESS_NETBT_INTERNAL pNetBT; int remote_len; ppNetBT[0] = NULL; if (MaxInputBufferLength < sizeof (TDI_ADDRESS_NETBIOS_UNICODE_EX)) { ASSERT (0); return (STATUS_INVALID_ADDRESS); } switch(pTA->Address[0].Address[0].NameBufferType) { case NBT_READONLY: case NBT_WRITEONLY: case NBT_READWRITE: case NBT_WRITTEN: break; default: ASSERT(FALSE); return (STATUS_INVALID_ADDRESS); } /* unaligned */ CTEMemCopy(&temp, &pTA->Address[0].Address[0].RemoteName, sizeof(temp)); if (temp.MaximumLength > DNS_NAME_BUFFER_LENGTH * sizeof(WCHAR)) { return (STATUS_INVALID_ADDRESS); } if (temp.Length > DNS_MAX_NAME_LENGTH * sizeof(WCHAR)) { return (STATUS_INVALID_ADDRESS); } if (temp.Length + sizeof(WCHAR) > temp.MaximumLength) { return (STATUS_INVALID_ADDRESS); } if (!NT_SUCCESS(RtlUpcaseUnicodeStringToOemString(&OemRemote, &temp, TRUE))) { return (STATUS_INVALID_ADDRESS); } CTEMemCopy(&temp, &pTA->Address[0].Address[0].EndpointName, sizeof(temp)); if (!NT_SUCCESS(RtlUpcaseUnicodeStringToOemString(&OemEndpoint, &temp, TRUE))) { RtlFreeOemString(&OemRemote); return (STATUS_INVALID_ADDRESS); } /* * Other NetBT may never expect that remote_len and endpoint_len can be less than NETBIOS_NAME_SIZE. * Some of them may try to access 15th byte of the name. * In NETBIOS_NAME_TYPE and NETBIOS_EX_NAME_TYPE, at least NETBIOS_NAME_SIZE bytes are required for each name. */ remote_len = OemRemote.MaximumLength; if (remote_len <= NETBIOS_NAME_SIZE) { remote_len = NETBIOS_NAME_SIZE + 1; } /* Calculate the needed buffer size */ required_size = NBT_DWORD_ALIGN(sizeof(TA_NETBT_INTERNAL_ADDRESS)) + NBT_DWORD_ALIGN(remote_len) + // For OEM remote NBT_DWORD_ALIGN((NETBIOS_NAME_SIZE+1)*sizeof(OemEndpoint.Buffer[0])); p = (PTA_NETBT_INTERNAL_ADDRESS)NbtAllocMem (required_size, NBT_TAG2('TA')); if (p == NULL) { RtlFreeOemString(&OemRemote); RtlFreeOemString(&OemEndpoint); return STATUS_INSUFFICIENT_RESOURCES; } CTEZeroMemory(p, required_size); // From now on, we cannot have failure. pNetBT = p->Address[0].Address; p->TAAddressCount = 1; p->Address[0].AddressLength = sizeof(TA_NETBT_INTERNAL_ADDRESS); p->Address[0].AddressType = TDI_ADDRESS_TYPE_UNSPEC; pNetBT->NameType = pTA->Address[0].Address[0].NetbiosNameType; pNetBT->AddressType = TDI_ADDRESS_TYPE_NETBIOS_EX; // map to NETBIOS_EX // copy OEM EndpointName pNetBT->OEMEndpointName.Buffer = (PVOID)((PUCHAR)p + NBT_DWORD_ALIGN(sizeof(TA_NETBT_INTERNAL_ADDRESS))); pNetBT->OEMEndpointName.MaximumLength = NBT_DWORD_ALIGN((NETBIOS_NAME_SIZE+1)); pNetBT->OEMEndpointName.Length = NETBIOS_NAME_SIZE; ASSERT((NETBIOS_NAME_SIZE % sizeof(OemRemote.Buffer[0])) == 0); if (OemEndpoint.Length < NETBIOS_NAME_SIZE) { memset(pNetBT->OEMEndpointName.Buffer + OemEndpoint.Length, ' ', NETBIOS_NAME_SIZE); CTEMemCopy(pNetBT->OEMEndpointName.Buffer, OemEndpoint.Buffer, OemEndpoint.Length); } else { CTEMemCopy(pNetBT->OEMEndpointName.Buffer, OemEndpoint.Buffer, NETBIOS_NAME_SIZE); } pNetBT->OEMEndpointName.Buffer[NETBIOS_NAME_SIZE] = 0; RtlFreeOemString(&OemEndpoint); // copy OEM RemoteName pNetBT->OEMRemoteName.Buffer = ((PUCHAR)pNetBT->OEMEndpointName.Buffer + pNetBT->OEMEndpointName.MaximumLength); pNetBT->OEMRemoteName.MaximumLength = NBT_DWORD_ALIGN(remote_len); if (OemRemote.Length < NETBIOS_NAME_SIZE) { pNetBT->OEMRemoteName.Length = NETBIOS_NAME_SIZE; memset (pNetBT->OEMRemoteName.Buffer, ' ', NETBIOS_NAME_SIZE); CTEMemCopy(pNetBT->OEMRemoteName.Buffer, OemRemote.Buffer, OemRemote.Length); pNetBT->OEMRemoteName.Buffer[remote_len-1] = 0; } else { pNetBT->OEMRemoteName.Length = OemRemote.Length; CTEMemCopy(pNetBT->OEMRemoteName.Buffer, OemRemote.Buffer, OemRemote.MaximumLength); } RtlFreeOemString(&OemRemote); pNetBT->pNetbiosUnicodeEX = pTA->Address[0].Address; ppNetBT[0] = p; return STATUS_SUCCESS; } VOID DeleteInternalAddress(IN PTA_NETBT_INTERNAL_ADDRESS pNetBT) { #if 0 PTA_NETBT_INTERNAL_ADDRESS p; p = CONTAINING_RECORD(pNetBT,PTA_NETBT_INTERNAL_ADDRESS,Address[0].Address); ASSERT(p->AddressCount == 1); ASSERT(p->Address[0].AddressLength == sizeof(TDI_ADDRESS_NETBT_INTERNAL)); ASSERT(p->Address[0].AddressType == TDI_ADDRESS_TYPE_UNSPEC); #endif if (pNetBT == NULL) { return; } ASSERT(pNetBT->TAAddressCount == 1); ASSERT(pNetBT->Address[0].AddressLength == sizeof(TA_NETBT_INTERNAL_ADDRESS)); ASSERT(pNetBT->Address[0].AddressType == TDI_ADDRESS_TYPE_UNSPEC); CTEMemFree(pNetBT); } //---------------------------------------------------------------------------- NTSTATUS NewInternalAddressFromTransportAddress( IN TRANSPORT_ADDRESS UNALIGNED *pTransportAddress, IN ULONG MaxInputBufferLength, OUT PTA_NETBT_INTERNAL_ADDRESS *ppNetBT ) /*++ Routine Description This routine handles deciphering the weird transport address syntax and convert all types of NetBIOS address into one internal format. Arguments: Return Values: NTSTATUS - status of the request --*/ { ppNetBT[0] = NULL; // // Check for the minimum acceptable length // if ((!pTransportAddress) || (MaxInputBufferLength < sizeof (TA_NETBIOS_ADDRESS))) { ASSERT (0); return (STATUS_INVALID_ADDRESS); } switch (pTransportAddress->Address[0].AddressType) { case (TDI_ADDRESS_TYPE_NETBIOS): return NewInternalAddressFromNetbios( (PTA_NETBIOS_ADDRESS)pTransportAddress, MaxInputBufferLength, ppNetBT); #ifndef VXD case (TDI_ADDRESS_TYPE_NETBIOS_EX): return NewInternalAddressFromNetbiosEX( (PTA_NETBIOS_EX_ADDRESS)pTransportAddress, MaxInputBufferLength, ppNetBT); #endif // !VXD case (TDI_ADDRESS_TYPE_NETBIOS_UNICODE_EX): return NewInternalAddressFromUnicodeAddress( (PTA_NETBIOS_UNICODE_EX_ADDRESS)pTransportAddress, MaxInputBufferLength, ppNetBT); default: return (STATUS_INVALID_ADDRESS); } if (ppNetBT[0]->Address[0].Address[0].OEMRemoteName.Length > DNS_MAX_NAME_LENGTH) { DeleteInternalAddress(ppNetBT[0]); ppNetBT[0] = NULL; return (STATUS_NAME_TOO_LONG); } return (STATUS_SUCCESS); } //---------------------------------------------------------------------------- NTSTATUS GetNetBiosNameFromTransportAddress( IN TRANSPORT_ADDRESS UNALIGNED *pTransportAddress, IN ULONG MaxInputBufferLength, OUT PTDI_ADDRESS_NETBT_INTERNAL pNetBT ) /*++ Routine Description This routine handles deciphering the weird transport address syntax to retrieve the netbios name out of that address. Arguments: Return Values: NTSTATUS - status of the request --*/ { // // Check for the minimum acceptable length // if ((!pTransportAddress) || (MaxInputBufferLength < sizeof (TA_NETBIOS_ADDRESS))) { ASSERT (0); return (STATUS_INVALID_ADDRESS); } CTEZeroMemory(pNetBT, sizeof(pNetBT[0])); switch (pTransportAddress->Address[0].AddressType) { case (TDI_ADDRESS_TYPE_NETBIOS): return NetbiosAddressToInternalAddress( (PTA_NETBIOS_ADDRESS)pTransportAddress, MaxInputBufferLength, pNetBT); #ifndef VXD case (TDI_ADDRESS_TYPE_NETBIOS_EX): return NetbiosEXAddressToInternalAddress( (PTA_NETBIOS_EX_ADDRESS)pTransportAddress, MaxInputBufferLength, pNetBT); #endif // !VXD default: { return (STATUS_INVALID_ADDRESS); } } if (pNetBT->OEMRemoteName.Length > DNS_MAX_NAME_LENGTH) { return (STATUS_NAME_TOO_LONG); } return (STATUS_SUCCESS); } //---------------------------------------------------------------------------- NTSTATUS ConvertToAscii( IN PCHAR pNameHdr, IN LONG NumBytes, OUT PCHAR pName, OUT PCHAR *pScope, OUT PULONG pNameSize ) /*++ Routine Description: This routine converts half ascii to normal ascii and then appends the scope onto the end of the name to make a full name again. Arguments: NumBytes - the total number of bytes in the message starting from the tNETBIOS_NAME structure - may include more than just the name itself Return Value: NTSTATUS - success or not This routine returns the length of the name in half ascii format including the null at the end, but NOT including the length byte at the beginning. Thus, for a non-scoped name it would return 33. It converts the name to ascii and puts 16 bytes into pName, then it returns pScope as the Ptr to the scope that is still in pNameHdr. --*/ { LONG i, ScopeLength, lValue; ULONG UNALIGNED *pHdr; // 1st byte is length of the half ascii name, ie 32 (0x20) ==> (Length == 1 byte) // It should be followed by the half-ascii name ==> (Length == 32 bytes) // Finally, it has the Scope information ==> (Length >= 1 byte) // if ((NumBytes > 1+NETBIOS_NAME_SIZE*2) && (*pNameHdr == NETBIOS_NAME_SIZE*2)) { pHdr = (ULONG UNALIGNED *)++pNameHdr; // to increment past the length byte // the Half AScii portion of the netbios name is always 32 bytes long for (i=0; i < NETBIOS_NAME_SIZE*2 ;i +=4 ) { lValue = *pHdr - 0x41414141; // four A's pHdr++; lValue = ((lValue & 0x0F000000) >> 16) + ((lValue & 0x000F0000) >> 4) + ((lValue & 0x00000F00) >> 8) + ((lValue & 0x0000000F) << 4); *(PUSHORT)pName = (USHORT)lValue; ((PUSHORT)pName)++; } // verify that the name has the correct format...i.e. it is one or more // labels each starting with the length byte for the label and the whole // thing terminated with a 0 byte (for the root node name length of zero). // count the length of the scope. // pHdr should be pointing to the first byte after the half ascii name. // (If there is no scope present, then pHdr must be pointing to the NULL byte) // // Also, check for an overflow on the maximum length of 256 bytes if ((STATUS_SUCCESS != (strnlen ((PUCHAR)pHdr, NumBytes-(1+NETBIOS_NAME_SIZE*2), &ScopeLength))) || (ScopeLength > ((MAX_SCOPE_LENGTH+1)-NETBIOS_NAME_SIZE))) { // the name is too long..probably badly formed return(STATUS_UNSUCCESSFUL); } // Store the address of the start of the scope in the netbios name // (if one is present). // *pScope = (PUCHAR)pHdr; *pNameSize = NETBIOS_NAME_SIZE*2 + ScopeLength + 1; // include the null at the end. return(STATUS_SUCCESS); } else { return(STATUS_UNSUCCESSFUL); } } //---------------------------------------------------------------------------- PCHAR ConvertToHalfAscii( OUT PCHAR pDest, IN PCHAR pName, IN PCHAR pScope, IN ULONG uScopeSize ) /*++ Routine Description: This routine converts ascii to half ascii and appends the scope on the end Arguments: Return Value: the address of the next byte in the destination after the the name has been converted and copied --*/ { LONG i; // the first byte of the name is the length field = 2*16 *pDest++ = ((UCHAR)NETBIOS_NAME_SIZE << 1); // step through name converting ascii to half ascii, for 32 times for (i=0; i < NETBIOS_NAME_SIZE ;i++ ) { *pDest++ = ((UCHAR)*pName >> 4) + 'A'; *pDest++ = (*pName++ & 0x0F) + 'A'; } // // put the length of the scope into the next byte followed by the // scope itself. For 1 length scopes (the normal case), writing // the zero(for the end of the scope is all that is needed). // if (uScopeSize > 1) { CTEMemCopy(pDest,pScope,uScopeSize); pDest = pDest + uScopeSize; } else { *pDest++ = 0; } // return the address of the next byte of the destination return(pDest); } //---------------------------------------------------------------------------- ULONG Nbt_inet_addr( IN PCHAR pName, IN ULONG Flags ) /*++ Routine Description: This routine converts ascii ipaddr (11.101.4.25) into a ULONG. This is based on the inet_addr code in winsock Arguments: pName - the string containing the ipaddress Return Value: the ipaddress as a ULONG if it's a valid ipaddress. Otherwise, 0. --*/ { PCHAR pStr; int i; int len, fieldLen; int fieldsDone; ULONG IpAddress; BYTE ByteVal; PCHAR pIpPtr; BOOLEAN fDotFound; BOOLEAN fieldOk; pStr = pName; len = 0; pIpPtr = (PCHAR)&IpAddress; pIpPtr += 3; // so that we store in network order fieldsDone=0; // // the 11.101.4.25 format can be atmost 15 chars, and pName is guaranteed // to be at least 16 chars long (how convenient!!). Convert the string to // a ULONG. // while(len < NETBIOS_NAME_SIZE) { fieldLen=0; fieldOk = FALSE; ByteVal = 0; fDotFound = FALSE; // // This loop traverses each of the four fields (max len of each // field is 3, plus 1 for the '.' // while (fieldLen < 4) { if ((*pStr >='0') && (*pStr <='9')) { // // No Byte value should be greater than 255! // Bug#: 10487 // if ((ByteVal > 25) || ((ByteVal == 25) && (*pStr > '5'))) { return (0); } ByteVal = (ByteVal*10) + (*pStr - '0'); fieldOk = TRUE; } else if ((*pStr == '.') || (*pStr == ' ') || (*pStr == '\0')) { *pIpPtr = ByteVal; pIpPtr--; fieldsDone++; if (*pStr == '.') { fDotFound = TRUE; } else // (*pStr == ' ') || (*pStr == '\0') { // if we got a space or 0, assume it's the 4th field break; } } else // unacceptable char: can't be ipaddr { return(0); } pStr++; len++; fieldLen++; // if we found the dot, we are done with this field: go to the next one if (fDotFound) break; } // this field wasn't ok (e.g. "11.101..4" or "11.101.4." etc.) if (!fieldOk) { return(0); } // if we are done with all 4 fields, we are done with the outer loop too if ( fieldsDone == 4) break; if (!fDotFound) { return(0); } } // // make sure the remaining NETBIOS_NAME_SIZE-1 chars are spaces or 0's // (i.e. don't allow 11.101.4.25xyz to succeed) // for (i=len; i is not unique!\n", pName)); IpAddress = 0; } return( IpAddress ); } //---------------------------------------------------------------------------- tDGRAM_SEND_TRACKING * NbtAllocInitTracker( IN tDGRAM_SEND_TRACKING *pTracker ) /*++ Routine Description: This routine allocates memory for several of the structures attached to the dgram tracking list, so that this memory does not need to be allocated and freed for each send. Arguments: ppListHead - a ptr to a ptr to the list head Return Value: none --*/ { PLIST_ENTRY pEntry; PTRANSPORT_ADDRESS pTransportAddr; ULONG TotalSize; TotalSize = sizeof(tDGRAM_SEND_TRACKING) + sizeof(TDI_CONNECTION_INFORMATION) + sizeof(TRANSPORT_ADDRESS) -1 + NbtConfig.SizeTransportAddress; // // If not Tracker was provided, then we will have to allocate one! // if (!pTracker) { // // allocate all the tracker memory as one block and then divy it up later // into the various buffers // if (pTracker = (tDGRAM_SEND_TRACKING *) NbtAllocMem (TotalSize, NBT_TAG2('07'))) { NbtConfig.iCurrentNumBuff[eNBT_DGRAM_TRACKER]++; } } if (pTracker) { CTEZeroMemory(pTracker,TotalSize); pTracker->Verify = NBT_VERIFY_TRACKER; pTracker->RefCount = 1; InitializeListHead (&pTracker->Linkage); InitializeListHead (&pTracker->TrackerList); // Empty the list of trackers linked to this one pTracker->pSendInfo = (PTDI_CONNECTION_INFORMATION)((PUCHAR)pTracker + sizeof(tDGRAM_SEND_TRACKING)); // fill in the connection information - especially the Remote address structure pTracker->pSendInfo->RemoteAddressLength = sizeof(TRANSPORT_ADDRESS) -1 + pNbtGlobConfig->SizeTransportAddress; // allocate the remote address structure pTransportAddr = (PTRANSPORT_ADDRESS) ((PUCHAR)pTracker->pSendInfo + sizeof(TDI_CONNECTION_INFORMATION)); // fill in the remote address pTransportAddr->TAAddressCount = 1; pTransportAddr->Address[0].AddressLength = NbtConfig.SizeTransportAddress; pTransportAddr->Address[0].AddressType = TDI_ADDRESS_TYPE_IP; ((PTDI_ADDRESS_IP)pTransportAddr->Address[0].Address)->sin_port = NBT_NAMESERVICE_UDP_PORT; ((PTDI_ADDRESS_IP)pTransportAddr->Address[0].Address)->in_addr = 0L; // put a ptr to this address structure into the sendinfo structure pTracker->pSendInfo->RemoteAddress = (PVOID)pTransportAddr; } return(pTracker); } //---------------------------------------------------------------------------- #define MAX_FREE_TRACKERS 50 ULONG NumFreeTrackers = 0; // #if DBG ULONG TrackTrackers[NBT_TRACKER_NUM_TRACKER_TYPES]; ULONG TrackerHighWaterMark[NBT_TRACKER_NUM_TRACKER_TYPES]; // #endif // DBG NTSTATUS GetTracker( OUT tDGRAM_SEND_TRACKING **ppTracker, IN enum eTRACKER_TYPE TrackerType) /*++ Routine Description: This Routine gets a Tracker data structure to track sending a datagram or session packet. Arguments: Return Value: Status - STATUS_SUCCESS or STATUS_INSUFFICIENT_RESOURCES --*/ { PLIST_ENTRY pListEntry; CTELockHandle OldIrq; tDGRAM_SEND_TRACKING *pTracker = NULL; NTSTATUS status = STATUS_INSUFFICIENT_RESOURCES; CTESpinLock(&NbtConfig,OldIrq); if (!IsListEmpty(&NbtConfig.DgramTrackerFreeQ)) { pListEntry = RemoveHeadList(&NbtConfig.DgramTrackerFreeQ); pTracker = CONTAINING_RECORD(pListEntry,tDGRAM_SEND_TRACKING,Linkage); NumFreeTrackers--; } else if (NbtConfig.iCurrentNumBuff[eNBT_DGRAM_TRACKER] >= NbtConfig.iMaxNumBuff[eNBT_DGRAM_TRACKER]) { CTESpinFree(&NbtConfig,OldIrq); KdPrint(("GetTracker: WARNING: Tracker leak -- Failing request!\n")) ; *ppTracker = NULL; return (status); } if (pTracker = NbtAllocInitTracker (pTracker)) { // #if DBG pTracker->TrackerType = TrackerType; InsertTailList (&UsedTrackers, &pTracker->DebugLinkage); // keep tracker on a used list for debug TrackTrackers[TrackerType]++; if (TrackTrackers[TrackerType] > TrackerHighWaterMark[TrackerType]) { TrackerHighWaterMark[TrackerType] = TrackTrackers[TrackerType]; } // #endif status = STATUS_SUCCESS; } CTESpinFree(&NbtConfig,OldIrq); *ppTracker = pTracker; return (status); } //---------------------------------------------------------------------------- VOID FreeTracker( IN tDGRAM_SEND_TRACKING *pTracker, IN ULONG Actions ) /*++ Routine Description: This routine cleans up a Tracker block and puts it back on the free queue. Arguments: Return Value: NTSTATUS - success or not --*/ { CTELockHandle OldIrq; PLIST_ENTRY pListEntry; CTESpinLock(&NbtConfig,OldIrq); // CHECK_PTR(pTracker); if (!NBT_VERIFY_HANDLE(pTracker, NBT_VERIFY_TRACKER)) // Bad pointer -- don't mess with it! { CTESpinFree(&NbtConfig,OldIrq); DbgPrint("Nbt.FreeTracker: ERROR! Bad Tracker ptr @<%p>\n", pTracker); ASSERT(0); return; } if (Actions & REMOVE_LIST) { // // unlink the tracker block from the NodeStatus Q RemoveEntryList(&pTracker->Linkage); } if (Actions & FREE_HDR) { // return the datagram hdr to the free pool // if (pTracker->SendBuffer.pDgramHdr) { CTEMemFree((PVOID)pTracker->SendBuffer.pDgramHdr); } // Free the RemoteName storage // if (pTracker->pRemoteName) { CTEMemFree((PVOID)pTracker->pRemoteName); pTracker->pRemoteName = NULL; } if (pTracker->UnicodeRemoteName) { CTEMemFree((PVOID)pTracker->UnicodeRemoteName); pTracker->UnicodeRemoteName = NULL; } } #ifdef MULTIPLE_WINS if (pTracker->pFailedIpAddresses) { CTEMemFree((PVOID)pTracker->pFailedIpAddresses); pTracker->pFailedIpAddresses = NULL; } #endif if (pTracker->IpList) { ASSERT(pTracker->NumAddrs); CTEMemFree(pTracker->IpList); } ASSERT (IsListEmpty (&pTracker->TrackerList)); InitializeListHead(&pTracker->TrackerList); InsertTailList (&NbtConfig.DgramTrackerFreeQ, &pTracker->Linkage); pTracker->Verify = NBT_VERIFY_TRACKER_DOWN; // #IF DBG TrackTrackers[pTracker->TrackerType]--; RemoveEntryList(&pTracker->DebugLinkage); // #endif // DBG if (NumFreeTrackers > MAX_FREE_TRACKERS) { // // We already have the required # of free trackers available // in our pool, so just free the oldest Tracker // pListEntry = RemoveHeadList(&NbtConfig.DgramTrackerFreeQ); pTracker = CONTAINING_RECORD(pListEntry,tDGRAM_SEND_TRACKING,Linkage); CTEMemFree (pTracker); NbtConfig.iCurrentNumBuff[eNBT_DGRAM_TRACKER]--; } else { NumFreeTrackers++; } CTESpinFree(&NbtConfig,OldIrq); } //---------------------------------------------------------------------------- NTSTATUS NbtInitTrackerQ( LONG iNumBuffers ) /*++ Routine Description: This routine allocates memory blocks for doubly linked lists and links them to a list. Arguments: ppListHead - a ptr to a ptr to the list head to add buffer to iNumBuffers - the number of buffers to add to the queue Return Value: none --*/ { int i; tDGRAM_SEND_TRACKING *pTracker; for (i=0; iLinkage); NumFreeTrackers++; } } return(STATUS_SUCCESS); } //---------------------------------------------------------------------------- #ifndef VXD NTSTATUS GetIrp( OUT PIRP *ppIrp) /*++ Routine Description: This Routine gets an Irp from the free queue or it allocates another one the queue is empty. Arguments: Return Value: BOOLEAN - TRUE if IRQL is too high --*/ { NTSTATUS status = STATUS_INSUFFICIENT_RESOURCES; CTELockHandle OldIrq; PIRP pIrp = NULL; pIrp = IoAllocateIrp(NbtConfig.MaxIrpStackSize, FALSE); if (NULL == pIrp) { return STATUS_INSUFFICIENT_RESOURCES; } CTESpinLock(&NbtConfig,OldIrq); *ppIrp = pIrp; InsertTailList(&UsedIrps, &(pIrp->ThreadListEntry)); CTESpinFree(&NbtConfig,OldIrq); status = STATUS_SUCCESS; return(status); } VOID NbtFreeIrp ( PIRP pIrp ) { CTELockHandle OldIrq; CTESpinLock(&NbtConfig,OldIrq); RemoveEntryList(&pIrp->ThreadListEntry); CTESpinFree(&NbtConfig,OldIrq); // // To make I/O manager and driver verifier happy, we need to empty the list // entry // InitializeListHead(&pIrp->ThreadListEntry); IoFreeIrp(pIrp); } #endif //!VXD //---------------------------------------------------------------------------- ULONG CountLocalNames(IN tNBTCONFIG *pNbtConfig ) /*++ Routine Description: This Routine counts the number of names in the local name table. Arguments: Return Value: ULONG - the number of names --*/ { PLIST_ENTRY pHead; PLIST_ENTRY pEntry; ULONG Count; tNAMEADDR *pNameAddr; LONG i; Count = 0; for (i=0;i < NbtConfig.pLocalHashTbl->lNumBuckets ;i++ ) { pHead = &NbtConfig.pLocalHashTbl->Bucket[i]; pEntry = pHead; while ((pEntry = pEntry->Flink) != pHead) { pNameAddr = CONTAINING_RECORD(pEntry,tNAMEADDR,Linkage); // // don't want unresolved names, or the broadcast name // if (!(pNameAddr->NameTypeState & STATE_RESOLVING) && (pNameAddr->Name[0] != '*')) { Count++; } } } return(Count); } //---------------------------------------------------------------------------- ULONG CountUpperConnections( IN tDEVICECONTEXT *pDeviceContext ) /*++ Routine Description: This Routine counts the number of upper connections that have been created in preparation for creating an equivalent number of lower connections. Arguments: Return Value: ULONG - the number of names --*/ { PLIST_ENTRY pHead; PLIST_ENTRY pEntry; PLIST_ENTRY pClientHead; PLIST_ENTRY pConnHead; PLIST_ENTRY pClientEntry; PLIST_ENTRY pConnEntry; ULONG CountConnections = 0; tADDRESSELE *pAddressEle; tCLIENTELE *pClient; tCONNECTELE *pConnEle; CTELockHandle OldIrq1, OldIrq2, OldIrq3; // // Need to hold JointLock before accessing AddressHead! // CTESpinLock(&NbtConfig.JointLock,OldIrq1); // get the list of addresses for this device pHead = &NbtConfig.AddressHead; pEntry = pHead->Flink; while (pEntry != pHead) { pAddressEle = CONTAINING_RECORD(pEntry,tADDRESSELE,Linkage); // // Need to hold pAddressEle lock before accessing ClientHead! // CTESpinLock(pAddressEle,OldIrq2); pClientHead = &pAddressEle->ClientHead; pClientEntry = pClientHead->Flink; while (pClientEntry != pClientHead) { pClient = CONTAINING_RECORD(pClientEntry,tCLIENTELE,Linkage); // // Need to hold pClient lock before accessing ConnectHead! // CTESpinLock(pClient, OldIrq3); pConnHead = &pClient->ConnectHead; pConnEntry = pConnHead->Flink; while (pConnEntry != pConnHead) { pConnEle = CONTAINING_RECORD(pConnEntry,tCONNECTELE,Linkage); if (pConnEle->pDeviceContext == pDeviceContext) { CountConnections++; } pConnEntry = pConnEntry->Flink; } CTESpinFree(pClient, OldIrq3); pClientEntry = pClientEntry->Flink; } CTESpinFree(pAddressEle, OldIrq2); pEntry = pEntry->Flink; } CTESpinFree(&NbtConfig.JointLock, OldIrq1); return(CountConnections); } //---------------------------------------------------------------------------- NTSTATUS DisableInboundConnections( IN tDEVICECONTEXT *pDeviceContext ) /*++ Routine Description: This routine checks the devicecontext for open connections and sets the Lower Connection free list to empty. Arguments: Return Value: none --*/ { CTELockHandle OldIrqJoint; CTELockHandle OldIrqDevice; CTELockHandle OldIrqConn; CTELockHandle OldIrqLower; tLOWERCONNECTION *pLowerConn; NTSTATUS status; PVOID ConnectContext; CTESpinLock(&NbtConfig.JointLock,OldIrqJoint); CTESpinLock(pDeviceContext,OldIrqDevice); // // First take care of the free connections // while (!IsListEmpty (&pDeviceContext->LowerConnFreeHead)) { pLowerConn = CONTAINING_RECORD (pDeviceContext->LowerConnFreeHead.Flink,tLOWERCONNECTION,Linkage); RemoveEntryList (&pLowerConn->Linkage); InitializeListHead (&pLowerConn->Linkage); // // close the lower connection with the transport // NBT_DEREFERENCE_LOWERCONN (pLowerConn, REF_LOWC_CREATE, TRUE); InterlockedDecrement (&pDeviceContext->NumFreeLowerConnections); } ASSERT (pDeviceContext->NumFreeLowerConnections == 0); // // Now go through the list of Inbound connections and cleanup! // while (!IsListEmpty (&pDeviceContext->WaitingForInbound)) { pLowerConn = CONTAINING_RECORD(pDeviceContext->WaitingForInbound.Flink,tLOWERCONNECTION,Linkage); RemoveEntryList (&pLowerConn->Linkage); InitializeListHead (&pLowerConn->Linkage); SET_STATE_LOWER(pLowerConn, NBT_IDLE); // so that Inbound doesn't start processing it! if (pLowerConn->SpecialAlloc) { InterlockedDecrement(&pLowerConn->pDeviceContext->NumSpecialLowerConn); } ASSERT (pLowerConn->RefCount == 2); NBT_DEREFERENCE_LOWERCONN (pLowerConn, REF_LOWC_WAITING_INBOUND, TRUE); // RefCount will go to 1 NBT_DEREFERENCE_LOWERCONN (pLowerConn, REF_LOWC_CREATE, TRUE);// This should close all the Tcp handles InterlockedDecrement (&pDeviceContext->NumWaitingForInbound); } ASSERT (pDeviceContext->NumWaitingForInbound == 0); // ****************************************** // NOTE: The code after this point can probably be deleted // because TCP should disconnect all open connections when it // is notified of the address change. Just use this code for test. // // // Now go through the list of active Lower connections to see which are // still up and issue disconnects on them. // while (!IsListEmpty (&pDeviceContext->LowerConnection)) { pLowerConn = CONTAINING_RECORD (pDeviceContext->LowerConnection.Flink,tLOWERCONNECTION,Linkage); RemoveEntryList (&pLowerConn->Linkage); InitializeListHead (&pLowerConn->Linkage); NBT_REFERENCE_LOWERCONN (pLowerConn, REF_LOWC_DISABLE_INBOUND); CTESpinFree(pDeviceContext,OldIrqDevice); CTESpinFree(&NbtConfig.JointLock,OldIrqJoint); CTESpinLock(pLowerConn,OldIrqLower); // // In the connecting state the TCP connection is being // setup. // if ((pLowerConn->State == NBT_SESSION_UP) || (pLowerConn->State == NBT_CONNECTING)) { tCLIENTELE *pClientEle; tCONNECTELE *pConnEle; if (pLowerConn->State == NBT_CONNECTING) { // CleanupAfterDisconnect expects this ref count // to be 2, meaning that it got connected, so increment // here NBT_REFERENCE_LOWERCONN(pLowerConn, REF_LOWC_CONNECTED); } pClientEle = pLowerConn->pUpperConnection->pClientEle; pConnEle = pLowerConn->pUpperConnection; NBT_DISASSOCIATE_CONNECTION (pConnEle, pLowerConn); SET_STATE_LOWER (pLowerConn, NBT_DISCONNECTING); SET_STATE_UPPER (pConnEle, NBT_DISCONNECTED); SetStateProc(pLowerConn,RejectAnyData); CTESpinFree(pLowerConn,OldIrqLower); ConnectContext = pConnEle->ConnectContext; NBT_DEREFERENCE_CONNECTION (pConnEle, REF_CONN_CONNECT); if (pClientEle->evDisconnect) { status = (*pClientEle->evDisconnect)(pClientEle->DiscEvContext, ConnectContext, 0, NULL, 0, NULL, TDI_DISCONNECT_ABORT); } // this should kill of the connection when the irp // completes by calling CleanupAfterDisconnect. // #ifndef VXD status = DisconnectLower(pLowerConn, NBT_SESSION_UP, TDI_DISCONNECT_ABORT, &DefaultDisconnectTimeout, TRUE); #else // Vxd can't wait for the disconnect status = DisconnectLower(pLowerConn, NBT_SESSION_UP, TDI_DISCONNECT_ABORT, &DefaultDisconnectTimeout, FALSE); #endif } else if (pLowerConn->State == NBT_IDLE) { tCONNECTELE *pConnEle; CTESpinFree(pLowerConn,OldIrqLower); CTESpinLock(&NbtConfig.JointLock,OldIrqJoint); if (pConnEle = pLowerConn->pUpperConnection) { CTESpinLock(pConnEle,OldIrqConn); // // this makes a best effort to find the connection and // and cancel it. Anything not cancelled will eventually // fail with a bad ret code from the transport which is // ok too. // status = CleanupConnectingState(pConnEle,pDeviceContext, &OldIrqConn,&OldIrqLower); CTESpinFree(pConnEle,OldIrqConn); } CTESpinFree(&NbtConfig.JointLock,OldIrqJoint); } else { CTESpinFree(pLowerConn,OldIrqLower); } CTESpinLock(&NbtConfig.JointLock,OldIrqJoint); CTESpinLock(pDeviceContext,OldIrqDevice); // // remove the reference added above when the list was // created. // NBT_DEREFERENCE_LOWERCONN (pLowerConn, REF_LOWC_DISABLE_INBOUND, TRUE); } CTESpinFree(pDeviceContext,OldIrqDevice); CTESpinFree(&NbtConfig.JointLock,OldIrqJoint); return(STATUS_SUCCESS); } //---------------------------------------------------------------------------- NTSTATUS ReRegisterLocalNames( IN tDEVICECONTEXT *pDeviceContext, IN BOOLEAN fSendNameRelease ) /*++ Routine Description: This routine re-registers names with WINS when DHCP changes the IP address. Arguments: pDeviceContext - ptr to the devicecontext Return Value: status --*/ { NTSTATUS status; tTIMERQENTRY *pTimerEntry; CTELockHandle OldIrq; LONG i; PLIST_ENTRY pHead; PLIST_ENTRY pEntry; PLIST_ENTRY pHead1; PLIST_ENTRY pEntry1; PLIST_ENTRY pHead2; PLIST_ENTRY pEntry2; tDEVICECONTEXT *pRelDeviceContext; tDEVICECONTEXT *pSrcIpDeviceContext; tNAMEADDR *pNameAddr; tDGRAM_SEND_TRACKING *pTracker = NULL; CTEULONGLONG ReRegisterMask; CTESystemTime CurrentTime; IF_DBG(NBT_DEBUG_NAMESRV) KdPrint (("Nbt.ReRegisterLocalNames Called on Device=<%x>...\n", pDeviceContext)); CTESpinLock(&NbtConfig.JointLock,OldIrq); if (fSendNameRelease) { CTEQuerySystemTime (CurrentTime); if (((CurrentTime.QuadPart-NbtConfig.LastForcedReleaseTime.QuadPart) <= (TWO_MINUTES*10000)) || // Check in 100 nanosec units (!(NT_SUCCESS(GetTracker(&pTracker,NBT_TRACKER_RELEASE_REFRESH))))) { CTESpinFree(&NbtConfig.JointLock,OldIrq); IF_DBG(NBT_DEBUG_NAMESRV) KdPrint (("Nbt.ReRegisterLocalNames: ERROR: Name Release -- last Release interval=<%d>Secs\n", (LONG) ((CurrentTime.QuadPart-NbtConfig.LastForcedReleaseTime.QuadPart)/(1000*10000)))); return (STATUS_IO_TIMEOUT); } NbtConfig.LastForcedReleaseTime = CurrentTime; } if (pTimerEntry = NbtConfig.pRefreshTimer) { NbtConfig.pRefreshTimer = NULL; status = StopTimer(pTimerEntry,NULL,NULL); } // // restart timer and use // the initial refresh rate until we can contact the name server // NbtConfig.MinimumTtl = NbtConfig.InitialRefreshTimeout; NbtConfig.RefreshDivisor = REFRESH_DIVISOR; // // set this to 3 so that refreshBegin will refresh to the primary and // then switch to backup on the next refresh interval if it doesn't // get through. // NbtConfig.sTimeoutCount = 3; NbtConfig.GlobalRefreshState &= ~NBT_G_REFRESHING_NOW; status = StartTimer(RefreshTimeout, NbtConfig.InitialRefreshTimeout/NbtConfig.RefreshDivisor, NULL, // context value NULL, // context2 value NULL, NULL, NULL, // This is a Global Timer! &pTimerEntry, 0, TRUE); if ( !NT_SUCCESS( status ) ) { if (pTracker) { FreeTracker(pTracker, RELINK_TRACKER); } CTESpinFree(&NbtConfig.JointLock,OldIrq); return status ; } NbtConfig.pRefreshTimer = pTimerEntry; // // If a Device was specified to ReRegister on, then Zero out only the // bit for this Device in the RefreshMask for each name // otherwise, ReRegister on all Devices (Zero out all bits!) // if (pDeviceContext) { ReRegisterMask = ~pDeviceContext->AdapterMask; // Only bit for this Device is 0 pDeviceContext->DeviceRefreshState &= ~NBT_D_REFRESHING_NOW; } else { ReRegisterMask = 0; } for (i=0 ;i < NbtConfig.pLocalHashTbl->lNumBuckets ;i++ ) { pHead = &NbtConfig.pLocalHashTbl->Bucket[i]; pEntry = pHead->Flink; while (pEntry != pHead) { pNameAddr = CONTAINING_RECORD(pEntry,tNAMEADDR,Linkage); // // set so that nextrefresh finds the name and does a refresh // if (!(pNameAddr->NameTypeState & STATE_RESOLVED) || (pNameAddr->Name[0] == '*') || (pNameAddr->NameTypeState & NAMETYPE_QUICK)) { pEntry = pEntry->Flink; continue; } NBT_REFERENCE_NAMEADDR (pNameAddr, REF_NAME_RELEASE_REFRESH); if (fSendNameRelease) { IF_DBG(NBT_DEBUG_NAMESRV) KdPrint(("Nbt.ReRegisterLocalNames: Name=<%16.16s:%x>\n", pNameAddr->Name,pNameAddr->Name[15])); pTracker->pNameAddr = pNameAddr; // // Release this name on all the devices (brute force method)! // pHead1 = &NbtConfig.DeviceContexts; pEntry1 = pHead1->Flink; while (pEntry1 != pHead1) { pSrcIpDeviceContext = CONTAINING_RECORD(pEntry1,tDEVICECONTEXT,Linkage); if ((pSrcIpDeviceContext->IpAddress == 0) || (!NBT_REFERENCE_DEVICE (pSrcIpDeviceContext, REF_DEV_REREG, TRUE))) { pEntry1 = pEntry1->Flink; continue; } pHead2 = &NbtConfig.DeviceContexts; pEntry2 = pHead2->Flink; while (pEntry2 != pHead2) { // // See if we need to release on this device // pRelDeviceContext = CONTAINING_RECORD(pEntry2,tDEVICECONTEXT,Linkage); if ((pRelDeviceContext->IpAddress == 0) || (!NBT_REFERENCE_DEVICE (pRelDeviceContext, REF_DEV_REREG, TRUE))) { pEntry2 = pEntry2->Flink; continue; } // // Send the NameRelease to the Primary and Secondary Wins! // CTESpinFree(&NbtConfig.JointLock,OldIrq); pTracker->pDeviceContext = pRelDeviceContext; pTracker->SendBuffer.pDgramHdr = NULL; // catch erroneous frees pTracker->RemoteIpAddress = pSrcIpDeviceContext->IpAddress; pTracker->TransactionId = 0; // Primary Wins ... pTracker->Flags = NBT_NAME_SERVER | NBT_USE_UNIQUE_ADDR; pTracker->RefCount = 2; status = UdpSendNSBcast(pNameAddr,NbtConfig.pScope,pTracker, NULL, NULL, NULL, 0, 0, eNAME_RELEASE, TRUE); // Secondary Wins ... pTracker->Flags = NBT_NAME_SERVER_BACKUP | NBT_USE_UNIQUE_ADDR; pTracker->RefCount = 2; status = UdpSendNSBcast(pNameAddr,NbtConfig.pScope,pTracker, NULL, NULL, NULL, 0, 0, eNAME_RELEASE, TRUE); CTESpinLock(&NbtConfig.JointLock,OldIrq); pEntry2 = pRelDeviceContext->Linkage.Flink; NBT_DEREFERENCE_DEVICE (pRelDeviceContext, REF_DEV_REREG, TRUE); } pEntry1 = pSrcIpDeviceContext->Linkage.Flink; NBT_DEREFERENCE_DEVICE (pSrcIpDeviceContext, REF_DEV_REREG, TRUE); } } pNameAddr->RefreshMask &= ReRegisterMask; pNameAddr->Ttl = NbtConfig.InitialRefreshTimeout; pEntry = pNameAddr->Linkage.Flink; NBT_DEREFERENCE_NAMEADDR (pNameAddr, REF_NAME_RELEASE_REFRESH, TRUE); } } if (pTracker) { FreeTracker(pTracker, RELINK_TRACKER); } // start a refresh if there isn't one currently going on // Note that there is a time window here that if the refresh is // currently going on then, some names will not get refreshed with // the new IpAddress right away, but have to wait to the next // refresh interval. It seems that this is a rather unlikely // scenario and given the low probability of DHCP changing the // address it makes even less sense to add the code to handle that // case. // if (NT_SUCCESS(NTQueueToWorkerThread(NULL, DelayedRefreshBegin, NULL, NULL, NULL, NULL, TRUE))) { NbtConfig.GlobalRefreshState |= NBT_G_REFRESHING_NOW; } CTESpinFree(&NbtConfig.JointLock,OldIrq); return(STATUS_SUCCESS); } //---------------------------------------------------------------------------- VOID NbtStopRefreshTimer( ) { tTIMERQENTRY *pTimerEntry; CTELockHandle OldIrq; // // Stop the regular Refresh Timer CTESpinLock(&NbtConfig.JointLock,OldIrq); if (pTimerEntry = NbtConfig.pRefreshTimer) { NbtConfig.pRefreshTimer = NULL; StopTimer (pTimerEntry, NULL, NULL); } CTESpinFree(&NbtConfig.JointLock,OldIrq); } //---------------------------------------------------------------------------- NTSTATUS strnlen( PUCHAR pSrcString, LONG MaxBufferLength, LONG *pStringLen ) { LONG iIndex = 0; while ((iIndex < MaxBufferLength-1) && (*pSrcString)) { iIndex++; pSrcString++; } if (*pSrcString) { *pStringLen = 0; return (STATUS_UNSUCCESSFUL); } *pStringLen = iIndex; return (STATUS_SUCCESS); }